diff --git a/Cargo.lock b/Cargo.lock index 070aaac..8670e11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,56 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + [[package]] name = "anyhow" version = "1.0.98" @@ -40,9 +90,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -50,15 +100,9 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -67,9 +111,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "block-buffer" @@ -82,9 +126,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "bytes" @@ -94,9 +138,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.19" +version = "1.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" dependencies = [ "shlex", ] @@ -109,14 +153,54 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "num-traits", "serde", ] +[[package]] +name = "clap" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "codegen" version = "0.2.0" @@ -126,6 +210,22 @@ dependencies = [ "indexmap 1.9.3", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -138,9 +238,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -217,6 +317,15 @@ dependencies = [ "syn", ] +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.10.7" @@ -381,9 +490,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -392,9 +501,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -416,17 +525,17 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "headers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64 0.21.7", + "base64", "bytes", "headers-core", "http", @@ -444,6 +553,12 @@ dependencies = [ "http", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "home" version = "0.5.11" @@ -540,11 +655,10 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http", "hyper", "hyper-util", @@ -572,12 +686,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", @@ -613,7 +728,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", ] [[package]] @@ -651,7 +781,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c75b990324f09bef15e791606b7b7a296d02fc88a344f6eba9390970a870ad5" dependencies = [ - "base64 0.22.1", + "base64", "chrono", "schemars", "serde", @@ -677,7 +807,7 @@ version = "0.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fc2ed952042df20d15ac2fe9614d0ec14b6118eab89633985d4b36e688dccf1" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "chrono", "either", @@ -753,9 +883,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -766,6 +896,9 @@ name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "serde", +] [[package]] name = "memchr" @@ -790,13 +923,22 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" +dependencies = [ + "serde", ] [[package]] @@ -809,6 +951,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -818,6 +966,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "object" version = "0.36.7" @@ -833,6 +990,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "openssl-probe" version = "0.1.6" @@ -856,9 +1019,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -866,15 +1029,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -883,7 +1046,7 @@ version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ - "base64 0.22.1", + "base64", "serde", ] @@ -950,6 +1113,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -959,6 +1128,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -1009,14 +1188,14 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags", ] @@ -1064,7 +1243,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -1078,9 +1257,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustls" -version = "0.23.26" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "log", "once_cell", @@ -1127,15 +1306,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "ring", "rustls-pki-types", @@ -1222,7 +1404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -1317,9 +1499,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -1343,13 +1525,25 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.4" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ee1aca2bc74ef9589efa7ccaa0f3752751399940356209b3fd80c078149b5e" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] +[[package]] +name = "simple_logger" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c5dfa5e08767553704aa0ffd9d9794d527103c736aba9854773851fd7497eb" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + [[package]] name = "slab" version = "0.4.9" @@ -1367,9 +1561,9 @@ checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -1389,9 +1583,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -1434,6 +1628,39 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tokio" version = "1.45.1" @@ -1475,9 +1702,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -1505,11 +1732,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.2" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "base64 0.22.1", + "base64", "bitflags", "bytes", "http", @@ -1547,9 +1774,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" dependencies = [ "proc-macro2", "quote", @@ -1558,9 +1785,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -1597,6 +1824,20 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "type-reducer" +version = "0.1.0" +dependencies = [ + "clap", + "itertools", + "log", + "multimap", + "prettyplease", + "proc-macro2", + "simple_logger", + "syn", +] + [[package]] name = "typenum" version = "1.18.0" @@ -1627,13 +1868,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "js-sys", "rand", "wasm-bindgen", @@ -1755,13 +2002,22 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[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]] @@ -1770,7 +2026,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1779,28 +2050,46 @@ 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_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "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_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1813,24 +2102,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1848,18 +2161,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 4048149..503b8c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["gateway-api", "gateway-api/examples", "xtask"] +members = ["gateway-api", "gateway-api/examples", "xtask", "type-reducer"] resolver = "2" [workspace.package] diff --git a/gateway-api/Cargo.toml b/gateway-api/Cargo.toml index 81cef86..8f0fe96 100644 --- a/gateway-api/Cargo.toml +++ b/gateway-api/Cargo.toml @@ -38,5 +38,14 @@ uuid.workspace = true features = ["k8s-openapi/v1_32"] [features] -default = [] +default = ["processed"] +standard = [] experimental = [] +processed=[] + + +[lints.clippy] +derivable_impls="allow" +doc_lazy_continuation="allow" +tabs_in_doc_comments="allow" +empty_line_after_doc_comments="allow" \ No newline at end of file diff --git a/gateway-api/src/apis/mod.rs b/gateway-api/src/apis/mod.rs index 7651e9f..1eb5595 100644 --- a/gateway-api/src/apis/mod.rs +++ b/gateway-api/src/apis/mod.rs @@ -1,2 +1,4 @@ pub mod experimental; pub mod standard; + +pub mod processed; diff --git a/gateway-api/src/apis/processed/common_types.rs b/gateway-api/src/apis/processed/common_types.rs new file mode 100644 index 0000000..d1bf70d --- /dev/null +++ b/gateway-api/src/apis/processed/common_types.rs @@ -0,0 +1,224 @@ +// WARNING! generated file do not edit + +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; + pub use std::collections::BTreeMap; +} +use self::prelude::*; +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayAddress { + #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")] + pub r#type: Option, + pub value: String, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayInfrastructureParametersRef { + pub group: String, + pub kind: String, + pub name: String, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct Kind { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group: Option, + pub kind: String, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPHeader { + pub name: String, + pub value: String, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct RequestMirrorRef { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct RouteRef { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, + #[serde(default, skip_serializing_if = "Option::is_none", rename = "sectionName")] + pub section_name: Option, +} +/// HTTPRouteFilter defines processing steps that must be completed during the +/// request or response lifecycle. HTTPRouteFilters are meant as an extension +/// point to express processing that may be done in Gateway implementations. Some +/// examples include request or response modification, implementing +/// authentication strategies, rate-limiting, and traffic shaping. API +/// guarantee/conformance is defined based on the type of the filter. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum HTTPFilterType { + RequestHeaderModifier, + ResponseHeaderModifier, + RequestMirror, + RequestRedirect, + #[serde(rename = "URLRewrite")] + UrlRewrite, + ExtensionRef, +} +/// GRPCRouteFilter defines processing steps that must be completed during the +/// request or response lifecycle. GRPCRouteFilters are meant as an extension +/// point to express processing that may be done in Gateway implementations. Some +/// examples include request or response modification, implementing +/// authentication strategies, rate-limiting, and traffic shaping. API +/// guarantee/conformance is defined based on the type of the filter. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum GRPCFilterType { + ResponseHeaderModifier, + RequestHeaderModifier, + RequestMirror, + ExtensionRef, +} +/// RequestRedirect defines a schema for a filter that responds to the +/// request with an HTTP redirection. +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum RequestRedirectScheme { + #[serde(rename = "http")] + Http, + #[serde(rename = "https")] + Https, +} +/// GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request +/// headers. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum HeaderMatchType { + Exact, + RegularExpression, +} +/// Path defines parameters used to modify the path of the incoming request. +/// The modified path is then used to construct the `Location` header. When +/// empty, the request path is used as-is. +/// +/// Support: Extended +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum RequestOperationType { + ReplaceFullPath, + ReplacePrefixMatch, +} +/// RequestRedirect defines a schema for a filter that responds to the +/// request with an HTTP redirection. +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum RedirectStatusCode { + #[serde(rename = "301")] + r#_301, + #[serde(rename = "302")] + r#_302, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct RequestRedirectPath { + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "replaceFullPath" + )] + pub replace_full_path: Option, + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "replacePrefixMatch" + )] + pub replace_prefix_match: Option, + #[serde(rename = "type")] + pub r#type: RequestOperationType, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct MatchingHeaders { + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")] + pub r#type: Option, + pub value: String, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct RequestMirror { + #[serde(rename = "backendRef")] + pub backend_ref: RequestMirrorRef, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct ParentRouteStatus { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub conditions: Option>, + #[serde(rename = "controllerName")] + pub controller_name: String, + #[serde(rename = "parentRef")] + pub parent_ref: RouteRef, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HeaderModifier { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub add: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub remove: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub set: Option>, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRequestRedirect { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hostname: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub scheme: Option, + #[serde(default, skip_serializing_if = "Option::is_none", rename = "statusCode")] + pub status_code: Option, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct RouteStatus { + pub parents: Vec, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteUrlRewrite { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hostname: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, +} +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GRPCRouteFilter { + #[serde(default, skip_serializing_if = "Option::is_none", rename = "extensionRef")] + pub extension_ref: Option, + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestHeaderModifier" + )] + pub request_header_modifier: Option, + #[serde(default, skip_serializing_if = "Option::is_none", rename = "requestMirror")] + pub request_mirror: Option, + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "responseHeaderModifier" + )] + pub response_header_modifier: Option, + #[serde(rename = "type")] + pub r#type: GRPCFilterType, +} + + +// Next attempt + diff --git a/gateway-api/src/apis/processed/constants.rs b/gateway-api/src/apis/processed/constants.rs new file mode 100644 index 0000000..ef75c43 --- /dev/null +++ b/gateway-api/src/apis/processed/constants.rs @@ -0,0 +1,90 @@ +// WARNING! generated file do not edit + +#[derive(Debug, PartialEq, Eq)] +pub enum GatewayClassConditionType { + Accepted, +} +impl std::fmt::Display for GatewayClassConditionType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} +#[derive(Debug, PartialEq, Eq)] +pub enum GatewayClassConditionReason { + Accepted, + InvalidParameters, + Pending, + Unsupported, + Waiting, +} +impl std::fmt::Display for GatewayClassConditionReason { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} +#[derive(Debug, PartialEq, Eq)] +pub enum GatewayConditionType { + Programmed, + Accepted, + Ready, +} +impl std::fmt::Display for GatewayConditionType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} +#[derive(Debug, PartialEq, Eq)] +pub enum GatewayConditionReason { + Programmed, + Invalid, + NoResources, + AddressNotAssigned, + AddressNotUsable, + Accepted, + ListenersNotValid, + Pending, + UnsupportedAddress, + InvalidParameters, + Ready, + ListenersNotReady, +} +impl std::fmt::Display for GatewayConditionReason { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} +#[derive(Debug, PartialEq, Eq)] +pub enum ListenerConditionType { + Conflicted, + Accepted, + ResolvedRefs, + Programmed, + Ready, +} +impl std::fmt::Display for ListenerConditionType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} +#[derive(Debug, PartialEq, Eq)] +pub enum ListenerConditionReason { + HostnameConflict, + ProtocolConflict, + NoConflicts, + Accepted, + PortUnavailable, + UnsupportedProtocol, + ResolvedRefs, + InvalidCertificateRef, + InvalidRouteKinds, + RefNotPermitted, + Programmed, + Invalid, + Pending, + Ready, +} +impl std::fmt::Display for ListenerConditionReason { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/gateway-api/src/apis/processed/enum_defaults.rs b/gateway-api/src/apis/processed/enum_defaults.rs new file mode 100644 index 0000000..5a40b4d --- /dev/null +++ b/gateway-api/src/apis/processed/enum_defaults.rs @@ -0,0 +1,20 @@ +use super::common_types::*; +// WARNING: generated file - manual changes will be overriden + +impl Default for GRPCFilterType { + fn default() -> Self { + GRPCFilterType::RequestHeaderModifier + } +} + +impl Default for HTTPFilterType { + fn default() -> Self { + HTTPFilterType::RequestHeaderModifier + } +} + +impl Default for RequestOperationType { + fn default() -> Self { + RequestOperationType::ReplaceFullPath + } +} diff --git a/gateway-api/src/apis/processed/gatewayclasses.rs b/gateway-api/src/apis/processed/gatewayclasses.rs new file mode 100644 index 0000000..7efa74c --- /dev/null +++ b/gateway-api/src/apis/processed/gatewayclasses.rs @@ -0,0 +1,111 @@ +// WARNING! generated file do not edit + +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; +} +use self::prelude::*; +/// Spec defines the desired state of GatewayClass. +#[derive( + CustomResource, + Serialize, + Deserialize, + Clone, + Debug, + JsonSchema, + Default, + PartialEq +)] +#[kube( + group = "gateway.networking.k8s.io", + version = "v1", + kind = "GatewayClass", + plural = "gatewayclasses" +)] +#[kube(status = "GatewayClassStatus")] +#[kube(derive = "Default")] +#[kube(derive = "PartialEq")] +pub struct GatewayClassSpec { + /// ControllerName is the name of the controller that is managing Gateways of + /// this class. The value of this field MUST be a domain prefixed path. + /// + /// Example: "example.net/gateway-controller". + /// + /// This field is not mutable and cannot be empty. + /// + /// Support: Core + #[serde(rename = "controllerName")] + pub controller_name: String, + /// Description helps describe a GatewayClass with more details. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + /// ParametersRef is a reference to a resource that contains the configuration + /// parameters corresponding to the GatewayClass. This is optional if the + /// controller does not require any additional configuration. + /// + /// ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, + /// or an implementation-specific custom resource. The resource can be + /// cluster-scoped or namespace-scoped. + /// + /// If the referent cannot be found, refers to an unsupported kind, or when + /// the data within that resource is malformed, the GatewayClass SHOULD be + /// rejected with the "Accepted" status condition set to "False" and an + /// "InvalidParameters" reason. + /// + /// A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, + /// the merging behavior is implementation specific. + /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. + /// + /// Support: Implementation-specific + #[serde(default, skip_serializing_if = "Option::is_none", rename = "parametersRef")] + pub parameters_ref: Option, +} +/// ParametersRef is a reference to a resource that contains the configuration +/// parameters corresponding to the GatewayClass. This is optional if the +/// controller does not require any additional configuration. +/// +/// ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, +/// or an implementation-specific custom resource. The resource can be +/// cluster-scoped or namespace-scoped. +/// +/// If the referent cannot be found, refers to an unsupported kind, or when +/// the data within that resource is malformed, the GatewayClass SHOULD be +/// rejected with the "Accepted" status condition set to "False" and an +/// "InvalidParameters" reason. +/// +/// A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, +/// the merging behavior is implementation specific. +/// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. +/// +/// Support: Implementation-specific +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayClassParametersRef { + /// Group is the group of the referent. + pub group: String, + /// Kind is kind of the referent. + pub kind: String, + /// Name is the name of the referent. + pub name: String, + /// Namespace is the namespace of the referent. + /// This field is required when referring to a Namespace-scoped resource and + /// MUST be unset when referring to a Cluster-scoped resource. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} +/// Status defines the current state of GatewayClass. +/// +/// Implementations MUST populate status on all GatewayClass resources which +/// specify their controller name. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayClassStatus { + /// Conditions is the current status from the controller for + /// this GatewayClass. + /// + /// Controllers should prefer to publish conditions using values + /// of GatewayClassConditionType for the type of each Condition. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub conditions: Option>, +} diff --git a/gateway-api/src/apis/processed/gateways.rs b/gateway-api/src/apis/processed/gateways.rs new file mode 100644 index 0000000..7a47c1c --- /dev/null +++ b/gateway-api/src/apis/processed/gateways.rs @@ -0,0 +1,624 @@ +// WARNING! generated file do not edit + +use super::common_types::*; +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; + pub use std::collections::BTreeMap; +} +use self::prelude::*; +/// Spec defines the desired state of Gateway. +#[derive( + CustomResource, + Serialize, + Deserialize, + Clone, + Debug, + JsonSchema, + Default, + PartialEq +)] +#[kube( + group = "gateway.networking.k8s.io", + version = "v1", + kind = "Gateway", + plural = "gateways" +)] +#[kube(namespaced)] +#[kube(status = "GatewayStatus")] +#[kube(derive = "Default")] +#[kube(derive = "PartialEq")] +pub struct GatewaySpec { + /// Addresses requested for this Gateway. This is optional and behavior can + /// depend on the implementation. If a value is set in the spec and the + /// requested address is invalid or unavailable, the implementation MUST + /// indicate this in the associated entry in GatewayStatus.Addresses. + /// + /// The Addresses field represents a request for the address(es) on the + /// "outside of the Gateway", that traffic bound for this Gateway will use. + /// This could be the IP address or hostname of an external load balancer or + /// other networking infrastructure, or some other address that traffic will + /// be sent to. + /// + /// If no Addresses are specified, the implementation MAY schedule the + /// Gateway in an implementation-specific manner, assigning an appropriate + /// set of Addresses. + /// + /// The implementation MUST bind all Listeners to every GatewayAddress that + /// it assigns to the Gateway and add a corresponding entry in + /// GatewayStatus.Addresses. + /// + /// Support: Extended + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none")] + pub addresses: Option>, + /// GatewayClassName used for this Gateway. This is the name of a + /// GatewayClass resource. + #[serde(rename = "gatewayClassName")] + pub gateway_class_name: String, + /// Infrastructure defines infrastructure level attributes about this Gateway instance. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none")] + pub infrastructure: Option, + /// Listeners associated with this Gateway. Listeners define + /// logical endpoints that are bound on this Gateway's addresses. + /// At least one Listener MUST be specified. + /// + /// Each Listener in a set of Listeners (for example, in a single Gateway) + /// MUST be _distinct_, in that a traffic flow MUST be able to be assigned to + /// exactly one listener. (This section uses "set of Listeners" rather than + /// "Listeners in a single Gateway" because implementations MAY merge configuration + /// from multiple Gateways onto a single data plane, and these rules _also_ + /// apply in that case). + /// + /// Practically, this means that each listener in a set MUST have a unique + /// combination of Port, Protocol, and, if supported by the protocol, Hostname. + /// + /// Some combinations of port, protocol, and TLS settings are considered + /// Core support and MUST be supported by implementations based on their + /// targeted conformance profile: + /// + /// HTTP Profile + /// + /// 1. HTTPRoute, Port: 80, Protocol: HTTP + /// 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided + /// + /// TLS Profile + /// + /// 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough + /// + /// "Distinct" Listeners have the following property: + /// + /// The implementation can match inbound requests to a single distinct + /// Listener. When multiple Listeners share values for fields (for + /// example, two Listeners with the same Port value), the implementation + /// can match requests to only one of the Listeners using other + /// Listener fields. + /// + /// For example, the following Listener scenarios are distinct: + /// + /// 1. Multiple Listeners with the same Port that all use the "HTTP" + /// Protocol that all have unique Hostname values. + /// 2. Multiple Listeners with the same Port that use either the "HTTPS" or + /// "TLS" Protocol that all have unique Hostname values. + /// 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener + /// with the same Protocol has the same Port value. + /// + /// Some fields in the Listener struct have possible values that affect + /// whether the Listener is distinct. Hostname is particularly relevant + /// for HTTP or HTTPS protocols. + /// + /// When using the Hostname value to select between same-Port, same-Protocol + /// Listeners, the Hostname value must be different on each Listener for the + /// Listener to be distinct. + /// + /// When the Listeners are distinct based on Hostname, inbound request + /// hostnames MUST match from the most specific to least specific Hostname + /// values to choose the correct Listener and its associated set of Routes. + /// + /// Exact matches must be processed before wildcard matches, and wildcard + /// matches must be processed before fallback (empty Hostname value) + /// matches. For example, `"foo.example.com"` takes precedence over + /// `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. + /// + /// Additionally, if there are multiple wildcard entries, more specific + /// wildcard entries must be processed before less specific wildcard entries. + /// For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. + /// The precise definition here is that the higher the number of dots in the + /// hostname to the right of the wildcard character, the higher the precedence. + /// + /// The wildcard character will match any number of characters _and dots_ to + /// the left, however, so `"*.example.com"` will match both + /// `"foo.bar.example.com"` _and_ `"bar.example.com"`. + /// + /// If a set of Listeners contains Listeners that are not distinct, then those + /// Listeners are Conflicted, and the implementation MUST set the "Conflicted" + /// condition in the Listener Status to "True". + /// + /// Implementations MAY choose to accept a Gateway with some Conflicted + /// Listeners only if they only accept the partial Listener set that contains + /// no Conflicted Listeners. To put this another way, implementations may + /// accept a partial Listener set only if they throw out *all* the conflicting + /// Listeners. No picking one of the conflicting listeners as the winner. + /// This also means that the Gateway must have at least one non-conflicting + /// Listener in this case, otherwise it violates the requirement that at + /// least one Listener must be present. + /// + /// The implementation MUST set a "ListenersNotValid" condition on the + /// Gateway Status when the Gateway contains Conflicted Listeners whether or + /// not they accept the Gateway. That Condition SHOULD clearly + /// indicate in the Message which Listeners are conflicted, and which are + /// Accepted. Additionally, the Listener status for those listeners SHOULD + /// indicate which Listeners are conflicted and not Accepted. + /// + /// A Gateway's Listeners are considered "compatible" if: + /// + /// 1. They are distinct. + /// 2. The implementation can serve them in compliance with the Addresses + /// requirement that all Listeners are available on all assigned + /// addresses. + /// + /// Compatible combinations in Extended support are expected to vary across + /// implementations. A combination that is compatible for one implementation + /// may not be compatible for another. + /// + /// For example, an implementation that cannot serve both TCP and UDP listeners + /// on the same address, or cannot mix HTTPS and generic TLS listens on the same port + /// would not consider those cases compatible, even though they are distinct. + /// + /// Note that requests SHOULD match at most one Listener. For example, if + /// Listeners are defined for "foo.example.com" and "*.example.com", a + /// request to "foo.example.com" SHOULD only be routed using routes attached + /// to the "foo.example.com" Listener (and not the "*.example.com" Listener). + /// This concept is known as "Listener Isolation". Implementations that do + /// not support Listener Isolation MUST clearly document this. + /// + /// Implementations MAY merge separate Gateways onto a single set of + /// Addresses if all Listeners across all Gateways are compatible. + /// + /// Support: Core + pub listeners: Vec, +} +/// Infrastructure defines infrastructure level attributes about this Gateway instance. +/// +/// Support: Extended +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayInfrastructure { + /// Annotations that SHOULD be applied to any resources created in response to this Gateway. + /// + /// For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. + /// For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. + /// + /// An implementation may chose to add additional implementation-specific annotations as they see fit. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none")] + pub annotations: Option>, + /// Labels that SHOULD be applied to any resources created in response to this Gateway. + /// + /// For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. + /// For other implementations, this refers to any relevant (implementation specific) "labels" concepts. + /// + /// An implementation may chose to add additional implementation-specific labels as they see fit. + /// + /// If an implementation maps these labels to Pods, or any other resource that would need to be recreated when labels + /// change, it SHOULD clearly warn about this behavior in documentation. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none")] + pub labels: Option>, + /// ParametersRef is a reference to a resource that contains the configuration + /// parameters corresponding to the Gateway. This is optional if the + /// controller does not require any additional configuration. + /// + /// This follows the same semantics as GatewayClass's `parametersRef`, but on a per-Gateway basis + /// + /// The Gateway's GatewayClass may provide its own `parametersRef`. When both are specified, + /// the merging behavior is implementation specific. + /// It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. + /// + /// Support: Implementation-specific + #[serde(default, skip_serializing_if = "Option::is_none", rename = "parametersRef")] + pub parameters_ref: Option, +} +/// Listener embodies the concept of a logical endpoint where a Gateway accepts +/// network connections. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayListeners { + /// AllowedRoutes defines the types of routes that MAY be attached to a + /// Listener and the trusted namespaces where those Route resources MAY be + /// present. + /// + /// Although a client request may match multiple route rules, only one rule + /// may ultimately receive the request. Matching precedence MUST be + /// determined in order of the following criteria: + /// + /// * The most specific match as defined by the Route type. + /// * The oldest Route based on creation timestamp. For example, a Route with + /// a creation timestamp of "2020-09-08 01:02:03" is given precedence over + /// a Route with a creation timestamp of "2020-09-08 01:02:04". + /// * If everything else is equivalent, the Route appearing first in + /// alphabetical order (namespace/name) should be given precedence. For + /// example, foo/bar is given precedence over foo/baz. + /// + /// All valid rules within a Route attached to this Listener should be + /// implemented. Invalid Route rules can be ignored (sometimes that will mean + /// the full Route). If a Route rule transitions from valid to invalid, + /// support for that Route rule should be dropped to ensure consistency. For + /// example, even if a filter specified by a Route rule is invalid, the rest + /// of the rules within that Route should still be supported. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none", rename = "allowedRoutes")] + pub allowed_routes: Option, + /// Hostname specifies the virtual hostname to match for protocol types that + /// define this concept. When unspecified, all hostnames are matched. This + /// field is ignored for protocols that don't require hostname based + /// matching. + /// + /// Implementations MUST apply Hostname matching appropriately for each of + /// the following protocols: + /// + /// * TLS: The Listener Hostname MUST match the SNI. + /// * HTTP: The Listener Hostname MUST match the Host header of the request. + /// * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + /// protocol layers as described above. If an implementation does not + /// ensure that both the SNI and Host header match the Listener hostname, + /// it MUST clearly document that. + /// + /// For HTTPRoute and TLSRoute resources, there is an interaction with the + /// `spec.hostnames` array. When both listener and route specify hostnames, + /// there MUST be an intersection between the values for a Route to be + /// accepted. For more information, refer to the Route specific Hostnames + /// documentation. + /// + /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + /// as a suffix match. That means that a match for `*.example.com` would match + /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hostname: Option, + /// Name is the name of the Listener. This name MUST be unique within a + /// Gateway. + /// + /// Support: Core + pub name: String, + /// Port is the network port. Multiple listeners may use the + /// same port, subject to the Listener compatibility rules. + /// + /// Support: Core + pub port: i32, + /// Protocol specifies the network protocol this listener expects to receive. + /// + /// Support: Core + pub protocol: String, + /// TLS is the TLS configuration for the Listener. This field is required if + /// the Protocol field is "HTTPS" or "TLS". It is invalid to set this field + /// if the Protocol field is "HTTP", "TCP", or "UDP". + /// + /// The association of SNIs to Certificate defined in GatewayTLSConfig is + /// defined based on the Hostname field for this listener. + /// + /// The GatewayClass MUST use the longest matching SNI out of all + /// available certificates for any TLS handshake. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tls: Option, +} +/// AllowedRoutes defines the types of routes that MAY be attached to a +/// Listener and the trusted namespaces where those Route resources MAY be +/// present. +/// +/// Although a client request may match multiple route rules, only one rule +/// may ultimately receive the request. Matching precedence MUST be +/// determined in order of the following criteria: +/// +/// * The most specific match as defined by the Route type. +/// * The oldest Route based on creation timestamp. For example, a Route with +/// a creation timestamp of "2020-09-08 01:02:03" is given precedence over +/// a Route with a creation timestamp of "2020-09-08 01:02:04". +/// * If everything else is equivalent, the Route appearing first in +/// alphabetical order (namespace/name) should be given precedence. For +/// example, foo/bar is given precedence over foo/baz. +/// +/// All valid rules within a Route attached to this Listener should be +/// implemented. Invalid Route rules can be ignored (sometimes that will mean +/// the full Route). If a Route rule transitions from valid to invalid, +/// support for that Route rule should be dropped to ensure consistency. For +/// example, even if a filter specified by a Route rule is invalid, the rest +/// of the rules within that Route should still be supported. +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayListenersAllowedRoutes { + /// Kinds specifies the groups and kinds of Routes that are allowed to bind + /// to this Gateway Listener. When unspecified or empty, the kinds of Routes + /// selected are determined using the Listener protocol. + /// + /// A RouteGroupKind MUST correspond to kinds of Routes that are compatible + /// with the application protocol specified in the Listener's Protocol field. + /// If an implementation does not support or recognize this resource type, it + /// MUST set the "ResolvedRefs" condition to False for this Listener with the + /// "InvalidRouteKinds" reason. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kinds: Option>, + /// Namespaces indicates namespaces from which Routes may be attached to this + /// Listener. This is restricted to the namespace of this Gateway by default. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespaces: Option, +} +/// Namespaces indicates namespaces from which Routes may be attached to this +/// Listener. This is restricted to the namespace of this Gateway by default. +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayListenersAllowedRoutesNamespaces { + /// From indicates where Routes will be selected for this Gateway. Possible + /// values are: + /// + /// * All: Routes in all namespaces may be used by this Gateway. + /// * Selector: Routes in namespaces selected by the selector may be used by + /// this Gateway. + /// * Same: Only Routes in the same namespace may be used by this Gateway. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub from: Option, + /// Selector must be specified when From is set to "Selector". In that case, + /// only Routes in Namespaces matching this Selector will be selected by this + /// Gateway. This field is ignored for other values of "From". + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub selector: Option, +} +/// Namespaces indicates namespaces from which Routes may be attached to this +/// Listener. This is restricted to the namespace of this Gateway by default. +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum GatewayListenersAllowedRoutesNamespacesFrom { + All, + Selector, + Same, +} +/// Selector must be specified when From is set to "Selector". In that case, +/// only Routes in Namespaces matching this Selector will be selected by this +/// Gateway. This field is ignored for other values of "From". +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayListenersAllowedRoutesNamespacesSelector { + /// matchExpressions is a list of label selector requirements. The requirements are ANDed. + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "matchExpressions" + )] + pub match_expressions: Option< + Vec, + >, + /// matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + /// map is equivalent to an element of matchExpressions, whose key field is "key", the + /// operator is "In", and the values array contains only "value". The requirements are ANDed. + #[serde(default, skip_serializing_if = "Option::is_none", rename = "matchLabels")] + pub match_labels: Option>, +} +/// A label selector requirement is a selector that contains values, a key, and an operator that +/// relates the key and values. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayListenersAllowedRoutesNamespacesSelectorMatchExpressions { + /// key is the label key that the selector applies to. + pub key: String, + /// operator represents a key's relationship to a set of values. + /// Valid operators are In, NotIn, Exists and DoesNotExist. + pub operator: String, + /// values is an array of string values. If the operator is In or NotIn, + /// the values array must be non-empty. If the operator is Exists or DoesNotExist, + /// the values array must be empty. This array is replaced during a strategic + /// merge patch. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub values: Option>, +} +/// TLS is the TLS configuration for the Listener. This field is required if +/// the Protocol field is "HTTPS" or "TLS". It is invalid to set this field +/// if the Protocol field is "HTTP", "TCP", or "UDP". +/// +/// The association of SNIs to Certificate defined in GatewayTLSConfig is +/// defined based on the Hostname field for this listener. +/// +/// The GatewayClass MUST use the longest matching SNI out of all +/// available certificates for any TLS handshake. +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayListenersTls { + /// CertificateRefs contains a series of references to Kubernetes objects that + /// contains TLS certificates and private keys. These certificates are used to + /// establish a TLS handshake for requests that match the hostname of the + /// associated listener. + /// + /// A single CertificateRef to a Kubernetes Secret has "Core" support. + /// Implementations MAY choose to support attaching multiple certificates to + /// a Listener, but this behavior is implementation-specific. + /// + /// References to a resource in different namespace are invalid UNLESS there + /// is a ReferenceGrant in the target namespace that allows the certificate + /// to be attached. If a ReferenceGrant does not allow this reference, the + /// "ResolvedRefs" condition MUST be set to False for this listener with the + /// "RefNotPermitted" reason. + /// + /// This field is required to have at least one element when the mode is set + /// to "Terminate" (default) and is optional otherwise. + /// + /// CertificateRefs can reference to standard Kubernetes resources, i.e. + /// Secret, or implementation-specific custom resources. + /// + /// Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls + /// + /// Support: Implementation-specific (More than one reference or other resource types) + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "certificateRefs" + )] + pub certificate_refs: Option>, + /// Mode defines the TLS behavior for the TLS session initiated by the client. + /// There are two possible modes: + /// + /// - Terminate: The TLS session between the downstream client and the + /// Gateway is terminated at the Gateway. This mode requires certificates + /// to be specified in some way, such as populating the certificateRefs + /// field. + /// - Passthrough: The TLS session is NOT terminated by the Gateway. This + /// implies that the Gateway can't decipher the TLS stream except for + /// the ClientHello message of the TLS protocol. The certificateRefs field + /// is ignored in this mode. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mode: Option, + /// Options are a list of key/value pairs to enable extended TLS + /// configuration for each implementation. For example, configuring the + /// minimum TLS version or supported cipher suites. + /// + /// A set of common keys MAY be defined by the API in the future. To avoid + /// any ambiguity, implementation-specific definitions MUST use + /// domain-prefixed names, such as `example.com/my-custom-option`. + /// Un-prefixed names are reserved for key names defined by Gateway API. + /// + /// Support: Implementation-specific + #[serde(default, skip_serializing_if = "Option::is_none")] + pub options: Option>, +} +/// SecretObjectReference identifies an API object including its namespace, +/// defaulting to Secret. +/// +/// The API object must be valid in the cluster; the Group and Kind must +/// be registered in the cluster for this reference to be valid. +/// +/// References to objects with invalid Group and Kind are not valid, and must +/// be rejected by the implementation, with appropriate Conditions set +/// on the containing object. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayListenersTlsCertificateRefs { + /// Group is the group of the referent. For example, "gateway.networking.k8s.io". + /// When unspecified or empty string, core API group is inferred. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group: Option, + /// Kind is kind of the referent. For example "Secret". + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + /// Name is the name of the referent. + pub name: String, + /// Namespace is the namespace of the referenced object. When unspecified, the local + /// namespace is inferred. + /// + /// Note that when a namespace different than the local namespace is specified, + /// a ReferenceGrant object is required in the referent namespace to allow that + /// namespace's owner to accept the reference. See the ReferenceGrant + /// documentation for details. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} +/// TLS is the TLS configuration for the Listener. This field is required if +/// the Protocol field is "HTTPS" or "TLS". It is invalid to set this field +/// if the Protocol field is "HTTP", "TCP", or "UDP". +/// +/// The association of SNIs to Certificate defined in GatewayTLSConfig is +/// defined based on the Hostname field for this listener. +/// +/// The GatewayClass MUST use the longest matching SNI out of all +/// available certificates for any TLS handshake. +/// +/// Support: Core +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum GatewayListenersTlsMode { + Terminate, + Passthrough, +} +/// Status defines the current state of Gateway. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayStatus { + /// Addresses lists the network addresses that have been bound to the + /// Gateway. + /// + /// This list may differ from the addresses provided in the spec under some + /// conditions: + /// + /// * no addresses are specified, all addresses are dynamically assigned + /// * a combination of specified and dynamic addresses are assigned + /// * a specified address was unusable (e.g. already in use) + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none")] + pub addresses: Option>, + /// Conditions describe the current conditions of the Gateway. + /// + /// Implementations should prefer to express Gateway conditions + /// using the `GatewayConditionType` and `GatewayConditionReason` + /// constants so that operators and tools can converge on a common + /// vocabulary to describe Gateway state. + /// + /// Known condition types are: + /// + /// * "Accepted" + /// * "Programmed" + /// * "Ready" + #[serde(default, skip_serializing_if = "Option::is_none")] + pub conditions: Option>, + /// Listeners provide status for each unique listener port defined in the Spec. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub listeners: Option>, +} +/// ListenerStatus is the status associated with a Listener. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GatewayStatusListeners { + /// AttachedRoutes represents the total number of Routes that have been + /// successfully attached to this Listener. + /// + /// Successful attachment of a Route to a Listener is based solely on the + /// combination of the AllowedRoutes field on the corresponding Listener + /// and the Route's ParentRefs field. A Route is successfully attached to + /// a Listener when it is selected by the Listener's AllowedRoutes field + /// AND the Route has a valid ParentRef selecting the whole Gateway + /// resource or a specific Listener as a parent resource (more detail on + /// attachment semantics can be found in the documentation on the various + /// Route kinds ParentRefs fields). Listener or Route status does not impact + /// successful attachment, i.e. the AttachedRoutes field count MUST be set + /// for Listeners with condition Accepted: false and MUST count successfully + /// attached Routes that may themselves have Accepted: false conditions. + /// + /// Uses for this field include troubleshooting Route attachment and + /// measuring blast radius/impact of changes to a Listener. + #[serde(rename = "attachedRoutes")] + pub attached_routes: i32, + /// Conditions describe the current condition of this listener. + pub conditions: Vec, + /// Name is the name of the Listener that this status corresponds to. + pub name: String, + /// SupportedKinds is the list indicating the Kinds supported by this + /// listener. This MUST represent the kinds an implementation supports for + /// that Listener configuration. + /// + /// If kinds are specified in Spec that are not supported, they MUST NOT + /// appear in this list and an implementation MUST set the "ResolvedRefs" + /// condition to "False" with the "InvalidRouteKinds" reason. If both valid + /// and invalid Route kinds are specified, the implementation MUST + /// reference the valid Route kinds that have been specified. + #[serde(rename = "supportedKinds")] + pub supported_kinds: Vec, +} diff --git a/gateway-api/src/apis/processed/grpcroutes.rs b/gateway-api/src/apis/processed/grpcroutes.rs new file mode 100644 index 0000000..8b9a315 --- /dev/null +++ b/gateway-api/src/apis/processed/grpcroutes.rs @@ -0,0 +1,406 @@ +// WARNING! generated file do not edit + +use super::common_types::*; +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; +} +use self::prelude::*; +/// Spec defines the desired state of GRPCRoute. +#[derive( + CustomResource, + Serialize, + Deserialize, + Clone, + Debug, + JsonSchema, + Default, + PartialEq +)] +#[kube( + group = "gateway.networking.k8s.io", + version = "v1", + kind = "GRPCRoute", + plural = "grpcroutes" +)] +#[kube(namespaced)] +#[kube(status = "RouteStatus")] +#[kube(derive = "Default")] +#[kube(derive = "PartialEq")] +pub struct GRPCRouteSpec { + /// Hostnames defines a set of hostnames to match against the GRPC + /// Host header to select a GRPCRoute to process the request. This matches + /// the RFC 1123 definition of a hostname with 2 notable exceptions: + /// + /// 1. IPs are not allowed. + /// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + /// label MUST appear by itself as the first label. + /// + /// If a hostname is specified by both the Listener and GRPCRoute, there + /// MUST be at least one intersecting hostname for the GRPCRoute to be + /// attached to the Listener. For example: + /// + /// * A Listener with `test.example.com` as the hostname matches GRPCRoutes + /// that have either not specified any hostnames, or have specified at + /// least one of `test.example.com` or `*.example.com`. + /// * A Listener with `*.example.com` as the hostname matches GRPCRoutes + /// that have either not specified any hostnames or have specified at least + /// one hostname that matches the Listener hostname. For example, + /// `test.example.com` and `*.example.com` would both match. On the other + /// hand, `example.com` and `test.example.net` would not match. + /// + /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + /// as a suffix match. That means that a match for `*.example.com` would match + /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + /// + /// If both the Listener and GRPCRoute have specified hostnames, any + /// GRPCRoute hostnames that do not match the Listener hostname MUST be + /// ignored. For example, if a Listener specified `*.example.com`, and the + /// GRPCRoute specified `test.example.com` and `test.example.net`, + /// `test.example.net` MUST NOT be considered for a match. + /// + /// If both the Listener and GRPCRoute have specified hostnames, and none + /// match with the criteria above, then the GRPCRoute MUST NOT be accepted by + /// the implementation. The implementation MUST raise an 'Accepted' Condition + /// with a status of `False` in the corresponding RouteParentStatus. + /// + /// If a Route (A) of type HTTPRoute or GRPCRoute is attached to a + /// Listener and that listener already has another Route (B) of the other + /// type attached and the intersection of the hostnames of A and B is + /// non-empty, then the implementation MUST accept exactly one of these two + /// routes, determined by the following criteria, in order: + /// + /// * The oldest Route based on creation timestamp. + /// * The Route appearing first in alphabetical order by + /// "{namespace}/{name}". + /// + /// The rejected Route MUST raise an 'Accepted' condition with a status of + /// 'False' in the corresponding RouteParentStatus. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hostnames: Option>, + /// ParentRefs references the resources (usually Gateways) that a Route wants + /// to be attached to. Note that the referenced parent resource needs to + /// allow this for the attachment to be complete. For Gateways, that means + /// the Gateway needs to allow attachment from Routes of this kind and + /// namespace. For Services, that means the Service must either be in the same + /// namespace for a "producer" route, or the mesh implementation must support + /// and allow "consumer" routes for the referenced Service. ReferenceGrant is + /// not applicable for governing ParentRefs to Services - it is not possible to + /// create a "producer" route for a Service in a different namespace from the + /// Route. + /// + /// There are two kinds of parent resources with "Core" support: + /// + /// * Gateway (Gateway conformance profile) + /// * Service (Mesh conformance profile, ClusterIP Services only) + /// + /// This API may be extended in the future to support additional kinds of parent + /// resources. + /// + /// ParentRefs must be _distinct_. This means either that: + /// + /// * They select different objects. If this is the case, then parentRef + /// entries are distinct. In terms of fields, this means that the + /// multi-part key defined by `group`, `kind`, `namespace`, and `name` must + /// be unique across all parentRef entries in the Route. + /// * They do not select different objects, but for each optional field used, + /// each ParentRef that selects the same object must set the same set of + /// optional fields to different values. If one ParentRef sets a + /// combination of optional fields, all must set the same combination. + /// + /// Some examples: + /// + /// * If one ParentRef sets `sectionName`, all ParentRefs referencing the + /// same object must also set `sectionName`. + /// * If one ParentRef sets `port`, all ParentRefs referencing the same + /// object must also set `port`. + /// * If one ParentRef sets `sectionName` and `port`, all ParentRefs + /// referencing the same object must also set `sectionName` and `port`. + /// + /// It is possible to separately reference multiple distinct objects that may + /// be collapsed by an implementation. For example, some implementations may + /// choose to merge compatible Gateway Listeners together. If that is the + /// case, the list of routes attached to those resources should also be + /// merged. + /// + /// Note that for ParentRefs that cross namespace boundaries, there are specific + /// rules. Cross-namespace references are only valid if they are explicitly + /// allowed by something in the namespace they are referring to. For example, + /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a + /// generic way to enable other kinds of cross-namespace reference. + /// + /// + /// + /// + /// + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none", rename = "parentRefs")] + pub parent_refs: Option>, + /// Rules are a list of GRPC matchers, filters and actions. + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none")] + pub rules: Option>, +} +/// GRPCRouteRule defines the semantics for matching a gRPC request based on +/// conditions (matches), processing it (filters), and forwarding the request to +/// an API object (backendRefs). +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GRPCRouteRules { + /// BackendRefs defines the backend(s) where matching requests should be + /// sent. + /// + /// Failure behavior here depends on how many BackendRefs are specified and + /// how many are invalid. + /// + /// If *all* entries in BackendRefs are invalid, and there are also no filters + /// specified in this route rule, *all* traffic which matches this rule MUST + /// receive an `UNAVAILABLE` status. + /// + /// See the GRPCBackendRef definition for the rules about what makes a single + /// GRPCBackendRef invalid. + /// + /// When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for + /// requests that would have otherwise been routed to an invalid backend. If + /// multiple backends are specified, and some are invalid, the proportion of + /// requests that would otherwise have been routed to an invalid backend + /// MUST receive an `UNAVAILABLE` status. + /// + /// For example, if two backends are specified with equal weights, and one is + /// invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. + /// Implementations may choose how that 50 percent is determined. + /// + /// Support: Core for Kubernetes Service + /// + /// Support: Implementation-specific for any other resource + /// + /// Support for weight: Core + #[serde(default, skip_serializing_if = "Option::is_none", rename = "backendRefs")] + pub backend_refs: Option>, + /// Filters define the filters that are applied to requests that match + /// this rule. + /// + /// The effects of ordering of multiple behaviors are currently unspecified. + /// This can change in the future based on feedback during the alpha stage. + /// + /// Conformance-levels at this level are defined based on the type of filter: + /// + /// - ALL core filters MUST be supported by all implementations that support + /// GRPCRoute. + /// - Implementers are encouraged to support extended filters. + /// - Implementation-specific custom filters have no API guarantees across + /// implementations. + /// + /// Specifying the same filter multiple times is not supported unless explicitly + /// indicated in the filter. + /// + /// If an implementation can not support a combination of filters, it must clearly + /// document that limitation. In cases where incompatible or unsupported + /// filters are specified and cause the `Accepted` condition to be set to status + /// `False`, implementations may use the `IncompatibleFilters` reason to specify + /// this configuration error. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filters: Option>, + /// Matches define conditions used for matching the rule against incoming + /// gRPC requests. Each match is independent, i.e. this rule will be matched + /// if **any** one of the matches is satisfied. + /// + /// For example, take the following matches configuration: + /// + /// ```text + /// matches: + /// - method: + /// service: foo.bar + /// headers: + /// values: + /// version: 2 + /// - method: + /// service: foo.bar.v2 + /// ``` + /// + /// For a request to match against this rule, it MUST satisfy + /// EITHER of the two conditions: + /// + /// - service of foo.bar AND contains the header `version: 2` + /// - service of foo.bar.v2 + /// + /// See the documentation for GRPCRouteMatch on how to specify multiple + /// match conditions to be ANDed together. + /// + /// If no matches are specified, the implementation MUST match every gRPC request. + /// + /// Proxy or Load Balancer routing configuration generated from GRPCRoutes + /// MUST prioritize rules based on the following criteria, continuing on + /// ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. + /// Precedence MUST be given to the rule with the largest number of: + /// + /// * Characters in a matching non-wildcard hostname. + /// * Characters in a matching hostname. + /// * Characters in a matching service. + /// * Characters in a matching method. + /// * Header matches. + /// + /// If ties still exist across multiple Routes, matching precedence MUST be + /// determined in order of the following criteria, continuing on ties: + /// + /// * The oldest Route based on creation timestamp. + /// * The Route appearing first in alphabetical order by + /// "{namespace}/{name}". + /// + /// If ties still exist within the Route that has been given precedence, + /// matching precedence MUST be granted to the first matching rule meeting + /// the above criteria. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub matches: Option>, +} +/// GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. +/// +/// Note that when a namespace different than the local namespace is specified, a +/// ReferenceGrant object is required in the referent namespace to allow that +/// namespace's owner to accept the reference. See the ReferenceGrant +/// documentation for details. +/// +/// +/// +/// When the BackendRef points to a Kubernetes Service, implementations SHOULD +/// honor the appProtocol field if it is set for the target Service Port. +/// +/// Implementations supporting appProtocol SHOULD recognize the Kubernetes +/// Standard Application Protocols defined in KEP-3726. +/// +/// If a Service appProtocol isn't specified, an implementation MAY infer the +/// backend protocol through its own means. Implementations MAY infer the +/// protocol from the Route type referring to the backend Service. +/// +/// If a Route is not able to send traffic to the backend using the specified +/// protocol then the backend is considered invalid. Implementations MUST set the +/// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. +/// +/// +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GRPCRouteRulesBackendRefs { + /// Filters defined at this level MUST be executed if and only if the + /// request is being forwarded to the backend defined here. + /// + /// Support: Implementation-specific (For broader support of filters, use the + /// Filters field in GRPCRouteRule.) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filters: Option>, + /// Group is the group of the referent. For example, "gateway.networking.k8s.io". + /// When unspecified or empty string, core API group is inferred. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group: Option, + /// Kind is the Kubernetes resource kind of the referent. For example + /// "Service". + /// + /// Defaults to "Service" when not specified. + /// + /// ExternalName services can refer to CNAME DNS records that may live + /// outside of the cluster and as such are difficult to reason about in + /// terms of conformance. They also may not be safe to forward to (see + /// CVE-2021-25740 for more information). Implementations SHOULD NOT + /// support ExternalName Services. + /// + /// Support: Core (Services with a type other than ExternalName) + /// + /// Support: Implementation-specific (Services with type ExternalName) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + /// Name is the name of the referent. + pub name: String, + /// Namespace is the namespace of the backend. When unspecified, the local + /// namespace is inferred. + /// + /// Note that when a namespace different than the local namespace is specified, + /// a ReferenceGrant object is required in the referent namespace to allow that + /// namespace's owner to accept the reference. See the ReferenceGrant + /// documentation for details. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + /// Port specifies the destination port number to use for this resource. + /// Port is required when the referent is a Kubernetes Service. In this + /// case, the port number is the service port number, not the target port. + /// For other resources, destination port might be derived from the referent + /// resource or this field. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, + /// Weight specifies the proportion of requests forwarded to the referenced + /// backend. This is computed as weight/(sum of all weights in this + /// BackendRefs list). For non-zero values, there may be some epsilon from + /// the exact proportion defined here depending on the precision an + /// implementation supports. Weight is not a percentage and the sum of + /// weights does not need to equal 100. + /// + /// If only one backend is specified and it has a weight greater than 0, 100% + /// of the traffic is forwarded to that backend. If weight is set to 0, no + /// traffic should be forwarded for this entry. If unspecified, weight + /// defaults to 1. + /// + /// Support for this field varies based on the context where used. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub weight: Option, +} +/// GRPCRouteMatch defines the predicate used to match requests to a given +/// action. Multiple match types are ANDed together, i.e. the match will +/// evaluate to true only if all conditions are satisfied. +/// +/// For example, the match below will match a gRPC request only if its service +/// is `foo` AND it contains the `version: v1` header: +/// +/// ```text +/// matches: +/// - method: +/// type: Exact +/// service: "foo" +/// headers: +/// - name: "version" +/// value "v1" +/// +/// ``` +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GRPCRouteRulesMatches { + /// Headers specifies gRPC request header matchers. Multiple match values are + /// ANDed together, meaning, a request MUST match all the specified headers + /// to select the route. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub headers: Option>, + /// Method specifies a gRPC request service/method matcher. If this field is + /// not specified, all services and methods will match. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub method: Option, +} +/// Method specifies a gRPC request service/method matcher. If this field is +/// not specified, all services and methods will match. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct GRPCRouteRulesMatchesMethod { + /// Value of the method to match against. If left empty or omitted, will + /// match all services. + /// + /// At least one of Service and Method MUST be a non-empty string. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub method: Option, + /// Value of the service to match against. If left empty or omitted, will + /// match any service. + /// + /// At least one of Service and Method MUST be a non-empty string. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub service: Option, + /// Type specifies how to match against the service and/or method. + /// Support: Core (Exact with service and method specified) + /// + /// Support: Implementation-specific (Exact with method specified but no service specified) + /// + /// Support: Implementation-specific (RegularExpression) + #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")] + pub r#type: Option, +} diff --git a/gateway-api/src/apis/processed/httproutes.rs b/gateway-api/src/apis/processed/httproutes.rs new file mode 100644 index 0000000..c3eeac7 --- /dev/null +++ b/gateway-api/src/apis/processed/httproutes.rs @@ -0,0 +1,743 @@ +// WARNING! generated file do not edit + +use super::common_types::*; +#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; +} +use self::prelude::*; +/// Spec defines the desired state of HTTPRoute. +#[derive( + CustomResource, + Serialize, + Deserialize, + Clone, + Debug, + JsonSchema, + Default, + PartialEq +)] +#[kube( + group = "gateway.networking.k8s.io", + version = "v1", + kind = "HTTPRoute", + plural = "httproutes" +)] +#[kube(namespaced)] +#[kube(status = "RouteStatus")] +#[kube(derive = "Default")] +#[kube(derive = "PartialEq")] +pub struct HTTPRouteSpec { + /// Hostnames defines a set of hostnames that should match against the HTTP Host + /// header to select a HTTPRoute used to process the request. Implementations + /// MUST ignore any port value specified in the HTTP Host header while + /// performing a match and (absent of any applicable header modification + /// configuration) MUST forward this header unmodified to the backend. + /// + /// Valid values for Hostnames are determined by RFC 1123 definition of a + /// hostname with 2 notable exceptions: + /// + /// 1. IPs are not allowed. + /// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + /// label must appear by itself as the first label. + /// + /// If a hostname is specified by both the Listener and HTTPRoute, there + /// must be at least one intersecting hostname for the HTTPRoute to be + /// attached to the Listener. For example: + /// + /// * A Listener with `test.example.com` as the hostname matches HTTPRoutes + /// that have either not specified any hostnames, or have specified at + /// least one of `test.example.com` or `*.example.com`. + /// * A Listener with `*.example.com` as the hostname matches HTTPRoutes + /// that have either not specified any hostnames or have specified at least + /// one hostname that matches the Listener hostname. For example, + /// `*.example.com`, `test.example.com`, and `foo.test.example.com` would + /// all match. On the other hand, `example.com` and `test.example.net` would + /// not match. + /// + /// Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + /// as a suffix match. That means that a match for `*.example.com` would match + /// both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + /// + /// If both the Listener and HTTPRoute have specified hostnames, any + /// HTTPRoute hostnames that do not match the Listener hostname MUST be + /// ignored. For example, if a Listener specified `*.example.com`, and the + /// HTTPRoute specified `test.example.com` and `test.example.net`, + /// `test.example.net` must not be considered for a match. + /// + /// If both the Listener and HTTPRoute have specified hostnames, and none + /// match with the criteria above, then the HTTPRoute is not accepted. The + /// implementation must raise an 'Accepted' Condition with a status of + /// `False` in the corresponding RouteParentStatus. + /// + /// In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. + /// overlapping wildcard matching and exact matching hostnames), precedence must + /// be given to rules from the HTTPRoute with the largest number of: + /// + /// * Characters in a matching non-wildcard hostname. + /// * Characters in a matching hostname. + /// + /// If ties exist across multiple Routes, the matching precedence rules for + /// HTTPRouteMatches takes over. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hostnames: Option>, + /// ParentRefs references the resources (usually Gateways) that a Route wants + /// to be attached to. Note that the referenced parent resource needs to + /// allow this for the attachment to be complete. For Gateways, that means + /// the Gateway needs to allow attachment from Routes of this kind and + /// namespace. For Services, that means the Service must either be in the same + /// namespace for a "producer" route, or the mesh implementation must support + /// and allow "consumer" routes for the referenced Service. ReferenceGrant is + /// not applicable for governing ParentRefs to Services - it is not possible to + /// create a "producer" route for a Service in a different namespace from the + /// Route. + /// + /// There are two kinds of parent resources with "Core" support: + /// + /// * Gateway (Gateway conformance profile) + /// * Service (Mesh conformance profile, ClusterIP Services only) + /// + /// This API may be extended in the future to support additional kinds of parent + /// resources. + /// + /// ParentRefs must be _distinct_. This means either that: + /// + /// * They select different objects. If this is the case, then parentRef + /// entries are distinct. In terms of fields, this means that the + /// multi-part key defined by `group`, `kind`, `namespace`, and `name` must + /// be unique across all parentRef entries in the Route. + /// * They do not select different objects, but for each optional field used, + /// each ParentRef that selects the same object must set the same set of + /// optional fields to different values. If one ParentRef sets a + /// combination of optional fields, all must set the same combination. + /// + /// Some examples: + /// + /// * If one ParentRef sets `sectionName`, all ParentRefs referencing the + /// same object must also set `sectionName`. + /// * If one ParentRef sets `port`, all ParentRefs referencing the same + /// object must also set `port`. + /// * If one ParentRef sets `sectionName` and `port`, all ParentRefs + /// referencing the same object must also set `sectionName` and `port`. + /// + /// It is possible to separately reference multiple distinct objects that may + /// be collapsed by an implementation. For example, some implementations may + /// choose to merge compatible Gateway Listeners together. If that is the + /// case, the list of routes attached to those resources should also be + /// merged. + /// + /// Note that for ParentRefs that cross namespace boundaries, there are specific + /// rules. Cross-namespace references are only valid if they are explicitly + /// allowed by something in the namespace they are referring to. For example, + /// Gateway has the AllowedRoutes field, and ReferenceGrant provides a + /// generic way to enable other kinds of cross-namespace reference. + /// + /// + /// + /// + /// + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none", rename = "parentRefs")] + pub parent_refs: Option>, + /// Rules are a list of HTTP matchers, filters and actions. + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none")] + pub rules: Option>, +} +/// HTTPRouteRule defines semantics for matching an HTTP request based on +/// conditions (matches), processing it (filters), and forwarding the request to +/// an API object (backendRefs). +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRules { + /// BackendRefs defines the backend(s) where matching requests should be + /// sent. + /// + /// Failure behavior here depends on how many BackendRefs are specified and + /// how many are invalid. + /// + /// If *all* entries in BackendRefs are invalid, and there are also no filters + /// specified in this route rule, *all* traffic which matches this rule MUST + /// receive a 500 status code. + /// + /// See the HTTPBackendRef definition for the rules about what makes a single + /// HTTPBackendRef invalid. + /// + /// When a HTTPBackendRef is invalid, 500 status codes MUST be returned for + /// requests that would have otherwise been routed to an invalid backend. If + /// multiple backends are specified, and some are invalid, the proportion of + /// requests that would otherwise have been routed to an invalid backend + /// MUST receive a 500 status code. + /// + /// For example, if two backends are specified with equal weights, and one is + /// invalid, 50 percent of traffic must receive a 500. Implementations may + /// choose how that 50 percent is determined. + /// + /// When a HTTPBackendRef refers to a Service that has no ready endpoints, + /// implementations SHOULD return a 503 for requests to that backend instead. + /// If an implementation chooses to do this, all of the above rules for 500 responses + /// MUST also apply for responses that return a 503. + /// + /// Support: Core for Kubernetes Service + /// + /// Support: Extended for Kubernetes ServiceImport + /// + /// Support: Implementation-specific for any other resource + /// + /// Support for weight: Core + #[serde(default, skip_serializing_if = "Option::is_none", rename = "backendRefs")] + pub backend_refs: Option>, + /// Filters define the filters that are applied to requests that match + /// this rule. + /// + /// Wherever possible, implementations SHOULD implement filters in the order + /// they are specified. + /// + /// Implementations MAY choose to implement this ordering strictly, rejecting + /// any combination or order of filters that can not be supported. If implementations + /// choose a strict interpretation of filter ordering, they MUST clearly document + /// that behavior. + /// + /// To reject an invalid combination or order of filters, implementations SHOULD + /// consider the Route Rules with this configuration invalid. If all Route Rules + /// in a Route are invalid, the entire Route would be considered invalid. If only + /// a portion of Route Rules are invalid, implementations MUST set the + /// "PartiallyInvalid" condition for the Route. + /// + /// Conformance-levels at this level are defined based on the type of filter: + /// + /// - ALL core filters MUST be supported by all implementations. + /// - Implementers are encouraged to support extended filters. + /// - Implementation-specific custom filters have no API guarantees across + /// implementations. + /// + /// Specifying the same filter multiple times is not supported unless explicitly + /// indicated in the filter. + /// + /// All filters are expected to be compatible with each other except for the + /// URLRewrite and RequestRedirect filters, which may not be combined. If an + /// implementation can not support other combinations of filters, they must clearly + /// document that limitation. In cases where incompatible or unsupported + /// filters are specified and cause the `Accepted` condition to be set to status + /// `False`, implementations may use the `IncompatibleFilters` reason to specify + /// this configuration error. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filters: Option>, + /// Matches define conditions used for matching the rule against incoming + /// HTTP requests. Each match is independent, i.e. this rule will be matched + /// if **any** one of the matches is satisfied. + /// + /// For example, take the following matches configuration: + /// + /// ```text + /// matches: + /// - path: + /// value: "/foo" + /// headers: + /// - name: "version" + /// value: "v2" + /// - path: + /// value: "/v2/foo" + /// ``` + /// + /// For a request to match against this rule, a request must satisfy + /// EITHER of the two conditions: + /// + /// - path prefixed with `/foo` AND contains the header `version: v2` + /// - path prefix of `/v2/foo` + /// + /// See the documentation for HTTPRouteMatch on how to specify multiple + /// match conditions that should be ANDed together. + /// + /// If no matches are specified, the default is a prefix + /// path match on "/", which has the effect of matching every + /// HTTP request. + /// + /// Proxy or Load Balancer routing configuration generated from HTTPRoutes + /// MUST prioritize matches based on the following criteria, continuing on + /// ties. Across all rules specified on applicable Routes, precedence must be + /// given to the match having: + /// + /// * "Exact" path match. + /// * "Prefix" path match with largest number of characters. + /// * Method match. + /// * Largest number of header matches. + /// * Largest number of query param matches. + /// + /// Note: The precedence of RegularExpression path matches are implementation-specific. + /// + /// If ties still exist across multiple Routes, matching precedence MUST be + /// determined in order of the following criteria, continuing on ties: + /// + /// * The oldest Route based on creation timestamp. + /// * The Route appearing first in alphabetical order by + /// "{namespace}/{name}". + /// + /// If ties still exist within an HTTPRoute, matching precedence MUST be granted + /// to the FIRST matching rule (in list order) with a match meeting the above + /// criteria. + /// + /// When no rules matching a request have been successfully attached to the + /// parent a request is coming from, a HTTP 404 status code MUST be returned. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub matches: Option>, + /// Timeouts defines the timeouts that can be configured for an HTTP request. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none")] + pub timeouts: Option, +} +/// HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. +/// +/// Note that when a namespace different than the local namespace is specified, a +/// ReferenceGrant object is required in the referent namespace to allow that +/// namespace's owner to accept the reference. See the ReferenceGrant +/// documentation for details. +/// +/// +/// +/// When the BackendRef points to a Kubernetes Service, implementations SHOULD +/// honor the appProtocol field if it is set for the target Service Port. +/// +/// Implementations supporting appProtocol SHOULD recognize the Kubernetes +/// Standard Application Protocols defined in KEP-3726. +/// +/// If a Service appProtocol isn't specified, an implementation MAY infer the +/// backend protocol through its own means. Implementations MAY infer the +/// protocol from the Route type referring to the backend Service. +/// +/// If a Route is not able to send traffic to the backend using the specified +/// protocol then the backend is considered invalid. Implementations MUST set the +/// "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. +/// +/// +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRulesBackendRefs { + /// Filters defined at this level should be executed if and only if the + /// request is being forwarded to the backend defined here. + /// + /// Support: Implementation-specific (For broader support of filters, use the + /// Filters field in HTTPRouteRule.) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filters: Option>, + /// Group is the group of the referent. For example, "gateway.networking.k8s.io". + /// When unspecified or empty string, core API group is inferred. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub group: Option, + /// Kind is the Kubernetes resource kind of the referent. For example + /// "Service". + /// + /// Defaults to "Service" when not specified. + /// + /// ExternalName services can refer to CNAME DNS records that may live + /// outside of the cluster and as such are difficult to reason about in + /// terms of conformance. They also may not be safe to forward to (see + /// CVE-2021-25740 for more information). Implementations SHOULD NOT + /// support ExternalName Services. + /// + /// Support: Core (Services with a type other than ExternalName) + /// + /// Support: Implementation-specific (Services with type ExternalName) + #[serde(default, skip_serializing_if = "Option::is_none")] + pub kind: Option, + /// Name is the name of the referent. + pub name: String, + /// Namespace is the namespace of the backend. When unspecified, the local + /// namespace is inferred. + /// + /// Note that when a namespace different than the local namespace is specified, + /// a ReferenceGrant object is required in the referent namespace to allow that + /// namespace's owner to accept the reference. See the ReferenceGrant + /// documentation for details. + /// + /// Support: Core + #[serde(default, skip_serializing_if = "Option::is_none")] + pub namespace: Option, + /// Port specifies the destination port number to use for this resource. + /// Port is required when the referent is a Kubernetes Service. In this + /// case, the port number is the service port number, not the target port. + /// For other resources, destination port might be derived from the referent + /// resource or this field. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub port: Option, + /// Weight specifies the proportion of requests forwarded to the referenced + /// backend. This is computed as weight/(sum of all weights in this + /// BackendRefs list). For non-zero values, there may be some epsilon from + /// the exact proportion defined here depending on the precision an + /// implementation supports. Weight is not a percentage and the sum of + /// weights does not need to equal 100. + /// + /// If only one backend is specified and it has a weight greater than 0, 100% + /// of the traffic is forwarded to that backend. If weight is set to 0, no + /// traffic should be forwarded for this entry. If unspecified, weight + /// defaults to 1. + /// + /// Support for this field varies based on the context where used. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub weight: Option, +} +/// HTTPRouteFilter defines processing steps that must be completed during the +/// request or response lifecycle. HTTPRouteFilters are meant as an extension +/// point to express processing that may be done in Gateway implementations. Some +/// examples include request or response modification, implementing +/// authentication strategies, rate-limiting, and traffic shaping. API +/// guarantee/conformance is defined based on the type of the filter. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRulesBackendRefsFilters { + /// ExtensionRef is an optional, implementation-specific extension to the + /// "filter" behavior. For example, resource "myroutefilter" in group + /// "networking.example.net"). ExtensionRef MUST NOT be used for core and + /// extended filters. + /// + /// This filter can be used multiple times within the same rule. + /// + /// Support: Implementation-specific + #[serde(default, skip_serializing_if = "Option::is_none", rename = "extensionRef")] + pub extension_ref: Option, + /// RequestHeaderModifier defines a schema for a filter that modifies request + /// headers. + /// + /// Support: Core + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestHeaderModifier" + )] + pub request_header_modifier: Option, + /// RequestMirror defines a schema for a filter that mirrors requests. + /// Requests are sent to the specified destination, but responses from + /// that destination are ignored. + /// + /// This filter can be used multiple times within the same rule. Note that + /// not all implementations will be able to support mirroring to multiple + /// backends. + /// + /// Support: Extended + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none", rename = "requestMirror")] + pub request_mirror: Option, + /// RequestRedirect defines a schema for a filter that responds to the + /// request with an HTTP redirection. + /// + /// Support: Core + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestRedirect" + )] + pub request_redirect: Option, + /// ResponseHeaderModifier defines a schema for a filter that modifies response + /// headers. + /// + /// Support: Extended + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "responseHeaderModifier" + )] + pub response_header_modifier: Option, + /// Type identifies the type of filter to apply. As with other API fields, + /// types are classified into three conformance levels: + /// + /// - Core: Filter types and their corresponding configuration defined by + /// "Support: Core" in this package, e.g. "RequestHeaderModifier". All + /// implementations must support core filters. + /// + /// - Extended: Filter types and their corresponding configuration defined by + /// "Support: Extended" in this package, e.g. "RequestMirror". Implementers + /// are encouraged to support extended filters. + /// + /// - Implementation-specific: Filters that are defined and supported by + /// specific vendors. + /// In the future, filters showing convergence in behavior across multiple + /// implementations will be considered for inclusion in extended or core + /// conformance levels. Filter-specific configuration for such filters + /// is specified using the ExtensionRef field. `Type` should be set to + /// "ExtensionRef" for custom filters. + /// + /// Implementers are encouraged to define custom implementation types to + /// extend the core API with implementation-specific behavior. + /// + /// If a reference to a custom filter type cannot be resolved, the filter + /// MUST NOT be skipped. Instead, requests that would have been processed by + /// that filter MUST receive a HTTP error response. + /// + /// Note that values may be added to this enum, implementations + /// must ensure that unknown values will not cause a crash. + /// + /// Unknown values here must result in the implementation setting the + /// Accepted Condition for the Route to `status: False`, with a + /// Reason of `UnsupportedValue`. + #[serde(rename = "type")] + pub r#type: HTTPFilterType, + /// URLRewrite defines a schema for a filter that modifies a request during forwarding. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none", rename = "urlRewrite")] + pub url_rewrite: Option, +} +/// HTTPRouteFilter defines processing steps that must be completed during the +/// request or response lifecycle. HTTPRouteFilters are meant as an extension +/// point to express processing that may be done in Gateway implementations. Some +/// examples include request or response modification, implementing +/// authentication strategies, rate-limiting, and traffic shaping. API +/// guarantee/conformance is defined based on the type of the filter. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRulesFilters { + /// ExtensionRef is an optional, implementation-specific extension to the + /// "filter" behavior. For example, resource "myroutefilter" in group + /// "networking.example.net"). ExtensionRef MUST NOT be used for core and + /// extended filters. + /// + /// This filter can be used multiple times within the same rule. + /// + /// Support: Implementation-specific + #[serde(default, skip_serializing_if = "Option::is_none", rename = "extensionRef")] + pub extension_ref: Option, + /// RequestHeaderModifier defines a schema for a filter that modifies request + /// headers. + /// + /// Support: Core + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestHeaderModifier" + )] + pub request_header_modifier: Option, + /// RequestMirror defines a schema for a filter that mirrors requests. + /// Requests are sent to the specified destination, but responses from + /// that destination are ignored. + /// + /// This filter can be used multiple times within the same rule. Note that + /// not all implementations will be able to support mirroring to multiple + /// backends. + /// + /// Support: Extended + /// + /// + #[serde(default, skip_serializing_if = "Option::is_none", rename = "requestMirror")] + pub request_mirror: Option, + /// RequestRedirect defines a schema for a filter that responds to the + /// request with an HTTP redirection. + /// + /// Support: Core + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "requestRedirect" + )] + pub request_redirect: Option, + /// ResponseHeaderModifier defines a schema for a filter that modifies response + /// headers. + /// + /// Support: Extended + #[serde( + default, + skip_serializing_if = "Option::is_none", + rename = "responseHeaderModifier" + )] + pub response_header_modifier: Option, + /// Type identifies the type of filter to apply. As with other API fields, + /// types are classified into three conformance levels: + /// + /// - Core: Filter types and their corresponding configuration defined by + /// "Support: Core" in this package, e.g. "RequestHeaderModifier". All + /// implementations must support core filters. + /// + /// - Extended: Filter types and their corresponding configuration defined by + /// "Support: Extended" in this package, e.g. "RequestMirror". Implementers + /// are encouraged to support extended filters. + /// + /// - Implementation-specific: Filters that are defined and supported by + /// specific vendors. + /// In the future, filters showing convergence in behavior across multiple + /// implementations will be considered for inclusion in extended or core + /// conformance levels. Filter-specific configuration for such filters + /// is specified using the ExtensionRef field. `Type` should be set to + /// "ExtensionRef" for custom filters. + /// + /// Implementers are encouraged to define custom implementation types to + /// extend the core API with implementation-specific behavior. + /// + /// If a reference to a custom filter type cannot be resolved, the filter + /// MUST NOT be skipped. Instead, requests that would have been processed by + /// that filter MUST receive a HTTP error response. + /// + /// Note that values may be added to this enum, implementations + /// must ensure that unknown values will not cause a crash. + /// + /// Unknown values here must result in the implementation setting the + /// Accepted Condition for the Route to `status: False`, with a + /// Reason of `UnsupportedValue`. + #[serde(rename = "type")] + pub r#type: HTTPFilterType, + /// URLRewrite defines a schema for a filter that modifies a request during forwarding. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none", rename = "urlRewrite")] + pub url_rewrite: Option, +} +/// HTTPRouteMatch defines the predicate used to match requests to a given +/// action. Multiple match types are ANDed together, i.e. the match will +/// evaluate to true only if all conditions are satisfied. +/// +/// For example, the match below will match a HTTP request only if its path +/// starts with `/foo` AND it contains the `version: v1` header: +/// +/// ```text +/// match: +/// +/// path: +/// value: "/foo" +/// headers: +/// - name: "version" +/// value "v1" +/// +/// ``` +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRulesMatches { + /// Headers specifies HTTP request header matchers. Multiple match values are + /// ANDed together, meaning, a request must match all the specified headers + /// to select the route. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub headers: Option>, + /// Method specifies HTTP method matcher. + /// When specified, this route will be matched only if the request has the + /// specified method. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none")] + pub method: Option, + /// Path specifies a HTTP request path matcher. If this field is not + /// specified, a default prefix match on the "/" path is provided. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + /// QueryParams specifies HTTP query parameter matchers. Multiple match + /// values are ANDed together, meaning, a request must match all the + /// specified query parameters to select the route. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none", rename = "queryParams")] + pub query_params: Option>, +} +/// HTTPRouteMatch defines the predicate used to match requests to a given +/// action. Multiple match types are ANDed together, i.e. the match will +/// evaluate to true only if all conditions are satisfied. +/// +/// For example, the match below will match a HTTP request only if its path +/// starts with `/foo` AND it contains the `version: v1` header: +/// +/// ```text +/// match: +/// +/// path: +/// value: "/foo" +/// headers: +/// - name: "version" +/// value "v1" +/// +/// ``` +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum HTTPRouteRulesMatchesMethod { + #[serde(rename = "GET")] + Get, + #[serde(rename = "HEAD")] + Head, + #[serde(rename = "POST")] + Post, + #[serde(rename = "PUT")] + Put, + #[serde(rename = "DELETE")] + Delete, + #[serde(rename = "CONNECT")] + Connect, + #[serde(rename = "OPTIONS")] + Options, + #[serde(rename = "TRACE")] + Trace, + #[serde(rename = "PATCH")] + Patch, +} +/// Path specifies a HTTP request path matcher. If this field is not +/// specified, a default prefix match on the "/" path is provided. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRulesMatchesPath { + /// Type specifies how to match against the path Value. + /// + /// Support: Core (Exact, PathPrefix) + /// + /// Support: Implementation-specific (RegularExpression) + #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")] + pub r#type: Option, + /// Value of the HTTP path to match against. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub value: Option, +} +/// Path specifies a HTTP request path matcher. If this field is not +/// specified, a default prefix match on the "/" path is provided. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, PartialEq)] +pub enum HTTPRouteRulesMatchesPathType { + Exact, + PathPrefix, + RegularExpression, +} +/// Timeouts defines the timeouts that can be configured for an HTTP request. +/// +/// Support: Extended +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct HTTPRouteRulesTimeouts { + /// BackendRequest specifies a timeout for an individual request from the gateway + /// to a backend. This covers the time from when the request first starts being + /// sent from the gateway to when the full response has been received from the backend. + /// + /// Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + /// completely. Implementations that cannot completely disable the timeout MUST + /// instead interpret the zero duration as the longest possible value to which + /// the timeout can be set. + /// + /// An entire client HTTP transaction with a gateway, covered by the Request timeout, + /// may result in more than one call from the gateway to the destination backend, + /// for example, if automatic retries are supported. + /// + /// The value of BackendRequest must be a Gateway API Duration string as defined by + /// GEP-2257. When this field is unspecified, its behavior is implementation-specific; + /// when specified, the value of BackendRequest must be no more than the value of the + /// Request timeout (since the Request timeout encompasses the BackendRequest timeout). + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none", rename = "backendRequest")] + pub backend_request: Option, + /// Request specifies the maximum duration for a gateway to respond to an HTTP request. + /// If the gateway has not been able to respond before this deadline is met, the gateway + /// MUST return a timeout error. + /// + /// For example, setting the `rules.timeouts.request` field to the value `10s` in an + /// `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds + /// to complete. + /// + /// Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout + /// completely. Implementations that cannot completely disable the timeout MUST + /// instead interpret the zero duration as the longest possible value to which + /// the timeout can be set. + /// + /// This timeout is intended to cover as close to the whole request-response transaction + /// as possible although an implementation MAY choose to start the timeout after the entire + /// request stream has been received instead of immediately after the transaction is + /// initiated by the client. + /// + /// The value of Request is a Gateway API Duration string as defined by GEP-2257. When this + /// field is unspecified, request timeout behavior is implementation-specific. + /// + /// Support: Extended + #[serde(default, skip_serializing_if = "Option::is_none")] + pub request: Option, +} diff --git a/gateway-api/src/apis/processed/mod.rs b/gateway-api/src/apis/processed/mod.rs new file mode 100644 index 0000000..937b77b --- /dev/null +++ b/gateway-api/src/apis/processed/mod.rs @@ -0,0 +1,10 @@ +// WARNING! generated file do not edit + +pub mod common_types; +pub mod constants; +pub mod enum_defaults; +pub mod gatewayclasses; +pub mod gateways; +pub mod grpcroutes; +pub mod httproutes; +pub mod referencegrants; diff --git a/gateway-api/src/apis/processed/referencegrants.rs b/gateway-api/src/apis/processed/referencegrants.rs new file mode 100644 index 0000000..c785978 --- /dev/null +++ b/gateway-api/src/apis/processed/referencegrants.rs @@ -0,0 +1,96 @@ +// WARNING! generated file do not edit + +#[allow(unused_imports)] +mod prelude { + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; +} +use self::prelude::*; +/// Spec defines the desired state of ReferenceGrant. +#[derive( + CustomResource, + Serialize, + Deserialize, + Clone, + Debug, + JsonSchema, + Default, + PartialEq +)] +#[kube( + group = "gateway.networking.k8s.io", + version = "v1beta1", + kind = "ReferenceGrant", + plural = "referencegrants" +)] +#[kube(namespaced)] +#[kube(derive = "Default")] +#[kube(derive = "PartialEq")] +pub struct ReferenceGrantSpec { + /// From describes the trusted namespaces and kinds that can reference the + /// resources described in "To". Each entry in this list MUST be considered + /// to be an additional place that references can be valid from, or to put + /// this another way, entries MUST be combined using OR. + /// + /// Support: Core + pub from: Vec, + /// To describes the resources that may be referenced by the resources + /// described in "From". Each entry in this list MUST be considered to be an + /// additional place that references can be valid to, or to put this another + /// way, entries MUST be combined using OR. + /// + /// Support: Core + pub to: Vec, +} +/// ReferenceGrantFrom describes trusted namespaces and kinds. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct ReferenceGrantFrom { + /// Group is the group of the referent. + /// When empty, the Kubernetes core API group is inferred. + /// + /// Support: Core + pub group: String, + /// Kind is the kind of the referent. Although implementations may support + /// additional resources, the following types are part of the "Core" + /// support level for this field. + /// + /// When used to permit a SecretObjectReference: + /// + /// * Gateway + /// + /// When used to permit a BackendObjectReference: + /// + /// * GRPCRoute + /// * HTTPRoute + /// * TCPRoute + /// * TLSRoute + /// * UDPRoute + pub kind: String, + /// Namespace is the namespace of the referent. + /// + /// Support: Core + pub namespace: String, +} +/// ReferenceGrantTo describes what Kinds are allowed as targets of the +/// references. +#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, Default, PartialEq)] +pub struct ReferenceGrantTo { + /// Group is the group of the referent. + /// When empty, the Kubernetes core API group is inferred. + /// + /// Support: Core + pub group: String, + /// Kind is the kind of the referent. Although implementations may support + /// additional resources, the following types are part of the "Core" + /// support level for this field: + /// + /// * Secret when used to permit a SecretObjectReference + /// * Service when used to permit a BackendObjectReference + pub kind: String, + /// Name is the name of the referent. When unspecified, this policy + /// refers to all resources of the specified Group and Kind in the local + /// namespace. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub name: Option, +} diff --git a/gateway-api/src/lib.rs b/gateway-api/src/lib.rs index e3fc72e..f5a5e44 100644 --- a/gateway-api/src/lib.rs +++ b/gateway-api/src/lib.rs @@ -1,11 +1,16 @@ pub mod duration; pub use duration::Duration; pub mod apis; + +#[cfg(feature = "standard")] pub use apis::standard::*; #[cfg(feature = "experimental")] pub use apis::experimental; +#[cfg(feature = "processed")] +pub use apis::processed::*; + #[cfg(test)] mod tests { use std::process::Command; @@ -26,12 +31,12 @@ mod tests { use uuid::Uuid; use crate::{ - constants::{ + apis::standard::constants::{ GatewayConditionReason, GatewayConditionType, ListenerConditionReason, ListenerConditionType, }, - gatewayclasses::{GatewayClass, GatewayClassSpec}, - gateways::{ + apis::standard::gatewayclasses::{GatewayClass, GatewayClassSpec}, + apis::standard::gateways::{ Gateway, GatewaySpec, GatewayStatus, GatewayStatusAddresses, GatewayStatusListeners, }, }; diff --git a/type-reducer/Cargo.toml b/type-reducer/Cargo.toml new file mode 100644 index 0000000..a521d3a --- /dev/null +++ b/type-reducer/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "type-reducer" +version = "0.1.0" +edition = "2024" +authors = ["Dawid Nowak nowakd@gmail.com"] + +[dependencies] +syn = { version = "2", features = [ + "full", + "extra-traits", + "visit", + "visit-mut", + "proc-macro", +] } +proc-macro2 = "1" +prettyplease = "0.2" +itertools = "0.14" +multimap = "0.10" +clap = { version = "4.5", features = ["derive"] } +log = { version = "0.4", features = ["std", "serde"] } +simple_logger = "5" diff --git a/type-reducer/README.md b/type-reducer/README.md new file mode 100644 index 0000000..c9488c9 --- /dev/null +++ b/type-reducer/README.md @@ -0,0 +1,38 @@ +# ast_parser + +Usage : + +Will create common types and create APIs using those. Additionally will output the mapped types to "./mapped_types_to_names.txt" and "./mapped_names.txt" files. ./mapped_names.txt can be use as a source to provide some customer/more sensible substitutes for type names in step two. + +1. Step 1 - Reducing leaf types, all structs that only simple types or arrays of simple types such as String, u32, etc. +```bash +cargo run -- --apis-dir ../gateway-api/src/apis/standard --out-dir ../gateway-api/src/apis/processed --previous-pass-derived-type-names mapped_names.txt --current-pass-derived-type-prefix=Common +``` + +2. Step 2 (Optional) +Create a file with new type names. The file format is like this: +``` +# type_name->new_type_name where type_name is taken from ./mapped_names.txt + +CommonAddressesGateway->AddressGateway +CommonBackendFiltersMirrorRefRequestRouteRules->MirrorBackendRef +CommonExtensionFiltersRefRouteRules->FilterExtensionRef +CommonFiltersHeaderModifierRouteRules->HeaderModifier +CommonGatewayKindsListeners->ListenerRouteKinds +CommonParentRoute->ParentRef + +``` +3. Step 3 (Optional) + +Will read a file specified by --with-substitute-names and try to use those names as substitutions when modifying the APIs. + +```bash +cargo run -- --apis-dir ../gateway-api/src/apis/standard --out-dir ../gateway-api/src/apis/processed --with-substitute-names ./custom_mapped_names.txt +``` + +4. Step 4(Optional) - Reducing types further + +```bash +cargo run -- --apis-dir ../gateway-api/src/apis/processed --out-dir ../gateway-api/src/apis/processed --previous-pass-derived-type-names mapped_names.txt --current-pass-derived-type-prefix=Common +``` + diff --git a/type-reducer/customized_mapped_names.txt b/type-reducer/customized_mapped_names.txt new file mode 100644 index 0000000..6712948 --- /dev/null +++ b/type-reducer/customized_mapped_names.txt @@ -0,0 +1,84 @@ +GRPCRouteRulesBackendRefsFiltersType->GRPCFilterType +GRPCRouteRulesFiltersType->GRPCFilterType +GatewayAddresses->GatewayAddress +GatewayStatusAddresses->GatewayAddress +GRPCRouteRulesBackendRefsFiltersExtensionRef->GatewayInfrastructureParametersRef +GRPCRouteRulesFiltersExtensionRef->GatewayInfrastructureParametersRef +GatewayInfrastructureParametersRef->GatewayInfrastructureParametersRef +HTTPRouteRulesBackendRefsFiltersExtensionRef->GatewayInfrastructureParametersRef +HTTPRouteRulesFiltersExtensionRef->GatewayInfrastructureParametersRef +GatewayListenersAllowedRoutesKinds->Kind +GatewayStatusListenersSupportedKinds->Kind +GRPCRouteParentRefs->RouteRef +GRPCRouteStatusParentsParentRef->RouteRef +HTTPRouteParentRefs->RouteRef +HTTPRouteStatusParentsParentRef->RouteRef +GRPCRouteRulesBackendRefsFiltersExtensionRef->FilterExtensionRef +GRPCRouteRulesFiltersExtensionRef->FilterExtensionRef +HTTPRouteRulesBackendRefsFiltersExtensionRef->FilterExtensionRef +HTTPRouteRulesFiltersExtensionRef->FilterExtensionRef +GRPCRouteRulesBackendRefsFiltersRequestHeaderModifierAdd->HTTPHeader +GRPCRouteRulesBackendRefsFiltersRequestHeaderModifierSet->HTTPHeader +GRPCRouteRulesBackendRefsFiltersResponseHeaderModifierAdd->HTTPHeader +GRPCRouteRulesBackendRefsFiltersResponseHeaderModifierSet->HTTPHeader +GRPCRouteRulesFiltersRequestHeaderModifierAdd->HTTPHeader +GRPCRouteRulesFiltersRequestHeaderModifierSet->HTTPHeader +GRPCRouteRulesFiltersResponseHeaderModifierAdd->HTTPHeader +GRPCRouteRulesFiltersResponseHeaderModifierSet->HTTPHeader +HTTPRouteRulesBackendRefsFiltersRequestHeaderModifierAdd->HTTPHeader +HTTPRouteRulesBackendRefsFiltersRequestHeaderModifierSet->HTTPHeader +HTTPRouteRulesBackendRefsFiltersResponseHeaderModifierAdd->HTTPHeader +HTTPRouteRulesBackendRefsFiltersResponseHeaderModifierSet->HTTPHeader +HTTPRouteRulesFiltersRequestHeaderModifierAdd->HTTPHeader +HTTPRouteRulesFiltersRequestHeaderModifierSet->HTTPHeader +HTTPRouteRulesFiltersResponseHeaderModifierAdd->HTTPHeader +HTTPRouteRulesFiltersResponseHeaderModifierSet->HTTPHeader +GRPCRouteRulesBackendRefsFiltersRequestMirrorBackendRef->RequestMirrorRef +GRPCRouteRulesFiltersRequestMirrorBackendRef->RequestMirrorRef +HTTPRouteRulesBackendRefsFiltersRequestMirrorBackendRef->RequestMirrorRef +HTTPRouteRulesFiltersRequestMirrorBackendRef->RequestMirrorRef +HTTPRouteRulesBackendRefsFiltersRequestRedirectPathType->RequestOperationType +HTTPRouteRulesBackendRefsFiltersUrlRewritePathType->RequestOperationType +HTTPRouteRulesFiltersRequestRedirectPathType->RequestOperationType +HTTPRouteRulesFiltersUrlRewritePathType->RequestOperationType +HTTPRouteRulesBackendRefsFiltersRequestRedirectScheme->RequestRedirectScheme +HTTPRouteRulesFiltersRequestRedirectScheme->RequestRedirectScheme +HTTPRouteRulesBackendRefsFiltersRequestRedirectStatusCode->RedirectStatusCode +HTTPRouteRulesFiltersRequestRedirectStatusCode->RedirectStatusCode +HTTPRouteRulesBackendRefsFiltersType->HTTPFilterType +HTTPRouteRulesFiltersType->HTTPFilterType +GRPCRouteRulesMatchesHeadersType->HeaderMatchType +GRPCRouteRulesMatchesMethodType->HeaderMatchType +HTTPRouteRulesMatchesHeadersType->HeaderMatchType +HTTPRouteRulesMatchesQueryParamsType->HeaderMatchType +#### Pass 2 +GRPCRouteRulesBackendRefsFiltersRequestHeaderModifier->HeaderModifier +GRPCRouteRulesBackendRefsFiltersResponseHeaderModifier->HeaderModifier +GRPCRouteRulesFiltersRequestHeaderModifier->HeaderModifier +GRPCRouteRulesFiltersResponseHeaderModifier->HeaderModifier +HTTPRouteRulesBackendRefsFiltersRequestHeaderModifier->HeaderModifier +HTTPRouteRulesBackendRefsFiltersResponseHeaderModifier->HeaderModifier +HTTPRouteRulesFiltersRequestHeaderModifier->HeaderModifier +HTTPRouteRulesFiltersResponseHeaderModifier->HeaderModifier +GRPCRouteRulesMatchesHeaders->MatchingHeaders +HTTPRouteRulesMatchesHeaders->MatchingHeaders +HTTPRouteRulesMatchesQueryParams->MatchingHeaders +GRPCRouteStatusParents->ParentRouteStatus +HTTPRouteStatusParents->ParentRouteStatus +GRPCRouteRulesBackendRefsFiltersRequestMirror->RequestMirror +GRPCRouteRulesFiltersRequestMirror->RequestMirror +HTTPRouteRulesBackendRefsFiltersRequestMirror->RequestMirror +HTTPRouteRulesFiltersRequestMirror->RequestMirror +HTTPRouteRulesBackendRefsFiltersRequestRedirectPath->RequestRedirectPath +HTTPRouteRulesBackendRefsFiltersUrlRewritePath->RequestRedirectPath +HTTPRouteRulesFiltersRequestRedirectPath->RequestRedirectPath +HTTPRouteRulesFiltersUrlRewritePath->RequestRedirectPath +#### Pass 3 +GRPCRouteRulesBackendRefsFilters->GRPCRouteFilter +GRPCRouteRulesFilters->GRPCRouteFilter +HTTPRouteRulesBackendRefsFiltersRequestRedirect->HTTPRouteRequestRedirect +HTTPRouteRulesFiltersRequestRedirect->HTTPRouteRequestRedirect +HTTPRouteRulesBackendRefsFiltersUrlRewrite->HTTPRouteUrlRewrite +HTTPRouteRulesFiltersUrlRewrite->HTTPRouteUrlRewrite +GRPCRouteStatus->RouteStatus +HTTPRouteStatus->RouteStatus diff --git a/type-reducer/reduce_types.sh b/type-reducer/reduce_types.sh new file mode 100755 index 0000000..398c617 --- /dev/null +++ b/type-reducer/reduce_types.sh @@ -0,0 +1,9 @@ +#!/bin/bash +rm -rf ../gateway-api/src/apis/processed +mkdir ../gateway-api/src/apis/processed +#cargo run -- --apis-dir ../gateway-api/src/apis/standard --out-dir ../gateway-api/src/apis/processed +echo "\n\n PHASE 1\n\n" +cargo run -- --apis-dir ../gateway-api/src/apis/standard --out-dir ../gateway-api/src/apis/processed --current-pass-substitute-names customized_mapped_names.txt --previous-pass-derived-type-names reduced_types_pass_0.txt +echo "\n\n PHASE 2\n\n" +cargo run -- --apis-dir ../gateway-api/src/apis/processed --out-dir ../gateway-api/src/apis/processed --previous-pass-derived-type-names reduced_types_pass_1.txt --current-pass-substitute-names customized_mapped_names.txt + diff --git a/type-reducer/reduced_types_pass_0.txt b/type-reducer/reduced_types_pass_0.txt new file mode 100644 index 0000000..b2f52f2 --- /dev/null +++ b/type-reducer/reduced_types_pass_0.txt @@ -0,0 +1 @@ +Condition diff --git a/type-reducer/reduced_types_pass_1.txt b/type-reducer/reduced_types_pass_1.txt new file mode 100644 index 0000000..7fead51 --- /dev/null +++ b/type-reducer/reduced_types_pass_1.txt @@ -0,0 +1,14 @@ +Condition +GRPCFilterType +GatewayAddress +GatewayInfrastructureParametersRef +Kind +RouteRef +FilterExtensionRef +HTTPHeader +RequestMirrorRef +RequestOperationType +RequestRedirectScheme +RedirectStatusCode +FiltersType +HeaderMatchType diff --git a/type-reducer/reduced_types_pass_2.txt b/type-reducer/reduced_types_pass_2.txt new file mode 100644 index 0000000..f0d7012 --- /dev/null +++ b/type-reducer/reduced_types_pass_2.txt @@ -0,0 +1,20 @@ +Condition +GRPCFilterType +GatewayAddress +GatewayInfrastructureParametersRef +Kind +RouteRef +FilterExtensionRef +HTTPHeader +RequestMirrorRef +RequestOperationType +RequestRedirectScheme +RedirectStatusCode +FiltersType +HeaderMatchType +#### Pass 2 +MatchingHeaders +HeaderModifier +RequestMirror +RequestRedirectPath +ParentRouteStatus diff --git a/type-reducer/reduced_types_pass_3.txt b/type-reducer/reduced_types_pass_3.txt new file mode 100644 index 0000000..552f3bc --- /dev/null +++ b/type-reducer/reduced_types_pass_3.txt @@ -0,0 +1,25 @@ +Condition +GRPCFilterType +GatewayAddress +GatewayInfrastructureParametersRef +Kind +RouteRef +FilterExtensionRef +HTTPHeader +RequestMirrorRef +RequestOperationType +RequestRedirectScheme +RedirectStatusCode +FiltersType +HeaderMatchType +#### Pass 2 +MatchingHeaders +HeaderModifier +RequestMirror +RequestRedirectPath +ParentRouteStatus +#### Pass 3 +GRPCRouteFilter +HTTPRouteRequestRedirect +HTTPRouteUrlRewrite +RouteStatus diff --git a/type-reducer/src/lib.rs b/type-reducer/src/lib.rs new file mode 100644 index 0000000..99cecb3 --- /dev/null +++ b/type-reducer/src/lib.rs @@ -0,0 +1,466 @@ +use std::collections::BTreeMap; +use std::collections::BTreeSet; + +use itertools::Itertools; +use log::debug; +use log::info; +use multimap::MultiMap; +use proc_macro2::{Ident, Span}; +use std::fs::OpenOptions; +use std::io; +use std::io::BufRead; +use std::io::Seek; +use std::io::Write; +use std::path::Path; +use syn::Fields; +use syn::File; +use syn::Item; +use syn::ItemEnum; +use syn::ItemStruct; +use syn::Variant; +use syn::punctuated::Punctuated; +use syn::token::Comma; + +use syn::visit::Visit; + +use syn::visit_mut::VisitMut; + +mod visitors; +pub use visitors::*; + +const COMMON_TYPES_FILE_PREAMBLE: &str = "#[allow(unused_imports)] +mod prelude { + pub use k8s_openapi::apimachinery::pkg::apis::meta::v1::Condition; + pub use kube::CustomResource; + pub use schemars::JsonSchema; + pub use serde::{Deserialize, Serialize}; + pub use std::collections::BTreeMap; +} +use self::prelude::*;"; + +const COMMON_TYPES_USE_PREAMBLE: &str = "use super::common_types::*;"; +const GENERATED_PREAMBLE: &str = "// WARNING! generated file do not edit\n\n"; + +fn break_into_words(type_name: &str) -> Vec { + let mut words = vec![]; + let mut current_word = String::new(); + + for t in type_name.chars().tuple_windows() { + let (current, next, next_next) = t; + if current.is_uppercase() { + if next.is_uppercase() { + current_word.push(current); + if !next_next.is_uppercase() { + words.push(current_word); + current_word = String::new(); + } + } else { + current_word.push(current); + } + } else { + current_word.push(current); + if next.is_uppercase() { + words.push(current_word); + current_word = String::new(); + } + } + } + let len = type_name.len() - 2; + if len > 0 { + current_word += &type_name[len..]; + words.push(current_word); + } else { + words.push(type_name.to_owned()); + } + + words +} + +pub fn common_words(words_sets: &[Vec]) -> Vec { + let word_sets: Vec> = words_sets + .iter() + .cloned() + .map(BTreeSet::from_iter) + .collect(); + + let mut intersection = if let Some(first) = word_sets.first() { + first.clone() + } else { + return vec![]; + }; + + for word_set in word_sets { + intersection = intersection.intersection(&word_set).cloned().collect(); + } + Vec::from_iter(intersection) +} + +pub fn create_struct_type_name_substitute( + customized_names_from_file: &BTreeMap, + v: &[(Ident, ItemStruct)], +) -> String { + let words: Vec> = v + .iter() + .map(|v| break_into_words(&v.0.to_string())) + .collect(); + + let common_words = common_words(&words); + + let new_name = common_words.iter().cloned().collect::(); + + if let Some(customized_name) = customized_names_from_file.get(&new_name) { + customized_name.clone() + } else { + new_name + } +} + +pub fn read_substitute(customized_names_from_file: &BTreeMap, i: &Ident) -> String { + if let Some(customized_name) = customized_names_from_file.get(&i.to_string()) { + customized_name.clone() + } else { + i.to_string() + } +} + +pub fn create_enum_type_name_substitute( + customized_names_from_file: &BTreeMap, + v: &[(Ident, ItemEnum)], +) -> String { + let words: Vec> = v + .iter() + .map(|v| break_into_words(&v.0.to_string())) + .collect(); + + let common_words = common_words(&words); + + let new_name = common_words.iter().cloned().collect::(); + + if let Some(customized_name) = customized_names_from_file.get(&new_name) { + customized_name.clone() + } else { + new_name + } +} + +pub fn read_type_mappings_from_file( + mapped_names: &Path, +) -> Result, Box> { + let mut mapped_types = BTreeMap::new(); + let mapped_names_file = std::fs::File::open(mapped_names)?; + for line in io::BufReader::new(mapped_names_file) + .lines() + .map_while(Result::ok) + { + let mut parts = line.split("->"); + if let (Some(type_name), Some(new_type_name)) = (parts.next(), parts.next()) { + mapped_types.insert(type_name.to_owned(), new_type_name.to_owned()); + } + } + Ok(mapped_types) +} + +pub fn read_type_names_from_file( + mapped_names: &Path, +) -> Result, Box> { + let mapped_names_file = std::fs::File::open(mapped_names)?; + Ok(io::BufReader::new(mapped_names_file) + .lines() + .map_while(Result::ok) + .collect::>()) +} + +pub fn write_type_names_to_file( + mapped_types: &BTreeMap, +) -> Result<(), Box> { + let mut mapped_names_file = std::fs::File::create("./mapped_names.txt")?; + for v in mapped_types.values().sorted().dedup() { + mapped_names_file.write_all(format!("{v}\n").as_bytes())?; + } + + let mut mapped_names_file = std::fs::File::create("./mapped_types_to_names.txt")?; + for (k, v) in mapped_types + .iter() + .sorted_by(|(_, this), (_, other)| this.cmp(other)) + { + mapped_names_file.write_all(format!("{k}->{v}\n").as_bytes())?; + } + Ok(()) +} + +pub fn delete_replaced_structs(file: File, type_names: Vec) -> File { + let File { + shebang, + attrs, + items, + } = file; + + let items = items + .into_iter() + .filter(|i| match i { + // delete top level items with ident that was replaced + Item::Struct(item) => { + if type_names.contains(&item.ident.to_string()) { + debug!("Deleting {}", item.ident); + false + } else { + true + } + } + Item::Enum(item) => { + if type_names.contains(&item.ident.to_string()) { + debug!("Deleting {}", item.ident); + false + } else { + true + } + } + _ => true, + }) + .collect(); + + File { + shebang, + attrs, + items, + } +} + +pub struct FindSimilarTypesResult { + pub visitors: Vec<(String, File)>, + pub similar_structs: MultiMap, + pub similar_enums: MultiMap, (Ident, ItemEnum)>, +} + +pub fn find_similar_types( + visitors: Vec<(StructEnumVisitor<'_, '_>, File)>, +) -> FindSimilarTypesResult { + let mut similar_structs = MultiMap::new(); + let mut similar_enums = MultiMap::new(); + + let visitors: Vec<_> = visitors + .into_iter() + .map(|(mut visitor, file)| { + visitor.visit_file(&file); + visitor.structs.into_iter().for_each(|i| { + let mut fields = i.fields.clone(); + + fields.iter_mut().for_each(|f| { + f.attrs = f + .attrs + .clone() + .into_iter() + .filter(|a| { + a.meta.path().get_ident() != Some(&Ident::new("doc", Span::call_site())) + }) + .collect::>() + }); + + similar_structs.insert(fields, (i.ident.clone(), (*i).clone())); + }); + visitor.enums.into_iter().for_each(|i| { + similar_enums.insert(i.variants.clone(), (i.ident.clone(), (*i).clone())); + }); + (visitor.name, file) + }) + .collect(); + + FindSimilarTypesResult { + visitors, + similar_structs, + similar_enums, + } +} + +pub fn prune_replaced_structs( + renaming_visitor: &mut StructEnumRenamer, + visitors: Vec<(String, File)>, +) -> Vec<(String, String, bool)> { + visitors + .into_iter() + .map(|(name, mut f)| { + renaming_visitor.changed = false; + renaming_visitor.visit_file_mut(&mut f); + let new_file = + delete_replaced_structs(f, renaming_visitor.names.keys().cloned().collect()); + ( + name, + prettyplease::unparse(&new_file), + renaming_visitor.changed, + ) + }) + .collect() +} + +fn generate_file_preamble( + changed: bool, + content: &str, + output_path: &Path, + name: &str, +) -> Result> { + if changed { + info!("Writing changed file {name}"); + let mut out_file = std::fs::File::create(output_path.join(name))?; + if !content.starts_with(GENERATED_PREAMBLE) { + out_file.write_all(GENERATED_PREAMBLE.as_bytes())?; + } + + if !content.contains(COMMON_TYPES_USE_PREAMBLE) { + out_file.write_all(COMMON_TYPES_USE_PREAMBLE.as_bytes())?; + } + Ok(out_file) + } else { + info!("Writing NOT changed file {name}"); + let mut out_file = std::fs::File::create(output_path.join(name))?; + if !content.starts_with(GENERATED_PREAMBLE) { + out_file.write_all(GENERATED_PREAMBLE.as_bytes())?; + } + Ok(out_file) + } +} + +pub fn recreate_project_files( + out_dir: &str, + unparsed_files: Vec<(String, String, bool)>, + items: Vec, +) -> Result<(), Box> { + let common_types = prettyplease::unparse(&File { + shebang: None, + attrs: vec![], + items, + }); + + let output_path = std::path::Path::new(&out_dir); + if output_path.is_dir() && output_path.exists() { + info!("Writing changed file mod.rs"); + let mut mod_file = std::fs::File::create(output_path.join("mod.rs"))?; + mod_file.write_all(GENERATED_PREAMBLE.as_bytes())?; + + let mut mod_names = vec!["pub mod common_types;".to_owned()]; + + for (name, content, changed) in unparsed_files { + let mut out_file = generate_file_preamble(changed, &content, output_path, &name)?; + out_file.write_all(content.as_bytes())?; + mod_names.push(format!("pub mod {};", &name[..name.len() - 3])); + } + + for mod_name in mod_names.into_iter().sorted().dedup() { + mod_file.write_all((mod_name + "\n").as_bytes())?; + } + + let common_types_file_name = output_path.join("common_types.rs"); + + if common_types_file_name.exists() { + let mut common_out_file = OpenOptions::new() + .append(true) + .open(common_types_file_name)?; + + info!("Current position {}", common_out_file.stream_position()?); + + common_out_file.write_all("\n\n// Next attempt \n\n".as_bytes())?; + common_out_file.write_all(common_types.as_bytes())?; + } else { + let mut common_out_file = std::fs::File::create(common_types_file_name)?; + let common_types_file_content = + COMMON_TYPES_FILE_PREAMBLE.to_owned() + "\n\n\n" + &common_types; + common_out_file.write_all(common_types_file_content.as_bytes())?; + } + Ok(()) + } else { + Err("Make sure that output path is a folder and tha it exists".into()) + } +} + +pub fn create_common_type_struct(s: &ItemStruct, type_new_name: &str) -> ItemStruct { + let mut new_struct = s.clone(); + new_struct.attrs = s + .attrs + .iter() + .filter(|&a| a.meta.path().get_ident() != Some(&Ident::new("doc", Span::call_site()))) + .cloned() + .collect(); + new_struct.fields = s.fields.clone(); + new_struct.fields.iter_mut().for_each(|f| { + f.attrs = f + .attrs + .clone() + .into_iter() + .filter(|a| a.meta.path().get_ident() != Some(&Ident::new("doc", Span::call_site()))) + .collect::>() + }); + + new_struct.ident = Ident::new(type_new_name, Span::call_site()); + new_struct +} + +pub fn create_common_type_enum(s: &ItemEnum, type_new_name: &str) -> ItemEnum { + let mut new_enum = s.clone(); + + // new_enum.attrs = s + // .attrs + // .iter() + // .filter(|&a| a.meta.path().get_ident() != Some(&Ident::new("doc", Span::call_site()))) + // .cloned() + // .collect(); + // new_enum.attrs = s.attrs.clone(); + // let attributes: Vec<_> = new_enum + // .attrs + // .iter_mut() + // .filter(|a| { + // if let Ok(s) = a.meta.require_list() { + // if let Some(s) = s.path.segments.first() { + // s.ident == Ident::new("derive", Span::call_site()) + // } else { + // false + // } + // } else { + // false + // } + // }) + // .map(|a| { + // let mut new_attr = a.clone(); + // if let Ok(s) = a.meta.require_list() { + // let mut new_meta = s.clone(); + // let mut new_tokens = new_meta.tokens.clone(); + // //new_tokens.extend(TokenStream::from_str(", Default").unwrap()); + // new_meta.tokens = new_tokens; + // warn!("Enum tokens {:?}", new_meta.tokens.to_string()); + // new_attr.meta = Meta::List(new_meta); + // } + // new_attr + // }) + // .collect(); + + // new_enum.attrs = attributes; + // warn!("New enum = {:?}", new_enum); + + new_enum.ident = Ident::new(type_new_name, Span::call_site()); + new_enum +} + +#[cfg(test)] +mod tests { + use crate::break_into_words; + + #[test] + fn test_word_breaking() { + let expected_words = [ + "GRPC", "Route", "Rules", "Backend", "Refs", "Filters", "Request", "Mirror", "Backend", + "Ref", + ]; + let words = break_into_words("GRPCRouteRulesBackendRefsFiltersRequestMirrorBackendRef"); + assert_eq!(expected_words.to_vec(), words); + + let expected_words = [ + "GRPC", "Route", "Rules", "Backend", "Refs", "Filters", "Request", "HTTPS", "Mirror", + "Backend", "Ref", + ]; + let words = + break_into_words("GRPCRouteRulesBackendRefsFiltersRequestHTTPSMirrorBackendRef"); + assert_eq!(expected_words.to_vec(), words); + + let expected_words = ["f", "RP"]; + let words = break_into_words("fRP"); + assert_eq!(expected_words.to_vec(), words); + } +} diff --git a/type-reducer/src/main.rs b/type-reducer/src/main.rs new file mode 100644 index 0000000..e8c57ea --- /dev/null +++ b/type-reducer/src/main.rs @@ -0,0 +1,169 @@ +use clap::Parser; +use log::info; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use std::fs; +use std::path::PathBuf; +use syn::Item; +use type_reducer::*; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + #[arg(long)] + apis_dir: String, + + #[arg(long)] + out_dir: String, + + #[arg(long)] + current_pass_substitute_names: Option, + + #[arg(long)] + previous_pass_derived_type_names: Option, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_env().unwrap(); + let Args { + apis_dir, + out_dir, + current_pass_substitute_names, + previous_pass_derived_type_names, + } = Args::parse(); + + let Ok(_) = fs::exists(out_dir.clone()) else { + return Err("our dir doesn't exist".into()); + }; + + let current_pass_type_name_substitutes = + if let Some(mapped_names) = current_pass_substitute_names.as_ref() { + read_type_mappings_from_file(mapped_names.as_path())? + } else { + BTreeMap::new() + }; + + let previous_pass_derived_type_names = + if let Some(mapped_names) = previous_pass_derived_type_names.as_ref() { + read_type_names_from_file(mapped_names.as_path())? + } else { + BTreeSet::new() + }; + + let mut visitors = vec![]; + + for dir_entry in fs::read_dir(apis_dir).unwrap() { + let Ok(dir_entry) = dir_entry else { + continue; + }; + + if let Ok(name) = dir_entry.file_name().into_string() { + if name.ends_with(".rs") && name != "mod.rs" { + info!("Adding file {:?}", dir_entry.path()); + if let Ok(api_file) = fs::read_to_string(dir_entry.path()) { + if let Ok(syntaxt_file) = syn::parse_file(&api_file) { + let visitor = StructEnumVisitor { + name, + structs: Vec::new(), + enums: Vec::new(), + derived_type_names: &previous_pass_derived_type_names, + }; + visitors.push((visitor, syntaxt_file)); + } + } + } + } + } + + let FindSimilarTypesResult { + visitors, + similar_structs, + similar_enums, + } = find_similar_types(visitors); + + let struct_items: Vec<_> = similar_structs + .iter_all() + .filter(|(_k, v)| v.len() > 1) + .filter_map(|(_k, v)| { + info!( + "Potentially similar structs {:#?}", + v.iter().map(|(i, _)| i.to_string()).collect::>() + ); + let mapped_type_names = v.iter().map(|v| v.0.to_string()).collect::>(); + + // let type_new_name = + // create_struct_type_name_substitute(¤t_pass_type_name_substitutes, v); + + if let Some((i, s)) = v.first() { + let new_struct = create_common_type_struct( + s, + &read_substitute(¤t_pass_type_name_substitutes, i), + ); + + let mut mapped = BTreeMap::new(); + for mapped_type_name in mapped_type_names { + mapped.insert(mapped_type_name, new_struct.ident.to_string().to_owned()); + } + + info!("Mapped types = {:#?}", &mapped); + if mapped.keys().len() < 2 { + None + } else { + Some((mapped, Item::Struct(new_struct))) + } + } else { + None + } + }) + .collect(); + + let enum_items: Vec<_> = similar_enums + .iter_all() + .filter(|(_k, v)| v.len() > 1) + .filter_map(|(_k, v)| { + info!( + "Potentially similar enums {:#?}", + v.iter().map(|(i, _)| i.to_string()).collect::>() + ); + let mapped_type_names = v.iter().map(|v| v.0.to_string()).collect::>(); + + // let type_new_name = + // create_enum_type_name_substitute(¤t_pass_type_name_substitutes, v); + + if let Some((i, s)) = v.first() { + let new_enum = create_common_type_enum( + s, + &read_substitute(¤t_pass_type_name_substitutes, i), + ); + + let mut mapped = BTreeMap::new(); + for mapped_type_name in mapped_type_names { + mapped.insert(mapped_type_name, new_enum.ident.to_string().to_owned()); + } + + info!("Mapped types = {:#?}", &mapped); + if mapped.keys().len() < 2 { + None + } else { + Some((mapped, Item::Enum(new_enum))) + } + } else { + None + } + }) + .collect(); + + let (mapped_types, items): (Vec>, Vec) = + struct_items.into_iter().chain(enum_items).unzip(); + + let mut renaming_visitor = StructEnumRenamer { + changed: false, + names: mapped_types.into_iter().flatten().collect(), + }; + + write_type_names_to_file(&renaming_visitor.names)?; + + let unparsed_files = prune_replaced_structs(&mut renaming_visitor, visitors); + + recreate_project_files(&out_dir, unparsed_files, items) +} diff --git a/type-reducer/src/visitors.rs b/type-reducer/src/visitors.rs new file mode 100644 index 0000000..99d258f --- /dev/null +++ b/type-reducer/src/visitors.rs @@ -0,0 +1,170 @@ +use log::debug; +use log::trace; +use proc_macro2::{Ident, Span}; +use std::collections::BTreeMap; +use std::collections::BTreeSet; +use syn::Fields; +use syn::ItemEnum; +use syn::ItemStruct; +use syn::PathSegment; +use syn::Type; +use syn::visit; +use syn::visit::Visit; +use syn::visit_mut; + +use syn::visit_mut::VisitMut; + +pub struct StructVisitor<'ast, 'b> { + pub name: String, + pub structs: Vec<&'ast ItemStruct>, + pub derived_type_names: &'b BTreeSet, +} + +pub struct StructEnumVisitor<'ast, 'b> { + pub name: String, + pub structs: Vec<&'ast ItemStruct>, + pub enums: Vec<&'ast ItemEnum>, + pub derived_type_names: &'b BTreeSet, +} + +pub struct StructEnumRenamer { + pub changed: bool, + pub names: BTreeMap, +} + +fn rewrite_ident(path: &mut PathSegment, names: &BTreeMap) -> bool { + let mut file_changed = false; + if path.arguments.is_empty() { + let ident = &path.ident; + if let Some(new_name) = names.get(&ident.to_string()) { + path.ident = Ident::new(new_name, Span::call_site()); + file_changed = true; + } + } else { + match path.arguments { + syn::PathArguments::None => {} + syn::PathArguments::AngleBracketed(ref mut angle_bracketed_generic_arguments) => { + for a in angle_bracketed_generic_arguments.args.iter_mut() { + if let syn::GenericArgument::Type(Type::Path(path)) = a { + for s in path.path.segments.iter_mut() { + file_changed |= rewrite_ident(s, names); + } + } + } + } + syn::PathArguments::Parenthesized(_) => {} + } + } + file_changed +} + +impl<'ast, 'b> Visit<'ast> for StructEnumVisitor<'ast, 'b> { + fn visit_item_struct(&mut self, node: &'ast ItemStruct) { + debug!("Visiting Struct name == {}", node.ident); + //debug!("Visiting Struct name == {:#?}", node); + let mut is_simple_leaf = true; + node.fields.iter().for_each(|f| match &f.ty { + Type::Path(path_type) => { + trace!( + "\twith field name = {:?} \n\t\tfield type = {:?}", + f.ident, f.ty + ); + + for segment in &path_type.path.segments { + check_simple_type(segment, &mut is_simple_leaf, self.derived_type_names); + } + } + + _ => { + is_simple_leaf = false; + } + }); + debug!( + "Visiting Struct name == {} is leaf {is_simple_leaf}", + node.ident + ); + if is_simple_leaf { + self.structs.push(node); + } + visit::visit_item_struct(self, node); + } + + fn visit_item_enum(&mut self, node: &'ast ItemEnum) { + debug!("Visiting Enum name == {} {:?}", node.ident, node.variants); + + if node + .variants + .iter() + .map(|f| &f.fields) + .all(|f| *f == Fields::Unit) + { + self.enums.push(node); + } + } +} + +impl VisitMut for StructEnumRenamer { + fn visit_item_struct_mut(&mut self, node: &mut ItemStruct) { + debug!( + "Visiting and changing fields in struct name == {}", + node.ident + ); + + node.fields.iter_mut().for_each(|f| { + let ty = f.ty.clone(); + if let Type::Path(path_type) = &mut f.ty { + trace!( + "\twith field name = {:?} \n\t\tfield type = {:?}", + f.ident, ty + ); + + for segment in &mut path_type.path.segments { + self.changed |= rewrite_ident(segment, &self.names); + } + } + }); + + visit_mut::visit_item_struct_mut(self, node); + } +} + +fn check_simple_type( + path: &PathSegment, + is_simple: &mut bool, + derived_type_names: &BTreeSet, +) { + if path.arguments.is_empty() { + let ident = &path.ident; + debug!( + "Checking path segment {} {} ", + path.ident, + derived_type_names.contains(&ident.to_string()) + ); + + if ident == &Ident::new("String", Span::call_site()) + || ident == &Ident::new("i32", Span::call_site()) + || ident == &Ident::new("i64", Span::call_site()) + || derived_type_names.contains(&ident.to_string()) + { + } else { + *is_simple = false; + } + } else { + match &path.arguments { + syn::PathArguments::None => *is_simple = false, + syn::PathArguments::AngleBracketed(angle_bracketed_generic_arguments) => { + for a in &angle_bracketed_generic_arguments.args { + match a { + syn::GenericArgument::Type(Type::Path(path)) => { + for s in &path.path.segments { + check_simple_type(s, is_simple, derived_type_names); + } + } + _ => *is_simple = false, + } + } + } + syn::PathArguments::Parenthesized(_) => *is_simple = false, + } + } +} diff --git a/update.sh b/update.sh index 296806e..9c82000 100755 --- a/update.sh +++ b/update.sh @@ -47,6 +47,7 @@ EOF mkdir -p $APIS_DIR/standard/ mkdir -p $APIS_DIR/experimental/ + echo "// WARNING! generated file do not edit" > $APIS_DIR/standard/mod.rs for API in "${STANDARD_APIS[@]}" @@ -128,3 +129,48 @@ echo "pub mod constants;" >> $APIS_DIR/experimental/mod.rs # Format the code. cargo fmt + +rm -rf $APIS_DIR/processed +mkdir -p $APIS_DIR/processed +export RUST_LOG=info + +echo " **** PHASE 1 ***** " +#cargo run --manifest-path type-reducer/Cargo.toml -- --apis-dir $APIS_DIR/standard --out-dir $APIS_DIR/processed --previous-pass-derived-type-names ./type-reducer/reduced_types_pass_0_with_enums.txt +cargo run --manifest-path type-reducer/Cargo.toml -- --apis-dir $APIS_DIR/standard --out-dir $APIS_DIR/processed --previous-pass-derived-type-names ./type-reducer/reduced_types_pass_0.txt --current-pass-substitute-names ./type-reducer/customized_mapped_names.txt +mv mapped_names.txt mapped_names_phase_1.txt +mv mapped_types_to_names.txt mapped_types_to_names_pahse_1.txt +echo " **** PHASE 2 ***** " +cargo run --manifest-path type-reducer/Cargo.toml -- --apis-dir $APIS_DIR/processed --out-dir $APIS_DIR/processed --previous-pass-derived-type-names ./type-reducer/reduced_types_pass_1.txt --current-pass-substitute-names ./type-reducer/customized_mapped_names.txt +mv mapped_names.txt mapped_names_phase_2.txt +mv mapped_types_to_names.txt mapped_types_to_names_pahse_2.txt +echo " **** PHASE 3 ***** " +cargo run --manifest-path type-reducer/Cargo.toml -- --apis-dir $APIS_DIR/processed --out-dir $APIS_DIR/processed --previous-pass-derived-type-names ./type-reducer/reduced_types_pass_2.txt --current-pass-substitute-names ./type-reducer/customized_mapped_names.txt +mv mapped_names.txt mapped_names_phase_3.txt +mv mapped_types_to_names.txt mapped_types_to_names_pahse_3.txt +echo " **** PHASE 4 ***** " +cargo run --manifest-path type-reducer/Cargo.toml -- --apis-dir $APIS_DIR/processed --out-dir $APIS_DIR/processed --previous-pass-derived-type-names ./type-reducer/reduced_types_pass_3.txt --current-pass-substitute-names ./type-reducer/customized_mapped_names.txt +# cargo run --manifest-path type-reducer/Cargo.toml -- --apis-dir $APIS_DIR/processed --out-dir $APIS_DIR/processed --previous-pass-derived-type-names ./type-reducer/reduced_types_pass_3_with_enums.txt --current-pass-substitute-names ./type-reducer/customized_mapped_names_pass_4_with_enums.txt +mv mapped_names.txt mapped_names_phase_4.txt +mv mapped_types_to_names.txt mapped_types_to_names_pahse_4.txt + +cat << EOF >> $APIS_DIR/mod.rs + +pub mod processed; +EOF + +ENUMS=( + GRPCFilterType=RequestHeaderModifier + RequestOperationType=ReplaceFullPath + HTTPFilterType=RequestHeaderModifier +) + +ENUMS_WITH_DEFAULTS=$(printf ",%s" "${ENUMS[@]}") +ENUMS_WITH_DEFAULTS=${ENUMS_WITH_DEFAULTS:1} +echo "use super::common_types::*;" > $APIS_DIR/processed/enum_defaults.rs +GATEWAY_API_ENUMS=${ENUMS_WITH_DEFAULTS} cargo xtask gen_enum_defaults >> $APIS_DIR/processed/enum_defaults.rs + +sed -i '/#\[kube(status = "GRPCRouteStatus")\]/c\#\[kube(status = "RouteStatus")\]' ./gateway-api/src/apis/processed/grpcroutes.rs +sed -i '/#\[kube(status = "HTTPRouteStatus")\]/c\#\[kube(status = "RouteStatus")\]' ./gateway-api/src/apis/processed/httproutes.rs + + +