diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ee95cc6..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -.opencode -.envrc diff --git a/Cargo.lock b/Cargo.lock index 6aaf74c..1cdd132 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,15 +8,15 @@ version = "0.6.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b657e772794c6b04730ea897b66a058ccd866c16d1967da05eeeecec39043fe" dependencies = [ - "crypto-common 0.2.2", + "crypto-common 0.2.1", "inout", ] [[package]] name = "aes" -version = "0.9.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" +checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" dependencies = [ "cipher", "cpubits", @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.11.0-rc.4" +version = "0.11.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da8c919c118108f144adecad74b425b804ad075580d605d9b33c2d6d1c62a2f8" +checksum = "e22c0c90bbe8d4f77c3ca9ddabe41a1f8382d6fc1f7cea89459d0f320371f972" dependencies = [ "aead", "aes", @@ -67,15 +67,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[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 = "anyhow" version = "1.0.102" @@ -131,6 +122,17 @@ dependencies = [ "syn", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -139,9 +141,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-fips-sys" @@ -279,7 +281,7 @@ dependencies = [ "quote", "regex", "rustc-hash", - "shlex 1.3.0", + "shlex", "syn", ] @@ -301,7 +303,6 @@ dependencies = [ "cfg-if", "constant_time_eq", "cpufeatures 0.3.0", - "serde", "zeroize", ] @@ -324,20 +325,11 @@ dependencies = [ "zeroize", ] -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - [[package]] name = "bumpalo" -version = "3.20.3" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" @@ -347,14 +339,14 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.63" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex 2.0.1", + "shlex", ] [[package]] @@ -372,6 +364,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chacha20" version = "0.10.0" @@ -385,26 +383,14 @@ dependencies = [ "zeroize", ] -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - [[package]] name = "cipher" -version = "0.5.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.2", + "crypto-common 0.2.1", "inout", "zeroize", ] @@ -431,9 +417,9 @@ dependencies = [ [[package]] name = "cmov" -version = "0.5.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" [[package]] name = "concurrent-queue" @@ -530,18 +516,18 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" dependencies = [ "hybrid-array", ] [[package]] name = "ctr" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21" +checksum = "17469f8eb9bdbfad10f71f4cfddfd38b01143520c0e717d8796ccb4d44d44e42" dependencies = [ "cipher", ] @@ -557,12 +543,12 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "5.0.0-rc.0" +version = "5.0.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bf" +checksum = "335f1947f241137a14106b6f5acc5918a5ede29c9d71d3f2cb1678d5075d9fc3" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.11.3", "fiat-crypto", @@ -615,16 +601,6 @@ dependencies = [ "syn", ] -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", - "serde_core", -] - [[package]] name = "derive_more" version = "2.1.1" @@ -672,7 +648,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", - "crypto-common 0.2.2", + "crypto-common 0.2.1", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -704,9 +691,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "3.0.0-rc.0" +version = "3.0.0-pre.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60" +checksum = "20449acd54b660981ae5caa2bcb56d1fe7f25f2e37a38ec507400fab034d4bb6" dependencies = [ "curve25519-dalek", "ed25519", @@ -728,9 +715,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "enum-ordinalize" @@ -807,6 +794,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" @@ -938,8 +931,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -949,9 +944,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 5.3.0", "wasip2", + "wasm-bindgen", ] [[package]] @@ -996,10 +993,23 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "h2" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] name = "hashbrown" @@ -1033,12 +1043,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hostname" version = "0.4.2" @@ -1052,9 +1056,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", "itoa", @@ -1107,14 +1111,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.10.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -1162,13 +1167,16 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ + "base64", "bytes", "futures-channel", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", "tokio", @@ -1177,27 +1185,85 @@ dependencies = [ ] [[package]] -name = "iana-time-zone" -version = "0.1.65" +name = "icu_collections" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "icu_locale_core" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ - "cc", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] @@ -1213,14 +1279,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "indexmap" -version = "1.9.3" +name = "idna" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1244,6 +1320,12 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + [[package]] name = "itertools" version = "0.13.0" @@ -1270,9 +1352,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.28" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -1280,14 +1362,14 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-link", + "windows-sys 0.61.2", ] [[package]] name = "jiff-static" -version = "0.2.28" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", @@ -1321,9 +1403,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ "cfg-if", "futures-util", @@ -1374,7 +1456,7 @@ checksum = "51b326f5219dd55872a72c1b6ddd1b830b8334996c667449c29391d657d78d5e" dependencies = [ "base64", "jiff", - "schemars 1.2.1", + "schemars", "serde", "serde_json", ] @@ -1439,7 +1521,7 @@ dependencies = [ "jiff", "json-patch", "k8s-openapi", - "schemars 1.2.1", + "schemars", "serde", "serde-value", "serde_json", @@ -1503,21 +1585,20 @@ dependencies = [ "kube", "mimalloc", "opentelemetry", + "opentelemetry-otlp", "opentelemetry_sdk", "prometheus-client", "rand 0.10.1", "rand_hc", - "schemars 1.2.1", + "schemars", "scopeguard", "serde", "serde_json", - "serde_with", "serde_yaml", "smallvec", "ssh-key", "thiserror", "tokio", - "tokio-util", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -1554,13 +1635,19 @@ dependencies = [ [[package]] name = "libmimalloc-sys" -version = "0.1.49" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9" +checksum = "2d1eacfa31c33ec25e873c136ba5669f00f9866d0688bea7be4d3f7e43067df6" dependencies = [ "cc", ] +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + [[package]] name = "lock_api" version = "0.4.14" @@ -1572,9 +1659,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.30" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "matchers" @@ -1593,15 +1686,15 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mimalloc" -version = "0.1.52" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862" +checksum = "b3627c4272df786b9260cabaa46aec1d59c93ede723d4c3ef646c503816b0640" dependencies = [ "libmimalloc-sys", ] @@ -1620,9 +1713,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -1648,12 +1741,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - [[package]] name = "num-traits" version = "0.2.19" @@ -1677,9 +1764,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "opentelemetry" -version = "0.32.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" +checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" dependencies = [ "futures-core", "futures-sink", @@ -1690,19 +1777,65 @@ dependencies = [ ] [[package]] -name = "opentelemetry_sdk" -version = "0.32.1" +name = "opentelemetry-http" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9" +checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" +dependencies = [ + "http", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "thiserror", + "tokio", + "tonic", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", + "tonic-prost", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" dependencies = [ "futures-channel", "futures-executor", "futures-util", "opentelemetry", "percent-encoding", - "portable-atomic", "rand 0.9.4", "thiserror", + "tokio", + "tokio-stream", ] [[package]] @@ -1875,10 +2008,13 @@ dependencies = [ ] [[package]] -name = "powerfmt" -version = "0.2.0" +name = "potential_utf" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] [[package]] name = "ppv-lite86" @@ -1931,6 +2067,84 @@ dependencies = [ "syn", ] +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.45" @@ -2065,6 +2279,46 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ring" version = "0.17.14" @@ -2111,9 +2365,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -2127,6 +2381,7 @@ version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ + "web-time", "zeroize", ] @@ -2162,18 +2417,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - [[package]] name = "schemars" version = "1.2.1" @@ -2306,9 +2549,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", @@ -2340,45 +2583,13 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "3.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" -dependencies = [ - "base64", - "bs58", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.14.0", - "schemars 0.9.0", - "schemars 1.2.1", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.14.0", + "indexmap", "itoa", "ryu", "serde", @@ -2422,12 +2633,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -2458,9 +2663,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", "windows-sys 0.61.2", @@ -2468,9 +2673,9 @@ dependencies = [ [[package]] name = "ssh-cipher" -version = "0.3.0-rc.10" +version = "0.3.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68126a396f08c64551b44cdb65865ef1d78fdf16ac02b53f78588227f60466fe" +checksum = "10db6f219196a8528f9ec904d9d45cdad692d65b0e57e72be4dedd1c5fddce36" dependencies = [ "aes", "aes-gcm", @@ -2484,9 +2689,9 @@ dependencies = [ [[package]] name = "ssh-encoding" -version = "0.3.0" +version = "0.3.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b54d0ed0498daf3f78d82e00e28c8eec9d75a067c4cfbcc7a0f7d0f4077749e" +checksum = "7abf34aa716da5d5b4c496936d042ea282ab392092cd68a72ef6a8863ff8c96a" dependencies = [ "base64ct", "ctutils", @@ -2512,6 +2717,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "strsim" version = "0.11.1" @@ -2540,6 +2751,20 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "thiserror" @@ -2571,34 +2796,13 @@ dependencies = [ ] [[package]] -name = "time" -version = "0.3.47" +name = "tinystr" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", + "displaydoc", + "zerovec", ] [[package]] @@ -2622,6 +2826,7 @@ version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ + "bytes", "libc", "mio", "pin-project-lite", @@ -2652,6 +2857,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -2666,6 +2882,43 @@ dependencies = [ "tokio", ] +[[package]] +name = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "async-trait", + "base64", + "bytes", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost", + "tonic", +] + [[package]] name = "tower" version = "0.5.3" @@ -2674,7 +2927,9 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project-lite", + "slab", "sync_wrapper", "tokio", "tokio-util", @@ -2685,13 +2940,14 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.11" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" dependencies = [ "base64", "bitflags", "bytes", + "futures-util", "http", "http-body", "mime", @@ -2700,6 +2956,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", + "url", ] [[package]] @@ -2760,9 +3017,9 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.33.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" +checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" dependencies = [ "js-sys", "opentelemetry", @@ -2800,9 +3057,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.20.1" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -2828,7 +3085,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96" dependencies = [ - "crypto-common 0.2.2", + "crypto-common 0.2.1", "ctutils", ] @@ -2850,6 +3107,24 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "valuable" version = "0.1.1" @@ -2897,9 +3172,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -2909,10 +3184,20 @@ dependencies = [ ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.122" +name = "wasm-bindgen-futures" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2920,9 +3205,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -2933,9 +3218,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -2957,7 +3242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.14.0", + "indexmap", "wasm-encoder", "wasmparser", ] @@ -2970,10 +3255,20 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags", "hashbrown 0.15.5", - "indexmap 2.14.0", + "indexmap", "semver", ] +[[package]] +name = "web-sys" +version = "0.3.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web-time" version = "1.1.0" @@ -2984,72 +3279,28 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -3067,14 +3318,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3083,48 +3351,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "wit-bindgen" version = "0.51.0" @@ -3159,7 +3475,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.14.0", + "indexmap", "prettyplease", "syn", "wasm-metadata", @@ -3190,7 +3506,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags", - "indexmap 2.14.0", + "indexmap", "log", "serde", "serde_derive", @@ -3209,7 +3525,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.14.0", + "indexmap", "log", "semver", "serde", @@ -3220,25 +3536,75 @@ dependencies = [ ] [[package]] -name = "zerocopy" -version = "0.8.50" +name = "writeable" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.2" @@ -3259,6 +3625,39 @@ dependencies = [ "syn", ] +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index a64b8e0..2898c1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,16 +18,18 @@ mimalloc.version = "^0.1" mimalloc.features = ["secure"] tokio.version = "^1" tokio.features = ["macros", "rt-multi-thread"] -tokio-util.version = '^0.7' axum.version = "^0.8" ## Tracing tracing.version = "^0.1" -tracing.features = ["max_level_debug", "release_max_level_info"] +tracing.features = ["max_level_trace", "release_max_level_info"] tracing-subscriber.version = "^0.3" -tracing-opentelemetry.version = "^0.33" -opentelemetry.version = "^0.32" -opentelemetry_sdk.version = "^0.32" -prometheus-client.version = "^0.24" +tracing-opentelemetry.version = "^0.32" +opentelemetry.version = "^0.31" +opentelemetry.features = ["trace"] +opentelemetry-otlp.version = "^0.31" +opentelemetry-otlp.features = ["grpc-tonic", "reqwest-rustls"] +opentelemetry_sdk.version = "^0.31" +opentelemetry_sdk.features = ["rt-tokio"] ## Utils jiff.version = "^0.2" scopeguard.version = "^1" @@ -39,8 +41,6 @@ schemars.version = "^1" base64.version = "^0.22" serde.version = "^1" serde.features = ["derive"] -serde_with.version = "3" -serde_with.features = ["hex", "schemars_1"] serde_json.version = "^1" serde_yaml.version = "^0.9" ## k8s @@ -48,6 +48,7 @@ kube.version = "^3" kube.features = ["derive"] k8s-openapi.version = "^0.27" k8s-openapi.features = ["latest", "schemars"] +prometheus-client.version = "^0.24" ## Codegen thiserror.version = "^2" anyhow.version = "^1" diff --git a/README.md b/README.md deleted file mode 100644 index 2d2df58..0000000 --- a/README.md +++ /dev/null @@ -1,185 +0,0 @@ -# K8s secret generator - -> 🚧 Please, if you encountered this repo, note that this is under heavy construction. - -> *AI*: This was also an exploration of opencode features, so there is some amount of AI-generated boilerplate. - -## Features - -### Generate namespaced secrets from specification - -To generate arbitrary secret deploy `RandomSecret` resource: - -```yaml -apiVersion: generator.kuyaseg.spushk.ru/v1alpha1 -kind: RandomSecret -metadata: - name: secret - namespace: default -spec: - # Specify all keys to generate and put into secret - keys: - # Binary key - random byte sequence - - key: binary-key - # Change to regenerate value without altering spec - version: 0 - # Determine secret shape - shape: !Bytes - # Set size of value in bytes - size: 64 - # Password key - guaranteed UTF-8 character sequence with repeat period >= 3 - - key: password-key - version: 0 - shape: !Password - # Set length of password in symbols - length: 24 - # Specify which symbols are allowed to use when generating password - dictionary: "abcdefghijklmnopqrstuvwxyz0123456789_" - # Required fields are `key` and `shape` - - key: default-binary-key - shape: !Bytes - - key: default-password - shape: !Password - # Adjust data, stringData and metadata of generated Secret - template: {} -``` - -#### SSH keys - -To generate SSH key deploy `RandomSsh` resource: - -```yaml -apiVersion: generator.kuyaseg.spushk.ru/v1alpha1 -kind: RandomSsh -metadata: - name: ssh-secret - namespace: default -spec: - # Configure keypair configuration - keypair: - # If changes after last reconcilation, will force regeneration with new keypair - version: 0 - # `ed25519` is default. `rsa` is WIP - algorithm: ed25519 - # optional field to set pubkey comment - comment: mysecret - # Configure SSH private key - identity: - # The key to put generated identity to - key: private-key - # Adjust data, stringData and metadata of generated Secret - template: {} - # Configure SSH public key - pubkey: - # The key to put generated identity to - key: public-key - # Adjust data, stringData and metadata of generated Secret - template: {} -``` - -The following template properties have additional validation: -- `metadata.owner_references`: controller owner is not allowed - -When reconciled, this resource will produce two `v1/Secret` resources in the same namespace: -- `-identity`: private SSH key, which is stored in specified key -- `-pubkey`: public SSH key which is stored in specified key - -Both resources inherit namespace and name (as prefix) from `RandomSsh` resource and are immutable. - -### Deterministic secrets - -> ⚠️ This feature exists primarily for testing purposes, where security is less of a concern then repeatability. -> -> While there are use-cases for predictable secrets in production (e.g. DB credentials in some cases), -> you should consider [ESO](https://external-secrets.io) -> with secure external storage to share secrets between clusters and keep secrets stable. -> -> If the seed secret gets compromised - every past and future generated secret can be easliy computed. - -Kuyaseg is able to provide deterministic source for secret and SSH keypair generation, offering idempotent sequence of secret values. - -With deterministic secrets value remains the same even if specification was deleted and created anew. - -This could be useful for testing, debugging and learning in local environments. - -Deterministic secrets are created with `DeterministicSecret` and `DeterministicSsh` resources, matching `RandomSecret` and `RandomSsh` resource schema: - -```yaml -# Schema matches the Secret from generator.kuyaseg.spushk.ru/v1alpha1 -apiVersion: generator.kuyaseg.spushk.ru/v1alpha1 -kind: DeterministicSecret -metadata: - name: deterministic-secret - namespace: testing -spec: - keys: - - key: debug-password - shape: !Password - template: - stringData: - username: debug-user -``` - -Secret values generated by deterministic generator depend on: -- Global `seed` value set for controller -- `metadata.name` and `metadata.namespace` of `DeterministicSecret` resource -- `spec.keys.*.key` and `spec.key.*.version` for each generated key specified in `DeterministicSecret` resource - -Thus, if seed secret remains stable and DeterministicSecret specification does not change - generated keys remain stable and predictable - -To set the `seed` for the controller, deploy a `v1/Secret` and target the controller to it with `KUYASEG_DETERMINISTIC_SEED` env variable. - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: controller-seed - namespace: testing -stringData: - my_seed: aabbcc123 -``` - -`KUYASEG_DETERMINISTIC_SEED=controller-seed/my_seed` - -When the later part `/my_seed` is omitted, controller will expect the `seed` key to contain seed value. - -When the seed *value* changes, all known secrets are reconciled. - -## Feature comparison - -We know there are plenty of decent, mature and well-adopted solutions like the one by [mittwald](https://github.com/mittwald/kubernetes-secret-generator) and even [ESO](https://external-secrets.io) which can also generate secrets. - -Here is a comparison. - -||Kuyaseg|kubernetes-secret-generator|ESO|| -|-|-|-|-|-| -|Generate cryptographically secure namespaced secrets|aws-lc-rs+fips, allows to specify generated **keys per resource**|Cryptographically secure, allows to specify generated **keys per resource**|Cryptographically secure, but generated keys are specified **per generator**|| -|Generate SSH keypairs|Ed25519 and RSA OpenSSH keypairs, private and public keys **stored in separate secrets**|Both secret and public keys are **stored in one secret**|Not supported|| -|Deterministic, reproducible secret generation|Available, Hc128 CSPRNG, optional, based on provided seed and secret specs. Stable|Not supported|Not supported|| -|Target secret template|Respects |[Not supported](https://github.com/mittwald/kubernetes-secret-generator/issues/88)|Allows to specity template in `ExternalSecret`|| - -## Security - -⚠️ This software have never received independent security audit. I am not a security expert. - ---- - -For security issues, please, view the [security policy](https://source.spushk.ru/security) and kindly e-mail -us at [reports@spushk.ru](mailto:reports@spushk.ru?subject=%5BSECURITY%5D%3A%20describe%20the%20issue&body=Thank%20you%20for%20submitting%20security%20report%20%E2%9D%A4%EF%B8%8F%0AWe%20kindly%20ask%20you%20not%20to%20publically%20disclose%20any%20information%20related%20to%20your%20findings%20just%20yet.%0APlease%2C%20fill%20out%20the%20following%20sections%20and%20provide%20any%20additional%20information%20you%27re%20willing%20to%20share.%0A%0A-------------------------------------------------------------------------------%20FINDING%0AWhat%20is%20the%20substance%20of%20an%20issue%3F%0A%0A-------------------------------------------------------------------------------%20AFFECTED%20SOFTWARE%0AWhich%20product%20and%20which%20versions%20is%20affected%3F%0A%0A-------------------------------------------------------------------------------%20REPRODUCTION%0APlease%2C%20share%20PoC%20if%20you%20have%20one%0A%0A-------------------------------------------------------------------------------%20DISCLOSURE%0AWhat%20are%20you%20expectations%20on%20disclosure%3F%0AWe%20kindly%20ask%20you%20not%20to%20share%20any%20information%20publically%0Aat%20least%20for%2090%20days%20since%20this%20report.%0A%0A-------------------------------------------------------------------------------%20CONTACT%0AHow%20do%20we%20contact%20you%3F) - ---- - -### Secret generation - -To produce random secrets (`RandomSecret`, `RandomSshSecret`), operator uses [aws-lc-rs+fips](https://docs.rs/aws-lc-rs/latest/aws_lc_rs/). - -To produce deterministic secrets (`DeterministicSecret`, `DeterministicSsh`), operator uses [Hc128](https://docs.rs/rand_hc/latest/rand_hc/) CSPRNG. - -### Secret persistence - -Random secrets defined by `RandomSecret` and `RandomSsh` are unstable: deletion of generated `Secret` will produce new random value on next reconcilation. - -Deterministic secrets defined by `DeterministicSecret` and `DeterministicSsh` are stable as long as the base `seed` and spec/version remain unchanged. -Deletion of generated `Secret` resources will lead to the same pseudo-random value put into the secret on next reconcilation. - -Best effort is taken to ensure that secrets do not remain in memory longer then needed and/or after crush. diff --git a/kuyaseg-controller/Cargo.toml b/kuyaseg-controller/Cargo.toml index 8affe9a..0e6e153 100644 --- a/kuyaseg-controller/Cargo.toml +++ b/kuyaseg-controller/Cargo.toml @@ -11,14 +11,13 @@ publish.workspace = true [[bin]] doc = false -name = "crdgen" -path = "src/bin/crdgen.rs" +name = "generate_crds" +path = "src/bin/generate_crds.rs" [dependencies] ## Rt mimalloc.workspace = true tokio.workspace = true -tokio-util.workspace = true axum.workspace = true ## Tracing tracing.workspace = true @@ -26,6 +25,7 @@ tracing-subscriber.workspace = true tracing-subscriber.features = ["env-filter"] tracing-opentelemetry.workspace = true opentelemetry.workspace = true +opentelemetry-otlp.workspace = true opentelemetry_sdk.workspace = true prometheus-client.workspace = true ## Utils @@ -38,7 +38,6 @@ itertools.workspace = true schemars.workspace = true base64.workspace = true serde.workspace = true -serde_with.workspace = true serde_json.workspace = true serde_yaml.workspace = true ## k8s @@ -55,5 +54,4 @@ ssh-key.workspace = true ssh-key.features = ["ed25519"] rand_hc.workspace = true blake3.workspace = true -blake3.features = ["serde"] zeroize.workspace = true diff --git a/kuyaseg-controller/src/bin/crdgen.rs b/kuyaseg-controller/src/bin/crdgen.rs deleted file mode 100644 index 8045fc9..0000000 --- a/kuyaseg-controller/src/bin/crdgen.rs +++ /dev/null @@ -1,3 +0,0 @@ -use kube::CustomResourceExt; - -fn main() {} diff --git a/kuyaseg-controller/src/bin/generate_crds.rs b/kuyaseg-controller/src/bin/generate_crds.rs new file mode 100644 index 0000000..e71fdf5 --- /dev/null +++ b/kuyaseg-controller/src/bin/generate_crds.rs @@ -0,0 +1 @@ +fn main() {} \ No newline at end of file diff --git a/kuyaseg-controller/src/generator.rs b/kuyaseg-controller/src/generator.rs index f359164..ad6a384 100644 --- a/kuyaseg-controller/src/generator.rs +++ b/kuyaseg-controller/src/generator.rs @@ -30,15 +30,10 @@ pub fn bytes(rng: &mut G, size: usize) -> Vec { buf } -pub fn str_bytes( - rng: &mut G, - length: usize, - dictionary: impl Iterator, -) -> Result, GeneratorErr> { +pub fn str_bytes(rng: &mut G, length: usize, dictionary: &[char]) -> Result, GeneratorErr> { let mut buf = String::with_capacity(length); - // TODO: unique may leak dictionary..? - let dictionary = Zeroizing::new(dictionary.unique().collect::>()); + let dictionary = Zeroizing::new(dictionary.iter().unique().cloned().collect::>()); let distribution = rand::distr::slice::Choose::new(&dictionary).map_err(|_| GeneratorErr::EmptyDistribution)?; let dict_size = dictionary.len(); @@ -70,12 +65,12 @@ pub fn str_bytes( } if buf.len() != length { - return Err(GeneratorErr::RngStuck { + Err(GeneratorErr::RngStuck { reason: format!("Unable to sample {length} characters"), - }); + }) + } else { + Ok(buf.into_bytes()) } - - Ok(buf.into_bytes()) } /// Generate SSH keypair, encoded for OpenSSH @@ -199,11 +194,11 @@ mod test { let mut rng = AwsRng::new(); let dict = dict_alnum(32); - let gen0 = str_bytes(&mut rng, 0, dict.iter().cloned()).unwrap(); - let gen1 = str_bytes(&mut rng, 1, dict.iter().cloned()).unwrap(); - let gen27 = str_bytes(&mut rng, 27, dict.iter().cloned()).unwrap(); - let gen256 = str_bytes(&mut rng, 256, dict.iter().cloned()).unwrap(); - assert_ne!(str_bytes(&mut rng, 256, dict.iter().cloned()).unwrap(), gen256); + let gen0 = str_bytes(&mut rng, 0, &dict).unwrap(); + let gen1 = str_bytes(&mut rng, 1, &dict).unwrap(); + let gen27 = str_bytes(&mut rng, 27, &dict).unwrap(); + let gen256 = str_bytes(&mut rng, 256, &dict).unwrap(); + assert_ne!(str_bytes(&mut rng, 256, &dict).unwrap(), gen256); // length assert_eq!(gen0.len(), 0); @@ -227,7 +222,7 @@ mod test { let mut rng = AwsRng::new(); let dict: Vec = dict_alnum(3); - let gen16 = str_bytes(&mut rng, 16, dict.iter().cloned()).unwrap(); + let gen16 = str_bytes(&mut rng, 16, &dict).unwrap(); assert_eq!(gen16.len(), 16); assert_dict_chars(&gen16, &dict); @@ -240,6 +235,8 @@ mod test { verify_ssh_pair(&pair); } + // ====== Deterministic (Hc128Rng) ====== + #[test] fn deterministic_bytes() { // we believe that generation is the same as for random, so our concern here is only value stability @@ -261,12 +258,12 @@ mod test { let dict = dict_alnum(32); assert_eq!( - str_bytes(&mut Hc128Rng::seed_from_u64(42), 256, dict.iter().cloned()).unwrap(), - str_bytes(&mut Hc128Rng::seed_from_u64(42), 256, dict.iter().cloned()).unwrap() + str_bytes(&mut Hc128Rng::seed_from_u64(42), 256, &dict).unwrap(), + str_bytes(&mut Hc128Rng::seed_from_u64(42), 256, &dict).unwrap() ); assert_ne!( - str_bytes(&mut Hc128Rng::seed_from_u64(42), 256, dict.iter().cloned()).unwrap(), - str_bytes(&mut Hc128Rng::seed_from_u64(43), 256, dict.iter().cloned()).unwrap() + str_bytes(&mut Hc128Rng::seed_from_u64(42), 256, &dict).unwrap(), + str_bytes(&mut Hc128Rng::seed_from_u64(43), 256, &dict).unwrap() ); } diff --git a/kuyaseg-controller/src/lib.rs b/kuyaseg-controller/src/lib.rs index c185f23..fed3523 100644 --- a/kuyaseg-controller/src/lib.rs +++ b/kuyaseg-controller/src/lib.rs @@ -1,4 +1,5 @@ +#![feature(sync_nonpoison, nonpoison_rwlock)] + pub mod generator; pub mod reporting; -pub mod resource; pub mod utils; diff --git a/kuyaseg-controller/src/reporting/k8s.rs b/kuyaseg-controller/src/reporting/k8s_event.rs similarity index 86% rename from kuyaseg-controller/src/reporting/k8s.rs rename to kuyaseg-controller/src/reporting/k8s_event.rs index 8254852..5bd1294 100644 --- a/kuyaseg-controller/src/reporting/k8s.rs +++ b/kuyaseg-controller/src/reporting/k8s_event.rs @@ -25,7 +25,7 @@ impl K8sEventRecorder { .recorder .publish(&event.event, &event.object) .await - .inspect_err(|e| tracing::error!(k13t.no_report=true, debug=?e, "Failed to record K8s event")); + .inspect_err(|e| tracing::error!(no_event=true, error=?e, "Failed to record K8s event")); } } diff --git a/kuyaseg-controller/src/reporting/mod.rs b/kuyaseg-controller/src/reporting/mod.rs index 6132d3f..240a9c4 100644 --- a/kuyaseg-controller/src/reporting/mod.rs +++ b/kuyaseg-controller/src/reporting/mod.rs @@ -4,7 +4,6 @@ use std::time::Duration; use anyhow::Context; use confique::Config; -use opentelemetry::TraceId; use tokio::sync::broadcast::{ self, Sender, @@ -12,27 +11,19 @@ use tokio::sync::broadcast::{ use tokio::task::JoinSet; use tokio_util::sync::CancellationToken; -use crate::reporting::k8s::K8sEvent; +use crate::reporting::k8s_event::K8sEvent; +use crate::reporting::otel::OtelObservation; use crate::reporting::prometheus::ProLabel; use crate::reporting::tracing_evt::{ TracingK8sEventLayer, TracingK8sEventLayerConfig, }; -pub mod k8s; +pub mod k8s_event; +pub mod otel; pub mod prometheus; pub mod tracing_evt; -pub fn span_trace_id() -> Option { - use opentelemetry::trace::TraceContextExt; - use tracing_opentelemetry::OpenTelemetrySpanExt; - - match tracing::Span::current().context().span().span_context().trace_id() { - TraceId::INVALID => None, - other => Some(other), - } -} - #[derive(Config)] pub struct ReportingConfig { #[config(env = "KUYASEG_REPORT_K8S")] @@ -41,16 +32,24 @@ pub struct ReportingConfig { #[config(env = "KUYASEG_REPORT_PROMETHEUS")] #[config(default = false)] pub prometheus: bool, + #[config(env = "KUYASEG_REPORT_OTEL")] + #[config(default = false)] + pub opentelemetry: bool, +} + +pub fn trace_id_str(trace_id: &opentelemetry::TraceId) -> Option { + match trace_id { + &opentelemetry::TraceId::INVALID => None, + x => Some(x.to_string()), + } } /// You should await on handle somewhere pub struct ReportingInit { pub tracing_layer: TracingK8sEventLayer, pub handle: JoinSet<()>, - pub pro_registry: Option, } -/// For opentelemetry TraceId to be present, tracing_opentelemetry::layer must also be set pub fn init(k8s_client: kube::Client, ct: CancellationToken) -> anyhow::Result { let config = ReportingConfig::builder() .env() @@ -62,18 +61,21 @@ pub fn init(k8s_client: kube::Client, ct: CancellationToken) -> anyhow::Result, ct: Cancella Reporter, }; - use crate::reporting::k8s::K8sEventRecorder; + use crate::reporting::k8s_event::K8sEventRecorder; const WARN_THRESHOLD: usize = 10; const MAX_CAPACITY: usize = 100; @@ -124,7 +126,7 @@ fn init_k8s_events(client: kube::Client, joinset: &mut JoinSet<()>, ct: Cancella } queue.push_back(ev); if queue.len() > WARN_THRESHOLD && !warned { - tracing::warn!(k13t.no_report=true, debug=?queue.len(), "K8s event queue exceeded threshold"); + tracing::warn!(no_event=true, queue_len=queue.len(), "K8s event queue exceeded threshold"); warned = true; } else if queue.len() <= WARN_THRESHOLD { warned = false; @@ -133,7 +135,7 @@ fn init_k8s_events(client: kube::Client, joinset: &mut JoinSet<()>, ct: Cancella rec_limit = 4; } Err(e) => { - tracing::error!(k13t.no_report=true, debug=?e, "Failed to receive k8s event from tracing"); + tracing::error!(no_event=true, debug=?e, "Failed to receive k8s event from tracing"); rx = rtx.subscribe(); // dumb recover strategy tokio::time::sleep(Duration::from_secs(3)).await; rec_limit -= 1; @@ -143,7 +145,7 @@ fn init_k8s_events(client: kube::Client, joinset: &mut JoinSet<()>, ct: Cancella } } tracing::error!( - k13t.no_report = true, + no_event = true, "K8s Event reporting from tracing stopped" ); }); @@ -151,10 +153,7 @@ fn init_k8s_events(client: kube::Client, joinset: &mut JoinSet<()>, ct: Cancella tx } -fn init_pro_metrics( - joinset: &mut JoinSet<()>, - ct: CancellationToken, -) -> (Sender>, prometheus_client::registry::Registry) { +fn init_pro_metrics(joinset: &mut JoinSet<()>, ct: CancellationToken) -> Sender> { use prometheus_client::registry::Registry; use crate::reporting::prometheus::ProMetrics; @@ -162,7 +161,10 @@ fn init_pro_metrics( let (tx, mut rx) = broadcast::channel::>(512); let metrics = ProMetrics::new(); + let registration_cancellation = CancellationToken::new(); + let rtx = tx.clone(); + let ctk = registration_cancellation.clone(); let promt = metrics.clone(); let consumer_ct = ct.clone(); joinset.spawn(async move { @@ -178,7 +180,7 @@ fn init_pro_metrics( rec_limit = 4; } Err(e) => { - tracing::error!(k13t.no_report=true, debug=?e, "Failed to receive prometheus measurement from tracing"); + tracing::error!(no_pro=true, debug=?e, "Failed to receive prometheus measurement from tracing"); rx = rtx.subscribe(); // dumb recover strategy; tokio::time::sleep(Duration::from_secs(3)).await; rec_limit -= 1; @@ -187,17 +189,82 @@ fn init_pro_metrics( } } } + ctk.cancel(); tracing::error!( - k13t.no_report=true, + no_pro = true, "Prometheus measurement from tracing stopped" ); }); - let registry = { + let ctk = ct.clone(); + joinset.spawn(async move { let mut registry = Registry::default(); - metrics.register(&mut registry); - registry - }; + loop { + tokio::select! { + _ = tokio::time::sleep(Duration::from_secs(10)) => { + metrics.register(&mut registry); + }, + _ = registration_cancellation.cancelled() => { + tracing::warn!(no_pro=true, "Stopped publishing prometheus metrics: consumer job cancelled"); + break; + } + _ = ctk.cancelled() => { + tracing::warn!(no_pro=true, "Stopped publishing prometheus metrics: shutdown"); + break; + } + } + } + }); - (tx, registry) + tx +} + +fn init_otel(joinset: &mut JoinSet<()>, ct: CancellationToken) -> anyhow::Result>> { + use crate::reporting::otel::OtelAggregator; + + let exporter = opentelemetry_otlp::LogExporter::builder() + .with_tonic() + .build() + .context("Failed to build OTLP log exporter")?; + + let provider = opentelemetry_sdk::logs::SdkLoggerProvider::builder() + .with_batch_exporter(exporter) + .build(); + + let aggregator = Arc::new(OtelAggregator::new(provider)); + let task_aggregator = aggregator.clone(); + + let (tx, mut rx) = broadcast::channel::>(512); + + let rtx = tx.clone(); + joinset.spawn(async move { + let mut rec_limit = 4; + while rec_limit > 0 { + tokio::select! { + biased; + _ = ct.cancelled() => break, + msg = rx.recv() => { + match msg { + Ok(obs) => { + task_aggregator.observe(&obs); + rec_limit = 4; + } + Err(e) => { + tracing::error!( + no_otel = true, + debug = ?e, + "Failed to receive otel observation from tracing" + ); + rx = rtx.subscribe(); + tokio::time::sleep(Duration::from_secs(3)).await; + rec_limit -= 1; + } + } + } + } + } + tracing::error!(no_otel = true, "OpenTelemetry reporting from tracing stopped"); + }); + + Ok(tx) } diff --git a/kuyaseg-controller/src/reporting/otel.rs b/kuyaseg-controller/src/reporting/otel.rs new file mode 100644 index 0000000..f850d4c --- /dev/null +++ b/kuyaseg-controller/src/reporting/otel.rs @@ -0,0 +1,106 @@ +use std::fmt::Debug; + +use opentelemetry::logs::{ + AnyValue, + LogRecord, + Logger, + LoggerProvider, + Severity, +}; +use opentelemetry_sdk::logs::SdkLoggerProvider; + +pub struct OtelLogFields { + pub severity: Severity, + pub message: String, + pub reason: String, + pub action: String, + pub k8s_name: String, + pub k8s_kind: String, + pub k8s_ns: Option, + pub k8s_api: Option, + pub k8s_uid: Option, + pub trace_id: Option, +} + +pub enum OtelObservation { + Log { fields: OtelLogFields }, + Metric {}, +} + +impl Debug for OtelObservation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let variant = match self { + Self::Log { .. } => "Log", + Self::Metric { .. } => "Metric", + }; + write!(f, "OtelObservation::{}", variant) + } +} + +pub struct OtelAggregator { + provider: SdkLoggerProvider, + logger: ::Logger, +} + +impl OtelAggregator { + pub fn new(provider: SdkLoggerProvider) -> Self { + let logger = provider.logger("kuyaseg-controller"); + Self { provider, logger } + } + + pub fn observe(&self, obs: &OtelObservation) { + match obs { + OtelObservation::Log { fields } => self.observe_log(fields), + OtelObservation::Metric {} => { /*Not implemented */ } + } + } + + pub fn force_flush(&self) { + if let Err(e) = self.provider.force_flush() { + tracing::warn!(error = ?e, "otel export force_flush failed"); + } + } + + fn observe_log(&self, fields: &OtelLogFields) { + let record = { + let mut record = self.logger.create_log_record(); + record.set_severity_number(fields.severity); + record.set_severity_text(fields.severity.name()); + record.set_body(AnyValue::from(fields.message.to_string())); + record.set_target("kuyaseg-controller"); + + record.add_attribute("reason", fields.reason.to_string()); + record.add_attribute("action", fields.action.to_string()); + record.add_attribute("k8s.resource.name", fields.k8s_name.to_string()); + record.add_attribute("k8s.resource.kind", fields.k8s_kind.to_string()); + + if let Some(ns) = fields.k8s_ns.as_ref() { + record.add_attribute("k8s.namespace.name", ns.to_string()); + } + if let Some(api) = fields.k8s_api.as_ref() { + record.add_attribute("k8s.resource.api_version", api.to_string()); + } + if let Some(uid) = fields.k8s_uid.as_ref() { + record.add_attribute("k8s.resource.uid", uid.to_string()); + } + if let Some(trace_id) = fields.trace_id.as_ref() { + record.add_attribute("trace_id", trace_id.to_string()); + } + + record + }; + + self.logger.emit(record); + } + + #[deprecated(note = "Not implemented")] + fn observe_metric(&self, _metric: ()) { + todo!("Metrics are not implemented and should not be used for now") + } +} + +impl Drop for OtelAggregator { + fn drop(&mut self) { + self.force_flush(); + } +} diff --git a/kuyaseg-controller/src/reporting/prometheus.rs b/kuyaseg-controller/src/reporting/prometheus.rs index dfcc3b1..4d0d7ba 100644 --- a/kuyaseg-controller/src/reporting/prometheus.rs +++ b/kuyaseg-controller/src/reporting/prometheus.rs @@ -4,6 +4,7 @@ use std::time::{ SystemTime, }; +use opentelemetry::trace::TraceContextExt; use prometheus_client::encoding::{ EncodeLabelSet, EncodeLabelValue, @@ -15,8 +16,9 @@ use prometheus_client::metrics::exemplar::{ }; use prometheus_client::metrics::family::Family; use prometheus_client::registry::Registry; +use tracing_opentelemetry::OpenTelemetrySpanExt; -use crate::reporting::span_trace_id; +use crate::reporting::trace_id_str; #[derive(Default, Clone)] pub struct ProMetrics { @@ -45,7 +47,6 @@ impl ProMetrics { } /// Start measuring reconcile duration. Registers on `TimedMeasurer` drop - // TODO: use this pub fn measure_reconcilation(&self) -> TimedMeasurer { self.reconcile_metrics.measure() } @@ -70,9 +71,8 @@ pub struct ProReconcileMetrics { impl ProReconcileMetrics { pub fn measure(&self) -> TimedMeasurer { self.runs.inc(); - let label = span_trace_id().map(|id| TraceLabel { - trace_id: id.to_string(), - }); + let trace_id = tracing::Span::current().context().span().span_context().trace_id(); + let label = trace_id_str(&trace_id).map(|trace_id| TraceLabel { trace_id }); TimedMeasurer { label, diff --git a/kuyaseg-controller/src/reporting/tracing_evt.rs b/kuyaseg-controller/src/reporting/tracing_evt.rs index f94b612..0e1c8fa 100644 --- a/kuyaseg-controller/src/reporting/tracing_evt.rs +++ b/kuyaseg-controller/src/reporting/tracing_evt.rs @@ -5,6 +5,7 @@ use kube::runtime::events::{ Event, EventType, }; +use opentelemetry::logs::Severity; use tokio::sync::broadcast::Sender; use tracing::field::Visit; use tracing::span::Attributes; @@ -13,23 +14,31 @@ use tracing::{ Id, Subscriber, }; +use tracing_opentelemetry::OtelData; use tracing_subscriber::Layer; use tracing_subscriber::layer::Context; use tracing_subscriber::registry::LookupSpan; -use crate::reporting::k8s::K8sEvent; +use crate::reporting::k8s_event::K8sEvent; +use crate::reporting::otel::{ + OtelLogFields, + OtelObservation, +}; use crate::reporting::prometheus::{ ProK8sExemplar, ProK8sLabel, ProK8sSeverity, ProLabel, }; +use crate::reporting::trace_id_str; pub struct TracingK8sEventLayerConfig { /// Set to enable k8s events from tracing pub k8s_events: Option>>, /// set to enable prometheus metrics from tracing pub prometheus: Option>>, + /// set to enable otel observations from tracing + pub otel: Option>>, } /// Watches for tracing events, containing information necessary to construst k8s events and write prometheus metrics. @@ -37,11 +46,10 @@ pub struct TracingK8sEventLayerConfig { /// - k8s native events you must record yourself - thats what `tx` is for. /// - prometheus metrics are written automatically (if metrics set), but labels are also send with `tx`. /// - k8s events are rate-limited to 10 events/min -/// -/// Recognizes fields with `k13t` (KubernatesEvent) prefix. pub struct TracingK8sEventLayer { k8s_tx: Option>>, pro_tx: Option>>, + otel_tx: Option>>, } impl TracingK8sEventLayer { @@ -49,6 +57,7 @@ impl TracingK8sEventLayer { Self { k8s_tx: config.k8s_events, pro_tx: config.prometheus, + otel_tx: config.otel, } } } @@ -81,7 +90,6 @@ where return; } - let span = ctx.event_span(event); let scope = match ctx.event_scope(event) { Some(s) => s, None => { @@ -114,13 +122,11 @@ where return; } - let trace_id = span - .and_then(|s| { - s.extensions() - .get::() - .map(|ctx| ctx.trace_id()) - }) - .map(|id| id.to_string()); + // gather trace_id + let otel_trace_id = ctx + .event_span(event) + .and_then(|span| span.extensions().get::().and_then(|dat| dat.trace_id())); + let str_trace_id = otel_trace_id.as_ref().and_then(trace_id_str); // write k8s event let k8s_event_type = match *event.metadata().level() { @@ -139,7 +145,7 @@ where object: span_fields.to_object(), event, })) - .inspect_err(|e| tracing::error!(k13t.no_report=true, debug=?e, "Failed to issue event on k8s event from tracing")); + .inspect_err(|e| tracing::error!(debug=?e, "Failed to issue event on k8s event from tracing")); } // write/measure pro event @@ -157,13 +163,48 @@ where }; if event_fields.no_pro.is_none_or(|v| !v) && span_fields.no_pro.is_none_or(|v| !v) - && let Some(label) = try_create_pro_label(&span_fields, &event_fields, pro_severity, trace_id) + && let Some(label) = try_create_pro_label(&span_fields, &event_fields, pro_severity, str_trace_id.clone()) && let Some(tx) = self.pro_tx.as_ref() { _ = tx.send(Arc::new(label)).inspect_err( - |e| tracing::error!(k13t.no_report=true, debug=?e, "Failed to issue event on prometheus label measure from tracing"), + |e| tracing::error!(debug=?e, "Failed to issue event on prometheus label measure from tracing"), ); } + + // write otel event + let otel_severity = match *event.metadata().level() { + tracing::Level::ERROR => { + if event_fields.critical.is_some_and(|v| v) { + Severity::Fatal + } else { + Severity::Error + } + } + tracing::Level::WARN => Severity::Warn, + tracing::Level::INFO => Severity::Info, + _ => return, + }; + if event_fields.no_otel.is_none_or(|v| !v) + && span_fields.no_otel.is_none_or(|v| !v) + && let Some(tx) = self.otel_tx.as_ref() + { + _ = tx + .send(Arc::new(OtelObservation::Log { + fields: OtelLogFields { + severity: otel_severity, + message: event_fields.message.clone().unwrap_or_default(), + reason: event_fields.reason.clone().unwrap_or_default(), + action: event_fields.action.clone().unwrap_or_default(), + k8s_name: span_fields.name.clone().unwrap_or_default(), + k8s_kind: span_fields.kind.clone().unwrap_or_default(), + k8s_ns: span_fields.ns.clone(), + k8s_api: span_fields.api.clone(), + k8s_uid: span_fields.uid.clone(), + trace_id: str_trace_id.clone(), + }, + })) + .inspect_err(|e| tracing::error!(debug=?e, "Failed to issue otel observation from tracing")); + } } } @@ -248,22 +289,22 @@ impl SpanFields { impl Visit for SpanFields { fn record_str(&mut self, field: &tracing::field::Field, value: &str) { match field.name() { - "k13t.name" => self.name = Some(value.to_string()), - "k13t.ns" => self.ns = Some(value.to_string()), - "k13t.kind" => self.kind = Some(value.to_string()), - "k13t.api" => self.api = Some(value.to_string()), - "k13t.uid" => self.uid = Some(value.to_string()), - "k13t.version" => self.version = Some(value.to_string()), - "k13t.field_path" => self.field_path = Some(value.to_string()), + "k8s_name" => self.name = Some(value.to_string()), + "k8s_ns" => self.ns = Some(value.to_string()), + "k8s_kind" => self.kind = Some(value.to_string()), + "k8s_api" => self.api = Some(value.to_string()), + "k8s_uid" => self.uid = Some(value.to_string()), + "k8s_version" => self.version = Some(value.to_string()), + "k8s_field_path" => self.field_path = Some(value.to_string()), _ => {} } } fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { match field.name() { - "k13t.no_event" => self.no_event = Some(value), - "k13t.no_pro" => self.no_pro = Some(value), - "k13t.no_otel" => self.no_otel = Some(value), + "no_event" => self.no_event = Some(value), + "no_pro" => self.no_pro = Some(value), + "no_otel" => self.no_otel = Some(value), _ => {} } } @@ -273,14 +314,12 @@ impl Visit for SpanFields { #[derive(Default, Clone)] struct EventFields { - /// Setting this to true will prevent from emitting k8s events + /// Setting this to true will block entire span from emitting k8s events no_event: Option, - /// Setting this to true will prevent from emitting prometheus metric labels + /// Setting this to true will block entire span from emitting prometheus metric labels no_pro: Option, - /// Setting this to true will prevent from emitting opentelemetry logs, + /// Setting this to true will block entire span from emitting opentelemetry logs, no_otel: Option, - /// Setting this to true will prevent from emitting any pro/otel/k8s events - no_report: Option, /// Only for: /// - prometheus label: force event severity to "critical". Only triggers on `ERROR` tracing level /// - opentelemetry: make event severify to "Fatal". Only triggers on `ERROR` tracing level @@ -293,7 +332,7 @@ struct EventFields { impl EventFields { /// Scope independend custom field validation fn check_fields(&self) -> bool { - !self.no_report.is_some_and(|v| v) && self.reason.is_some() && self.action.is_some() + self.reason.is_some() && self.action.is_some() } /// Return `None` if hard-requirements on fields not met. @@ -312,20 +351,19 @@ impl EventFields { impl Visit for EventFields { fn record_str(&mut self, field: &tracing::field::Field, value: &str) { match field.name() { - "k13t.reason" => self.reason = Some(value.to_string()), - "k13t.action" => self.action = Some(value.to_string()), - "k13t.message" => self.message = Some(value.to_string()), + "reason" => self.reason = Some(value.to_string()), + "action" => self.action = Some(value.to_string()), + "message" => self.message = Some(value.to_string()), _ => {} } } fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { match field.name() { - "k13t.critical" => self.critical = Some(value), - "k13t.no_event" => self.no_event = Some(value), - "k13t.no_pro" => self.no_pro = Some(value), - "k13t.no_otel" => self.no_otel = Some(value), - "k13t.no_report" => self.no_report = Some(value), + "critical" => self.critical = Some(value), + "no_event" => self.no_event = Some(value), + "no_pro" => self.no_pro = Some(value), + "no_otel" => self.no_otel = Some(value), _ => {} } } diff --git a/kuyaseg-controller/src/resource/generated/mod.rs b/kuyaseg-controller/src/resource/generated/mod.rs deleted file mode 100644 index 899b535..0000000 --- a/kuyaseg-controller/src/resource/generated/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod secret; -pub mod ssh; diff --git a/kuyaseg-controller/src/resource/generated/secret/mod.rs b/kuyaseg-controller/src/resource/generated/secret/mod.rs deleted file mode 100644 index 32a5a9d..0000000 --- a/kuyaseg-controller/src/resource/generated/secret/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod v1alpha1; diff --git a/kuyaseg-controller/src/resource/generated/secret/v1alpha1/mod.rs b/kuyaseg-controller/src/resource/generated/secret/v1alpha1/mod.rs deleted file mode 100644 index 871bd5b..0000000 --- a/kuyaseg-controller/src/resource/generated/secret/v1alpha1/mod.rs +++ /dev/null @@ -1,354 +0,0 @@ -use std::collections::{ - BTreeMap, - HashMap, -}; - -use k8s_openapi::ByteString; -use schemars::JsonSchema; -use serde::{ - Deserialize, - Serialize, -}; -use serde_with::serde_as; -use zeroize::Zeroize; - -use crate::utils::{ - serializers, - zeroizers, -}; - -mod pseudorandom; -pub use pseudorandom::*; - -mod random; -pub use random::*; - -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] -pub struct SecretStatus { - pub key_tracker: HashMap, -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -#[serde_as] -pub struct KeyStatus { - /// Version is tracked separately, for change must set NE status - pub version: usize, - #[schemars(schema_with = "String::json_schema")] - #[serde_as(as = "serde_with::hex::Hex")] - pub spec_hash: blake3::Hash, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(test, derive(PartialEq))] -pub struct KeySpec { - /// Name of the key to put generated data into - pub key: String, - /// Tracks the key generation version within the same shape. - /// Increasing this number will force value regeneration on next reconcilation - pub version: usize, - /// Determines the shape of generated data - pub shape: KeyShape, -} - -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(test, derive(PartialEq))] -pub enum KeyShape { - /// Generate arbitrary bytes - Bytes { - /// Size of generated sequence in bytes. - /// - /// Defaults to 64 - #[serde(default = "KeyShape::default_bytes_size")] - size: usize, - }, - /// Generate string password - Password { - /// The length of password in characters. - /// - /// Defaults to 24 - #[serde(default = "KeyShape::default_pwd_length")] - length: usize, - /// The string of characters allowed to be present in password. - /// When dictionary length is > 4, each character in generated password is guaranteed to repeat - /// with period at least 3. - /// - /// Defaults to lowercase+uppercase alphanumeric and `-` - #[serde(default = "KeyShape::default_pwd_dict")] - dictionary: String, - }, -} - -impl KeyShape { - /// BLAKE3 hash, incorporating shape and according properties - pub fn stable_hash(&self) -> blake3::Hash { - let mut hasher = blake3::Hasher::new(); - match self { - Self::Bytes { size } => { - hasher.update(b"bytes"); - hasher.update(&size.to_le_bytes()); - } - Self::Password { length, dictionary } => { - hasher.update(b"password"); - hasher.update(&length.to_le_bytes()); - hasher.update(dictionary.as_bytes()); - } - } - hasher.finalize() - } - - pub fn default_bytes_size() -> usize { - 64 - } - pub fn default_pwd_length() -> usize { - 24 - } - pub fn default_pwd_dict() -> String { - String::from("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-") - } -} - -impl Zeroize for KeyShape { - #[inline] - fn zeroize(&mut self) { - match self { - Self::Bytes { size } => size.zeroize(), - Self::Password { length, dictionary } => { - length.zeroize(); - dictionary.zeroize(); - } - } - } -} - -impl Drop for KeyShape { - fn drop(&mut self) { - self.zeroize(); - } -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(test, derive(PartialEq))] -pub struct SecretTemplate { - /// Define created Secret resource metadata - #[serde(default)] - pub metadata: SecretMetadata, - /// Include static data into created Secret - #[serde(default)] - #[schemars(schema_with = "BTreeMap::::json_schema")] - pub data: BTreeMap, - /// Include static string data into created Secret - #[serde(default)] - #[serde(rename = "stringData")] - #[serde(with = "serializers::btreemap_to_bytestring_inplace")] - #[schemars(schema_with = "BTreeMap::::json_schema")] - pub string_data: BTreeMap, -} - -impl Zeroize for SecretTemplate { - #[inline] - fn zeroize(&mut self) { - zeroizers::zeroize_btree_of_bytestring(&mut self.data); - zeroizers::zeroize_btree_of_bytestring(&mut self.string_data); - } -} - -impl Drop for SecretTemplate { - fn drop(&mut self) { - self.zeroize(); - } -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[cfg_attr(test, derive(PartialEq))] -pub struct SecretMetadata { - #[serde(default)] - pub annotations: BTreeMap, - #[serde(default)] - pub labels: BTreeMap, -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn deser_template_metadata() { - // Full sample - let sample = " - annotations: - my.custom-annotation: value - labels: - some-custom.label: value - "; - let expect = SecretMetadata { - annotations: BTreeMap::from_iter([("my.custom-annotation".into(), "value".into())]), - labels: BTreeMap::from_iter([("some-custom.label".into(), "value".into())]), - }; - let parsed: SecretMetadata = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Full sample didn't match"); - - // Empty annotations & labels - let sample = " - annotations: {} - labels: {} - "; - let expect = SecretMetadata { - annotations: BTreeMap::new(), - labels: BTreeMap::new(), - }; - let parsed: SecretMetadata = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty annotations&labels didn't match"); - - // Empty - let sample = ""; - let expect = SecretMetadata::default(); - let parsed: SecretMetadata = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty didn't match"); - } - - #[test] - fn deser_template() { - // full sample - let sample = " - data: - key0: YWJjZGU= - stringData: - key1: fghij - metadata: - annotations: - my.custom-annotation: value - labels: - some-custom.label: value - "; - let expect = SecretTemplate { - data: BTreeMap::from_iter([("key0".into(), ByteString("abcde".to_string().into_bytes()))]), - string_data: BTreeMap::from_iter([("key1".into(), ByteString("fghij".to_string().into_bytes()))]), - metadata: SecretMetadata { - annotations: BTreeMap::from_iter([("my.custom-annotation".into(), "value".into())]), - labels: BTreeMap::from_iter([("some-custom.label".into(), "value".into())]), - }, - }; - let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Full sample didn't match"); - - // Empty metadata - let sample = " - data: - key0: YWJjZGU= - stringData: - key1: fghij - metadata: {} - "; - let expect = SecretTemplate { - data: BTreeMap::from_iter([("key0".into(), ByteString("abcde".to_string().into_bytes()))]), - string_data: BTreeMap::from_iter([("key1".into(), ByteString("fghij".to_string().into_bytes()))]), - metadata: SecretMetadata::default(), - }; - let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty metadata didn't match"); - - // No metadata - let sample = " - data: - key0: YWJjZGU= - stringData: - key1: fghij - "; - let expect = SecretTemplate { - data: BTreeMap::from_iter([("key0".into(), ByteString("abcde".to_string().into_bytes()))]), - string_data: BTreeMap::from_iter([("key1".into(), ByteString("fghij".to_string().into_bytes()))]), - metadata: SecretMetadata::default(), - }; - let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "No metadata didn't match"); - - // Empty data & stringdata - let sample = " - data: {} - stringData: {} - "; - let expect = SecretTemplate { - data: BTreeMap::new(), - string_data: BTreeMap::new(), - metadata: SecretMetadata::default(), - }; - let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty data&stringData didn't match"); - - // Empty - let sample = ""; - let expect = SecretTemplate::default(); - let parsed: SecretTemplate = serde_yaml::from_str(sample).unwrap(); - assert_eq!(parsed, expect, "Empty didn't match"); - } - - #[test] - fn deser_key_spec() { - // Full password - let sample = " - key: aabbcc - version: 12 - shape: !Password - length: 123 - dictionary: abcdefg_1 - "; - let expect = KeySpec { - key: "aabbcc".into(), - version: 12, - shape: KeyShape::Password { - length: 123, - dictionary: "abcdefg_1".into(), - }, - }; - let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Full password mismatch"); - - // Empty password - let sample = " - key: aabbcc - version: 12 - shape: !Password - "; - let expect = KeySpec { - key: "aabbcc".into(), - version: 12, - shape: KeyShape::Password { - length: KeyShape::default_pwd_length(), - dictionary: KeyShape::default_pwd_dict(), - }, - }; - let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Empty password mismatch"); - - // Full bytes - let sample = " - key: aabbcc - version: 12 - shape: !Bytes - size: 1234 - "; - let expect = KeySpec { - key: "aabbcc".into(), - version: 12, - shape: KeyShape::Bytes { size: 1234 }, - }; - let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Full bytes mismatch"); - - // Empty bytes - let sample = " - key: aabbcc - version: 12 - shape: !Bytes - "; - let expect = KeySpec { - key: "aabbcc".into(), - version: 12, - shape: KeyShape::Bytes { - size: KeyShape::default_bytes_size(), - }, - }; - let parsed: KeySpec = serde_yaml::from_str(sample).unwrap(); - assert_eq!(expect, parsed, "Empty bytes mismatch"); - } -} diff --git a/kuyaseg-controller/src/resource/generated/secret/v1alpha1/pseudorandom.rs b/kuyaseg-controller/src/resource/generated/secret/v1alpha1/pseudorandom.rs deleted file mode 100644 index e69de29..0000000 diff --git a/kuyaseg-controller/src/resource/generated/secret/v1alpha1/random.rs b/kuyaseg-controller/src/resource/generated/secret/v1alpha1/random.rs deleted file mode 100644 index e69de29..0000000 diff --git a/kuyaseg-controller/src/resource/generated/ssh/mod.rs b/kuyaseg-controller/src/resource/generated/ssh/mod.rs deleted file mode 100644 index 8b13789..0000000 --- a/kuyaseg-controller/src/resource/generated/ssh/mod.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/kuyaseg-controller/src/resource/mod.rs b/kuyaseg-controller/src/resource/mod.rs deleted file mode 100644 index 743aa41..0000000 --- a/kuyaseg-controller/src/resource/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod generated; diff --git a/kuyaseg-controller/src/utils/mod.rs b/kuyaseg-controller/src/utils/mod.rs index 659e158..0269ccf 100644 --- a/kuyaseg-controller/src/utils/mod.rs +++ b/kuyaseg-controller/src/utils/mod.rs @@ -1,2 +1 @@ -pub mod serializers; pub mod zeroizers; diff --git a/kuyaseg-controller/src/utils/serializers.rs b/kuyaseg-controller/src/utils/serializers.rs deleted file mode 100644 index 83468f5..0000000 --- a/kuyaseg-controller/src/utils/serializers.rs +++ /dev/null @@ -1,106 +0,0 @@ -/// in-place conversion of String into ByteString, verbatim, utf8-lossy -/// -/// e.g. `"abc" -> ByteString("abc".to_string().into_bytes())` -pub mod str_to_bytestring_inplace { - use k8s_openapi::ByteString; - use serde::de::Visitor; - use serde::{ - Deserializer, - Serializer, - }; - - pub fn serialize(value: &ByteString, se: S) -> Result - where - S: Serializer, - { - let value = String::from_utf8_lossy(&value.0); - se.serialize_str(&value) - } - - pub fn deserialize<'de, D>(de: D) -> Result - where - D: Deserializer<'de>, - { - de.deserialize_str(StringBytestringVisitor) - } - - struct StringBytestringVisitor; - - impl<'de> Visitor<'de> for StringBytestringVisitor { - type Value = ByteString; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "string") - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(ByteString(v.as_bytes().into())) - } - } -} - -/// in-place conversion of BTreeMap to BTreeMap, verbatim, utf8-lossy -/// -///e.g. `{"abc": "def"} -> {"abc": ByteString("def".to_string().into_bytes())}` -pub mod btreemap_to_bytestring_inplace { - use std::collections::BTreeMap; - use std::marker::PhantomData; - - use k8s_openapi::ByteString; - use serde::de::Visitor; - use serde::ser::SerializeMap; - use serde::{ - Deserialize, - Deserializer, - Serialize, - Serializer, - }; - - pub fn serialize(value: &BTreeMap, se: S) -> Result - where - S: Serializer, - K: Serialize, - { - let mut map = se.serialize_map(Some(value.len()))?; - value - .iter() - .try_for_each(|(k, v)| map.serialize_entry(k, &String::from_utf8_lossy(&v.0)))?; - map.end() - } - - pub fn deserialize<'de, K, D>(de: D) -> Result, D::Error> - where - D: Deserializer<'de>, - K: Ord + Deserialize<'de>, - { - de.deserialize_map(BTreeVisitor(PhantomData)) - } - - struct BTreeVisitor(PhantomData); - - impl<'de, K: Ord + Deserialize<'de>> Visitor<'de> for BTreeVisitor { - type Value = BTreeMap; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(formatter, "map {ty} -> String", ty = std::any::type_name::()) - } - - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - let mut btree = BTreeMap::new(); - while let Some((key, value)) = map.next_entry::()? { - if btree.contains_key(&key) { - return Err(serde::de::Error::custom("Duplicate key")); - } - btree.insert(key, ByteString(value.as_bytes().into())); - } - - Ok(btree) - } - } -} diff --git a/opencode.jsonc b/opencode.jsonc deleted file mode 100644 index ef7fc30..0000000 --- a/opencode.jsonc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "watcher": { - "ignore": [".opencode/node_modules", ".opencode/package.json", ".opencode/package-lock.json"], - }, - "permission": { - "bash": "ask", - "read": { - ".env": "ask", - ".envrc": "ask", - }, - }, -}