diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2be55b3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1951 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "config" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +dependencies = [ + "async-trait", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "git-remote-k8s" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "config", + "futures", + "k8s-openapi", + "kube", + "once_cell", + "regex", + "scopeguard", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown 0.14.2", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "jsonpath_lib" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa63191d68230cccb81c5aa23abd53ed64d83337cacbb25a7b8c7979523774f" +dependencies = [ + "log", + "serde", + "serde_json", +] + +[[package]] +name = "k8s-openapi" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc3606fd16aca7989db2f84bb25684d0270c6d6fa1dbcd0025af7b4130523a6" +dependencies = [ + "base64 0.21.5", + "bytes", + "chrono", + "serde", + "serde-value", + "serde_json", +] + +[[package]] +name = "kube" +version = "0.86.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8647c2211a9b480d910b155d573602c52cd5f646acecb06a03d594865dc4784" +dependencies = [ + "k8s-openapi", + "kube-client", + "kube-core", +] + +[[package]] +name = "kube-client" +version = "0.86.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8952521f3e8ce11920229e5f2965fef70525aecd9efc7b65e39bf9e2c6f66d" +dependencies = [ + "base64 0.20.0", + "bytes", + "chrono", + "either", + "futures", + "home", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-timeout", + "jsonpath_lib", + "k8s-openapi", + "kube-core", + "pem", + "pin-project", + "rand", + "rustls", + "rustls-pemfile", + "secrecy", + "serde", + "serde_json", + "serde_yaml", + "thiserror", + "tokio", + "tokio-tungstenite", + "tokio-util", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "kube-core" +version = "0.86.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7608a0cd05dfa36167d2da982bb70f17feb5450f73ec601f6d428bbcf991c5b9" +dependencies = [ + "chrono", + "form_urlencoded", + "http", + "k8s-openapi", + "once_cell", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pem" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3163d2912b7c3b52d651a055f2c7eec9ba5cd22d26ef75b8dd3a59980b185923" +dependencies = [ + "base64 0.21.5", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + +[[package]] +name = "ron" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "serde", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustls" +version = "0.21.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.5", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "base64 0.21.5", + "bitflags 2.4.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..81a6159 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "git-remote-k8s" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.75" +clap = { version = "4.4.7", features = ["derive", "env"] } +config = "0.13.3" +futures = "0.3.29" +k8s-openapi = { version = "0.20.0", features = ["v1_27"] } +kube = { version = "0.86.0", features = ["ws"] } +once_cell = "1.18.0" +regex = "1.10.2" +scopeguard = "1.2.0" +serde = { version = "1.0.190", features = ["derive"] } +serde_json = "1.0.107" +thiserror = "1.0.50" +tokio = { version = "1.33.0", features = ["rt", "tokio-macros", "macros", "rt-multi-thread", "io-std", "io-util"] } +tokio-util = { version = "0.7.10", features = ["io-util"] } +tracing = { version = "0.1.40", features = ["log"] } +tracing-subscriber = "0.3.17" +url = "2.4.1" diff --git a/git-remote-k8s.sh b/git-remote-k8s.sh deleted file mode 100755 index 3440f60..0000000 --- a/git-remote-k8s.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/sh - -# git-remote-k8s, a remote helper for git targeting a kubernetes cluster -# -# Copyright (C) 2023 James Andariese -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -set -e - -if [ x = x"$GIT_DIR" ];then - 1>&2 echo "Please see $(dirname "$0")/README.md for instructions" - exit 1 -fi - -export IMAGE=alpine/git:latest - -export CONTEXT="$(echo "${2#*://}" | cut -d / -f 1)" -export NS="$(echo "${2#*://}" | cut -d / -f 2)" -export REPO="$(echo "${2#*://}" | cut -d / -f 3)" -export RUNID="$(dd if=/dev/random bs=600 count=1 status=none | base64 | tr -dc a-z0-9 | cut -c 1-6)" - -while read -r cmd arg rest; do - case "$cmd" in - "capabilities") - printf 'connect\n\n' - ;; - "connect") - case "$arg" in - git-receive-pack) SUBCOMMAND="git receive-pack .";; - git-send-pack) SUBCOMMAND="git send-pack .";; - git-upload-pack) SUBCOMMAND="git upload-pack .";; - git-upload-archive) SUBCOMMAND="git upload-archive .";; - *) - 1>&2 echo "invalid subcommand in connect $arg" - exit 1 - ;; - esac - 1>&2 echo "running $arg" - break - ;; - esac -done - -export COMMAND=" -[ -f HEAD ] || git init --bare -read -echo -$SUBCOMMAND -" -# if you named your pod FILTERME_HFOIQJF, I apologize - -echo ' ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: ${REPO} - namespace: ${NS} -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi - storageClassName: ${GIT_REMOTE_K8S_STORAGECLASS-FILTERME_HFOIQJF} - volumeMode: Filesystem ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: ${REPO}-gitc${RUNID} - namespace: ${NS} - labels: - git.kn8v.com/runid: ${RUNID} -spec: - template: - spec: - containers: - - name: git-connector - image: ${IMAGE} - stdin: true - stdinOnce: true - command: - - sh - - -c - - ${COMMAND} - workingDir: /repo - volumeMounts: - - name: repo - mountPath: /repo - volumes: - - name: repo - persistentVolumeClaim: - claimName: ${REPO} - restartPolicy: Never -' | grep -v FILTERME_HFOIQJF | yq ea '(.. | select(tag == "!!str")) |= envsubst' | kubectl --context "$CONTEXT" apply -f - 1>&2 - -KILLLOGS=: - -finalize() { - kubectl --context "$CONTEXT" delete job -n "$NS" "${REPO}-gitc${RUNID}" 1>&2 - $KILLLOGS - exit # must exit for INT and TERM. -} -trap finalize INT TERM - -1>&2 echo "waiting for the job to start..." -kubectl --context "$CONTEXT" wait job "${REPO}-gitc${RUNID}" --for jsonpath=.status.ready=1 1>&2 -(echo;cat) | kubectl --context "$CONTEXT" attach -i -q -n "$NS" "job/${REPO}-gitc${RUNID}" - -# also finalize on exit -finalize diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..30e72f3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,589 @@ +use std::process::ExitCode; +use std::{future::Future, collections::BTreeMap}; +use std::sync::Arc; +use futures::{StreamExt,TryStreamExt}; +use once_cell::sync::Lazy; + +use anyhow::{bail, anyhow, Result}; +use thiserror::Error as ThisError; + +use clap::{Parser as ClapParser, ArgAction}; +use tracing::{info, error, debug, trace, Level, Instrument, error_span}; +use tracing_subscriber::FmtSubscriber; + +use k8s::api::core::v1::*; +use k8s_openapi as k8s; +use kube::{api::*, config::KubeConfigOptions}; + +use tokio::task::JoinHandle; +use tokio::io::{AsyncReadExt, AsyncWriteExt, AsyncWrite, AsyncRead}; + + +const SCHEME: &str = "k8s"; +const PAT_SCHEME: &str = r"[a-zA-Z][a-zA-Z0-9+.-]*"; +const PAT_PATH: &str = r"[0-9a-zA-Z](?:[0-9a-zA-Z.-]*[0-9a-zA-Z])?"; +static REMOTE_PATTERN: Lazy = Lazy::new(|| {regex::Regex::new(&format!("^(?P(?P{PAT_SCHEME}):)?(?:/*)(?P{PAT_PATH})?/(?P{PAT_PATH})?/(?P{PAT_PATH})?(?P/)?")).expect("regex failed to compile")}); + +#[cfg(test)] +mod test; + +#[derive(ClapParser, Debug, Clone)] +struct Config { + #[arg( + short, + long, + env = "GIT_REMOTE_K8S_IMAGE", + default_value = "alpine/git:latest" + )] + /// Docker image used for git Jobs + image: String, + + #[arg( + index=1 + )] + /// remote name + remote_name: String, + + #[arg( + index=2 + )] + /// remote URL + remote_url: String, + + #[arg( + short, + long, + env = "GIT_REMOTE_K8S_DEBUG", + action=ArgAction::Count, + )] + /// verbosity, may be specified more than once + verbose: u8, +} + +#[derive(ThisError,Debug)] +pub enum ApplicationError { + /// cluster state problems + #[error("cluster is in an inconsistent state")] + RemoteClusterInconsistent, + + /// pod state problems + #[error("pod metadata doesn't contain a name")] + PodNoName, + #[error("pod metadata doesn't contain a namespace")] + PodNoNamespace, + #[error("couldn't open pod's stdin")] + PodCouldNotOpenStdin, + #[error("couldn't open pod's stdout")] + PodCouldNotOpenStdout, + #[error("couldn't open pod's stderr")] + PodCouldNotOpenStderr, +} + +#[derive(ThisError,Debug)] +pub enum ConfigError { + #[error("no namespace present in remote URL")] + RemoteNoNamespace, + #[error("trailing elements after pvc in remote URL")] + RemoteTrailingElements, + #[error("no context present in remote URL")] + RemoteNoContext, + #[error("no PVC name present in remote URL")] + RemoteNoPVC, + #[error("invalid remote URL")] + RemoteInvalid, + #[error("remote URL has an invalid (or no) scheme")] + RemoteInvalidScheme, +} + +impl Config { + /// parse and validate a k8s remote pvc short-URL into a triple of Strings of the form (context, namespace, pvc) + /// + /// this utilizes a regex instead of url::Url to ensure that it returns sensible errors + // TODO: find a way to memoize this cleanly. probably give it access to a memoizing context from AppContext. + fn parse_and_validate(&self) -> Result<(String,String,String)> { + let caps = REMOTE_PATTERN.captures(&self.remote_url).ok_or(ConfigError::RemoteInvalid)?; + let scheme = if caps.name("scheme_prefix").is_none() { + SCHEME + } else { + caps.name("scheme").ok_or(ConfigError::RemoteInvalidScheme)?.as_str() + }; + if scheme != SCHEME { + bail!(ConfigError::RemoteInvalidScheme); + } + let kctx = caps.name("context").ok_or(ConfigError::RemoteNoContext)?.as_str(); + let ns = caps.name("namespace").ok_or(ConfigError::RemoteNoNamespace)?.as_str(); + let pvc = caps.name("pvc").ok_or(ConfigError::RemoteNoPVC)?.as_str(); + // regex::Regex::find(REMOTE_PATTERN); + if kctx == "" { + bail!(ConfigError::RemoteNoContext); + }; + if ns == "" { + bail!(ConfigError::RemoteNoNamespace); + }; + if pvc == "" { + bail!(ConfigError::RemoteNoPVC); + }; + if let Some(trailing) = caps.name("trailing") { + if trailing.as_str() != "" { + bail!(ConfigError::RemoteTrailingElements); + } + } + Ok((kctx.to_owned(), ns.to_owned(), pvc.to_owned())) + } + + fn get_remote_context(&self) -> Result { + let (r,_,_) = self.parse_and_validate()?; + Ok(r) + } + + fn get_remote_namespace(&self) -> Result { + let (_,r,_) = self.parse_and_validate()?; + Ok(r) + } + + fn get_remote_pvc(&self) -> Result { + let (_,_,r) = self.parse_and_validate()?; + Ok(r) + } +} + +struct AppContext { + config: Arc, + ensures: Vec>, +} + +impl AppContext { + fn new() -> Result { + Ok(Self { + config: Arc::new(Config::parse()), + ensures: vec![], + // ensurance: ensurance, + }) + } + fn cfg(&self) -> Arc { + self.config.clone() + } + async fn ktx(&self, context_name: Option) -> Result { + let mut kco = KubeConfigOptions::default(); + kco.context = context_name; + Ok(kube::Client::try_from(kube::Config::from_kubeconfig(&kco).await?)?) + } + fn ensure(&mut self, f: F) -> EnsureHandle + where F: Future + Send + 'static + { + let handle = tokio::runtime::Handle::current(); + let (tx, rx) = tokio::sync::oneshot::channel(); + self.ensures.push(handle.spawn(async move { + let _ = rx.await.unwrap_or_default(); // it's either unit or unit! woo + trace!("ensure handler unblocked"); + f.await; + })); + tx + } +} + +type EnsureHandle = tokio::sync::oneshot::Sender<()>; + +impl Drop for AppContext { + fn drop(&mut self) { + let handle = tokio::runtime::Handle::current(); + futures::executor::block_on(async { + for ensure in self.ensures.drain(..) { + if let Err(e) = handle.spawn( + async move { + let _ = ensure.await.unwrap_or_default(); + }).await { + eprintln!("failed to ensure in Context: {e}"); + } + }; + }); + } +} + +trait PodExt { + fn label_selectors(&self) -> Vec; + fn field_selectors(&self) -> Result>; +} + +impl PodExt for Pod { + fn label_selectors(&self) -> Vec { + let l = self.labels(); + let mut selectors = Vec::with_capacity(l.len()); + for (k,v) in l.iter() { + format!("{}={}", k, v); + }; + selectors + } + + fn field_selectors(&self) -> Result> { + Ok(vec![ + format!("metadata.name={}", self.meta().name.as_ref().ok_or(ApplicationError::PodNoName)?), + format!("metadata.namespace={}", self.meta().namespace.as_ref().ok_or(ApplicationError::PodNoNamespace)?), + ]) + } + +} + +async fn wait_for_pod_running_watch(pods: &Api, pod: Pod) -> Result<()> { + let mut wp = WatchParams::default(); + for fs in pod.field_selectors()? { + wp = wp.fields(&fs); + } + let mut stream = pods.watch(&wp, "0").await?.boxed(); + while let Some(status) = stream.try_next().await? { + match status { + WatchEvent::Modified(o) => { + let s = o.status.as_ref().expect("status exists on pod"); + if s.phase.clone().unwrap_or_default() == "Running" { + info!("Ready to attach to {}", o.name_any()); + break; + } + } + _ => {} + } + }; + Ok(()) +} + +async fn is_pod_running(pods: &Api, pod: Pod) -> Result { + let got_pod = pods.get(&pod.metadata.name.ok_or(anyhow!("pod metadata must have a name"))?).await?; + let phase = got_pod + .status.ok_or(anyhow!("pod has no status"))? + .phase.ok_or(anyhow!("pod has no status.phase"))?; + if phase == "Running" { + Ok(true) + } else { + Ok(false) + } +} + +async fn wait_for_pod_running(pods: &Api, pod: Pod) -> Result<()> { + let (tx, mut rx) = tokio::sync::mpsc::channel(1); + let xtx = tx.clone(); + let xpods = pods.clone(); + let xpod = pod.clone(); + let _p: JoinHandle> = tokio::spawn(async move { + let r = is_pod_running(&xpods, xpod).await; + if let Ok(true) = r { + xtx.send(Ok(())).await.expect("couldn't send to channel"); + } + if let Err(e) = r { + xtx.send(Err(e)).await.expect("couldn't send to channel"); + } + Ok(()) + }); + let xtx = tx.clone(); + let xpods = pods.clone(); + let xpod = pod.clone(); + let _w = tokio::spawn(async move { + xtx.send(wait_for_pod_running_watch(&xpods, xpod).await).await.expect("couldn't send on channel"); + }); + let r = rx.recv().await; + if r.is_none() { + bail!("failed to read API while waiting for pod to start"); + } + let r = r.expect("failed to extract value after checking for None"); + r +} + +#[tokio::main] +async fn main() -> ExitCode { + let mut rc = ExitCode::from(0); + if let Err(e) = main_wrapped(&mut rc).await { + error!("{}", e); + return ExitCode::from(127); + } + rc +} + +async fn set_log_level(level: Level) { + let subscriber = FmtSubscriber::builder() + .with_max_level(level) + .with_writer(std::io::stderr) + .finish(); + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); +} + +async fn main_wrapped(rc: &mut ExitCode) -> crate::Result<()> { + *rc = 1.into(); + + let ctx = AppContext::new()?; + let cfg = ctx.cfg(); + + set_log_level(match cfg.verbose { + 0 => Level::WARN, + 1 => Level::INFO, + 2 => Level::DEBUG, + _ => Level::TRACE, + }).await; + + if let Err(_) = std::env::var("GIT_DIR") { + error!("Please see https://github.com/jamesandariese/git-remote-k8s for details on use."); + bail!("not running in git"); + } + + debug!("{:?}", &cfg); + + + // these should all be &str to ensure we don't accidentally fail to copy any + // and make weird errors later. instead of making these Strings here, we need + // to to_owned() them everywhere they're referenced to make a copy to go in the + // data structure. + let __kube_ns_val = cfg.get_remote_namespace()?; + let kube_ns = __kube_ns_val.as_str(); + let __kube_context_val = cfg.get_remote_context()?; + let kube_context = __kube_context_val.as_str(); + let __kube_pvc_val = cfg.get_remote_pvc()?; + let kube_pvc = __kube_pvc_val.as_str(); + let __kube_worker_name_val = format!("git-remote-worker-{kube_pvc}"); + let kube_worker_name = __kube_worker_name_val.as_str(); + let kube_image = cfg.image.as_str(); + let kube_job_label = "sleeper"; + let kube_container_name = kube_job_label; + let kube_shell_executable = "/bin/sh"; + let kube_shell_parameters = "-c"; + let kube_shell_sleeper_command = "set -e; while true;do sleep 5;done"; + let kube_repo_mount_path = "/repo"; + + let kube_pod_labels = vec![ + ("com.kn8v.git-remote-k8s/repo-name", kube_pvc), + ("com.kn8v.git-remote-k8s/job", kube_job_label), + ]; + + info!("Remote Context: {}", kube_context); + info!("Remote Namespace: {}", kube_ns); + info!("Remote PVC Name: {}", kube_pvc); + + let client = ctx.ktx(Some(kube_context.to_owned())).await?; + + let pvcs_api = kube::Api::::namespaced(client.clone(), &kube_ns); + let pods_api = kube::Api::::namespaced(client.clone(), &kube_ns); + + // TODO: create the pvc + + // create the worker pod + let mut worker_pod = Pod::default(); + worker_pod.metadata.name = Some(kube_worker_name.to_owned()); + worker_pod.metadata.namespace = Some(kube_ns.to_owned()); + { + let mut labels = BTreeMap::new(); + for (k,v) in kube_pod_labels.iter() { + let kk = k.to_owned().to_owned(); + let vv = v.to_owned().to_owned(); + labels.insert(kk, vv); + } + worker_pod.metadata.labels = Some(labels); + } + { + let mut spec = PodSpec::default(); + spec.restart_policy = Some("Never".to_owned()); + { + let mut container = Container::default(); + container.name = kube_container_name.to_owned(); + container.command = Some(vec![ + kube_shell_executable.to_owned(), + kube_shell_parameters.to_owned(), + kube_shell_sleeper_command.to_owned(), + ]); + container.image = Some(kube_image.to_owned()); + container.working_dir = Some(kube_repo_mount_path.to_owned()); + { + let mut volume_mount = VolumeMount::default(); + volume_mount.mount_path = kube_repo_mount_path.to_owned(); + volume_mount.name = "repo".to_owned(); + container.volume_mounts = Some(vec![volume_mount]); + } + spec.containers = vec![container]; + } + { + let mut volume = Volume::default(); + volume.name = "repo".to_owned(); + { + let mut pvcs = PersistentVolumeClaimVolumeSource::default(); + pvcs.claim_name = kube_pvc.to_owned(); + volume.persistent_volume_claim = Some(pvcs); + } + spec.volumes = Some(vec![volume]); + } + worker_pod.spec = Some(spec); + } + + // debug!("Pod: {:?}", worker_pod); + let mut lp = + ListParams::default(); + for (k,v) in kube_pod_labels { + lp = lp.labels(&format!("{}={}", k.to_owned(), v)); + } + debug!("list params: {lp:#?}"); + + let worker_pods = pods_api.list(&lp).await?; + // debug!("worker_pods: {worker_pods:#?}"); + if worker_pods.items.len() > 1 { + error!("GIT-REMOTE CLUSTER IS IN AN INCONSISTENT STATE"); + error!("Your cluster has multiple pods running which are uniquely used for this repo."); + let mut i = 0; + for pod in worker_pods.items.iter() { + i += 1; + let pn = pod.metadata.name.as_ref(); + error!("pod {i}: {:?}", pn); + } + error!("Cannot continue while these pods are all running."); + bail!(ApplicationError::RemoteClusterInconsistent); + } + let pod; + if worker_pods.items.len() == 0 { + let created_pod = pods_api.create(&PostParams::default(), &worker_pod).await?; + pod = created_pod; + } else { + pod = worker_pods.items.into_iter().next() + .expect("failed to take an item from an iter which is known to have enough items"); + } + + wait_for_pod_running(&pods_api, pod).await?; + + let mut gitcommand = "1>&2 echo welcome from the git-remote-k8s worker pod ; [ -f HEAD ] || git init --bare 1>&2".to_owned(); + let mut ttyout = tokio::io::stdout(); + let mut ttyin = tokio::io::stdin(); + + // tokio::spawn(async { + // loop { + // sleep(Duration::from_secs(1)).await; + // debug!("ping"); + // }; + // }.instrument(error_span!("pinger"))); + + let connect_cmd = negotiate_git_protocol(&mut ttyout, &mut ttyin).await? + .ok_or(anyhow!("no connect command specified and we don't know how to do anything else"))?; + + gitcommand.push_str(&format!(";echo;{connect_cmd} .;RC=$?;1>&2 echo remote worker exiting;exit $RC")); + let ap = + AttachParams::default() + .stdin(true) + .stdout(true) + .stderr(true) + .container(kube_container_name.to_owned()); + // let (ready_tx, ready_rx) = oneshot::channel::<()>(); + let mut stuff =pods_api.exec(kube_worker_name, vec!["sh", "-c", &gitcommand], &ap).await?; + let mut podout = stuff.stdout().ok_or(ApplicationError::PodCouldNotOpenStdout)?; + let mut podin = stuff.stdin().ok_or(ApplicationError::PodCouldNotOpenStdin)?; + // pod stderr is handled specially + let poderr = stuff.stderr().ok_or(ApplicationError::PodCouldNotOpenStderr)?; + let mut poderr = tokio_util::io::ReaderStream::new(poderr); + // ready_tx.send(()).expect("failed to send ready check"); + + let barrier = Arc::new(tokio::sync::Barrier::new(4)); + + let xbarrier: Arc = barrier.clone(); + let _jhe = tokio::spawn(async move { + debug!("entering"); + while let Some(l) = poderr.next().await { + if let Err(e) = l { + error!("error reading from pod stderr {e}"); + break; + } else if let Ok(l) = l { + let l = String::from_utf8_lossy(l.as_ref()); + let l = l.trim(); + info!("from pod: {l}"); + } + } + debug!("waiting for group to exit"); + xbarrier.wait().await; + debug!("exiting"); + }.instrument(error_span!("pod->tty", "test" = "fred"))); + + let xbarrier: Arc = barrier.clone(); + let _jhi = tokio::spawn(async move{ + debug!("entering"); + tokio::io::copy(&mut ttyin, &mut podin).await.expect("error copying tty input to pod input"); + podin.flush().await.expect("final flush to pod failed"); + debug!("waiting for group to exit"); + xbarrier.wait().await; + debug!("exiting"); + }.instrument(error_span!("git->pod"))); + let xbarrier: Arc = barrier.clone(); + let _jho = tokio::spawn(async move { + debug!("entering"); + tokio::io::copy(&mut podout, &mut ttyout).await.expect("error copying pod output to tty output"); + ttyout.flush().await.expect("final flush to git failed"); + debug!("waiting for group to exit"); + xbarrier.wait().await; + debug!("exiting"); + }.instrument(error_span!("git<-pod"))); + + let status = stuff.take_status() + .expect("failed to take status").await + .ok_or(anyhow!("could not take status of remote git worker"))?; + // this is an exit code which is always nonzero. + // we'll _default_ to 1 instead of 0 because we only return _anything_ other than 0 + // when NonZeroExitCode is also given as the exit reason. + debug!("exit code of job: {status:#?}"); + let exitcode = (|| -> Option{ + let exitcode = status.details.as_ref()?.causes.as_ref()?.first()?.message.to_owned()?; + if let Ok(rc) = exitcode.parse::() { + return Some(rc); + } + return Some(1); + })().unwrap_or(1); + debug!("exit status code of remote job discovered was {exitcode:?}"); + // finally, we'll set the exit code of the entire application + // to the exit code of the pod, if possible. if we know it's + // non-zero and can't figure out what it was, it will be 1. + // if we know it's zero (because it's not non-zero), then we + // return 0. if we _can_ figure it out though, we use the + // integer exit code. + if status.reason == Some("NonZeroExitCode".to_owned()) { + info!("exit status of remote job: {}", exitcode); + *rc = exitcode.into(); + } else { + *rc = 0.into(); + } + debug!("waiting for group to exit"); + barrier.wait().await; + let _ = stuff.join().await?; + + Ok(()) +} + +async fn get_tokio_line_by_bytes(podout: &mut (impl AsyncRead + Unpin)) -> Result { + let mut r = String::with_capacity(512); + + loop{ + let c = podout.read_u8().await?; + if c == b'\n' { + return Ok(r); + } + r.push(c.into()) + } +} + +#[derive(ThisError,Debug)] +enum ProtocolError { + #[error("no command given via git protocol")] + NoCommandGiven, + #[error("no service given to connect command")] + NoServiceGiven, + #[error("unknown command specified")] + UnknownCommand(String), +} + +async fn negotiate_git_protocol(ttytx: &mut (impl AsyncWrite + Unpin), ttyrx: &mut (impl AsyncRead + Unpin)) -> Result> { + loop { + let cmd = get_tokio_line_by_bytes(ttyrx).await?; + let mut argv = cmd.split_whitespace(); + let cmd = argv.next().ok_or(ProtocolError::NoCommandGiven)?; + + match cmd { + "capabilities" => { + ttytx.write_all(b"connect\n\n").await?; + }, + "connect" => { + let service = argv.next().ok_or(ProtocolError::NoServiceGiven)?; + return Ok(Some(service.to_owned())); + }, + unknown => { + return Err(anyhow!(ProtocolError::UnknownCommand(unknown.to_owned()))); + }, + } + } +} diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..ab3b090 --- /dev/null +++ b/src/test.rs @@ -0,0 +1,124 @@ +use crate::*; + +#[test] +fn test_config_extractors() -> Result<()> { + let newcfg = Config::parse_from(vec!["x", "x", "k8s://test-context/test-namespace/test-pvc"]); + assert_eq!("test-context", newcfg.get_remote_context()?); + assert_eq!("test-namespace", newcfg.get_remote_namespace()?); + assert_eq!("test-pvc", newcfg.get_remote_pvc()?); + Ok(()) +} + +#[test] +fn test_config_extractors_relative() -> Result<()> { + let newcfg = Config::parse_from(vec!["x", "x", "k8s:test-context/test-namespace/test-pvc"]); + assert_eq!("test-context", newcfg.get_remote_context()?); + assert_eq!("test-namespace", newcfg.get_remote_namespace()?); + assert_eq!("test-pvc", newcfg.get_remote_pvc()?); + Ok(()) +} + +#[test] +fn test_config_extractors_omitted_schema() -> Result<()> { + let newcfg = Config::parse_from(vec!["x", "x", "test-context/test-namespace/test-pvc"]); + assert_eq!("test-context", newcfg.get_remote_context()?); + assert_eq!("test-namespace", newcfg.get_remote_namespace()?); + assert_eq!("test-pvc", newcfg.get_remote_pvc()?); + Ok(()) +} + +#[test] +fn test_config_extractors_omitted_schema_absolute_path() -> Result<()> { + let newcfg = Config::parse_from(vec!["x", "x", "/test-context/test-namespace/test-pvc"]); + assert_eq!("test-context", newcfg.get_remote_context()?); + assert_eq!("test-namespace", newcfg.get_remote_namespace()?); + assert_eq!("test-pvc", newcfg.get_remote_pvc()?); + Ok(()) +} + +#[test] +fn test_config_extractors_trailing_slash() { + let newcfg = Config::parse_from(vec!["x", "x", "k8s://test-context/test-namespace/test-pvc/"]); + assert_eq!( + newcfg.get_remote_pvc().unwrap_err().to_string(), + ConfigError::RemoteTrailingElements.to_string(), + ); +} + +#[test] +fn test_config_extractors_too_many_elements() { + let newcfg = Config::parse_from(vec!["x", "x", "k8s://test-context/test-namespace/test-pvc/blah"]); + assert_eq!( + newcfg.get_remote_pvc().unwrap_err().to_string(), + ConfigError::RemoteTrailingElements.to_string(), + ); +} + +#[test] +fn test_config_extractors_blank_namespace() { + let newcfg = Config::parse_from(vec!["x", "x", "k8s://test-context//test-pvc"]); + let expected_err = newcfg.get_remote_namespace().expect_err("Expected RemoteNoNamespace error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteNoNamespace.to_string()); +} + +#[test] +fn test_config_extractors_blank_context() { + let newcfg = Config::parse_from(vec!["x", "x", "k8s:///test-namespace/test-pvc"]); + let expected_err = newcfg.get_remote_context().expect_err("Expected RemoteNoContext error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteNoContext.to_string()); +} + +#[test] +fn test_config_extractors_only_scheme() { + let newcfg = Config::parse_from(vec!["x", "x", "k8s:"]); + let expected_err = newcfg.get_remote_context().expect_err("Expected RemoteInvalid error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteInvalid.to_string()); +} + +#[test] +fn test_config_extractors_nothing() { + let newcfg = Config::parse_from(vec!["x", "x", ""]); + let expected_err = newcfg.get_remote_context().expect_err("Expected generic RemoteInvalid error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteInvalid.to_string()); +} + +#[test] +fn test_config_extractors_single_colon() { + let newcfg = Config::parse_from(vec!["x", "x", ":"]); + let expected_err = newcfg.get_remote_context().expect_err("Expected generic RemoteInvalid error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteInvalid.to_string()); +} + +#[test] +fn test_config_extractors_single_name() { + let newcfg = Config::parse_from(vec!["x", "x", "ted"]); + let expected_err = newcfg.get_remote_context().expect_err("Expected generic RemoteInvalid error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteInvalid.to_string()); +} + +#[test] +fn test_config_extractors_single_slash() { + let newcfg = Config::parse_from(vec!["x", "x", "/"]); + let expected_err = newcfg.get_remote_context().expect_err("Expected generic RemoteInvalid error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteInvalid.to_string()); +} + +#[test] +fn test_config_extractors_crazy_scheme() { + let newcfg = Config::parse_from(vec!["x", "x", "crazyscheme://ctx/ns/pvc"]); + let expected_err = newcfg.get_remote_context().expect_err("Expected generic RemoteInvalid error"); + assert_eq!(expected_err.to_string(), ConfigError::RemoteInvalidScheme.to_string()); +} + +#[test] +/// tests to ensure the appropriate error is returned in the face of many errors +/// specifically, if the scheme is invalid, anything else could be happening in +/// the url and it might not be an error _for that kind of URL_ +/// so note first when the scheme is wrong because it might be the _only_ error +/// that's truly present. +fn test_config_extractors_crazy_scheme_and_other_problems() { + let newcfg = Config::parse_from(vec!["x", "x", "crazyscheme:///ns"]); + let expected_err = newcfg.get_remote_context().expect_err("Expected generic RemoteInvalid error"); + let eestr = expected_err.to_string(); + assert_eq!(eestr, ConfigError::RemoteInvalidScheme.to_string()); +}