diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e52267..7a5f69cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Add user-info-fetcher to fetch user metadata from directory services ([#433]). + +### Changed + +- [BREAKING]: Remove legacy `nodeSelector` on rolegroups. Use the field `affinity.nodeAffinity` instead ([#433]). + +[#433]: https://github.com/stackabletech/opa-operator/pull/433 + ## [23.11.0] - 2023-11-24 ### Added diff --git a/Cargo.lock b/Cargo.lock index 6c752b5a..ba94d26a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,21 +19,22 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", "getrandom", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -81,41 +82,50 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", ] [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -124,6 +134,55 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backoff" version = "0.4.0" @@ -152,9 +211,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bit-set" @@ -179,9 +238,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block-buffer" @@ -209,6 +268,12 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + [[package]] name = "byteorder" version = "1.5.0" @@ -221,6 +286,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + [[package]] name = "cargo-lock" version = "9.0.0" @@ -233,6 +307,28 @@ dependencies = [ "url", ] +[[package]] +name = "cargo-platform" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.0.83" @@ -259,14 +355,14 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] name = "clap" -version = "4.4.6" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", "clap_derive", @@ -274,9 +370,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" dependencies = [ "anstream", "anstyle", @@ -286,21 +382,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" @@ -310,18 +406,18 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "const_format" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ "proc-macro2", "quote", @@ -330,9 +426,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -340,15 +436,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -363,6 +459,19 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.16" @@ -403,7 +512,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -414,7 +523,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -460,9 +569,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "either" @@ -494,6 +603,31 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fancy-regex" version = "0.11.0" @@ -504,17 +638,38 @@ dependencies = [ "regex", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -527,9 +682,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -542,9 +697,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -552,15 +707,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -569,38 +724,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures 0.1.31", "futures-channel", @@ -627,9 +782,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -638,9 +793,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git2" @@ -656,16 +811,35 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.12.3" +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -689,14 +863,14 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -742,13 +916,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -757,9 +932,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -783,18 +958,31 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -814,9 +1002,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -824,22 +1012,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown", ] [[package]] @@ -863,6 +1041,12 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itoa" version = "1.0.9" @@ -882,27 +1066,27 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] [[package]] name = "json-patch" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7765dccf8c39c3a470fc694efe322969d791e713ca46bc7b5c506886157572" +checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" dependencies = [ "serde", "serde_json", @@ -959,7 +1143,7 @@ dependencies = [ "bytes", "chrono", "either", - "futures 0.3.28", + "futures 0.3.29", "home", "http", "http-body", @@ -1013,7 +1197,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1026,8 +1210,8 @@ dependencies = [ "async-trait", "backoff", "derivative", - "futures 0.3.28", - "hashbrown 0.14.1", + "futures 0.3.29", + "hashbrown", "json-patch", "k8s-openapi", "kube-client", @@ -1050,9 +1234,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libgit2-sys" @@ -1078,11 +1262,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1094,6 +1284,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "mach2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +dependencies = [ + "libc", +] + [[package]] name = "matchers" version = "0.1.0" @@ -1103,12 +1302,27 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -1126,13 +1340,55 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "moka" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8017ec3548ffe7d4cef7ac0e12b044c01164a74c0f3119420faeaf13490ad8b" +dependencies = [ + "async-lock", + "async-trait", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "futures-util", + "once_cell", + "parking_lot", + "quanta", + "rustc_version", + "skeptic", + "smallvec", + "tagptr", + "thiserror", + "triomphe", + "uuid", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -1147,9 +1403,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1179,79 +1435,108 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "openssl" +version = "0.10.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" +checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" dependencies = [ - "opentelemetry_api", - "opentelemetry_sdk", + "futures-core", + "futures-sink", + "indexmap", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", + "urlencoding", ] [[package]] name = "opentelemetry-jaeger" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876958ba9084f390f913fcf04ddf7bbbb822898867bb0a51cc28f2b9e5c1b515" +checksum = "e617c66fd588e40e0dbbd66932fdc87393095b125d4459b1a3a10feb1712f8a1" dependencies = [ "async-trait", "futures-core", "futures-util", "opentelemetry", "opentelemetry-semantic-conventions", + "opentelemetry_sdk", "thrift", "tokio", ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73c9f9340ad135068800e7f1b24e9e09ed9e7143f5bf8518ded3d3ec69789269" +checksum = "f5774f1ef1f982ef2a447f6ee04ec383981a3ab99c8e77a1a7b30182e65bbc84" dependencies = [ "opentelemetry", ] -[[package]] -name = "opentelemetry_api" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a81f725323db1b1206ca3da8bb19874bbd3f57c3bcd59471bfb04525b265b9b" -dependencies = [ - "futures-channel", - "futures-util", - "indexmap 1.9.3", - "js-sys", - "once_cell", - "pin-project-lite", - "thiserror", - "urlencoding", -] - [[package]] name = "opentelemetry_sdk" -version = "0.20.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8e705a0612d48139799fcbaba0d4a90f06277153e43dd2bdc16c6f0edd8026" +checksum = "968ba3f2ca03e90e5187f5e4f46c791ef7f2c163ae87789c8ce5f5ca3b7b7de5" dependencies = [ "async-trait", "crossbeam-channel", "futures-channel", "futures-executor", "futures-util", + "glob", "once_cell", - "opentelemetry_api", - "ordered-float 3.9.1", + "opentelemetry", + "ordered-float 4.2.0", "percent-encoding", "rand", - "regex", "thiserror", "tokio", "tokio-stream", @@ -1259,18 +1544,18 @@ dependencies = [ [[package]] name = "ordered-float" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ "num-traits", ] [[package]] name = "ordered-float" -version = "3.9.1" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" +checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e" dependencies = [ "num-traits", ] @@ -1293,15 +1578,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1316,9 +1601,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -1351,7 +1636,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1382,7 +1667,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1411,9 +1696,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.68" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -1434,6 +1719,33 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + +[[package]] +name = "quanta" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" +dependencies = [ + "crossbeam-utils", + "libc", + "mach2", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quote" version = "1.0.33" @@ -1473,25 +1785,34 @@ dependencies = [ "getrandom", ] +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.9.6" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.9", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -1505,13 +1826,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.9" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -1522,23 +1843,60 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] [[package]] name = "ring" -version = "0.16.20" +version = "0.17.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1547,11 +1905,33 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" -version = "0.21.7" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", "ring", @@ -1573,18 +1953,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" -version = "0.101.6" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", @@ -1602,20 +1982,29 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "schemars" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "dyn-clone", "schemars_derive", @@ -1625,9 +2014,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ "proc-macro2", "quote", @@ -1643,9 +2032,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -1686,18 +2075,18 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -1708,19 +2097,19 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ - "ordered-float 2.10.0", + "ordered-float 2.10.1", "serde", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1736,32 +2125,54 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "indexmap 2.0.2", + "indexmap", "itoa", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ + "form_urlencoded", + "itoa", + "ryu", "serde", ] [[package]] name = "serde_yaml" -version = "0.9.25" +version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" dependencies = [ - "indexmap 2.0.2", + "indexmap", "itoa", "ryu", "serde", @@ -1797,6 +2208,21 @@ dependencies = [ "libc", ] +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "slab" version = "0.4.9" @@ -1808,9 +2234,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "snafu" @@ -1857,9 +2283,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -1867,24 +2293,25 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "stackable-opa-crd" version = "0.0.0-dev" dependencies = [ + "derivative", "semver", "serde", "serde_json", @@ -1901,7 +2328,7 @@ dependencies = [ "built", "clap", "fnv", - "futures 0.3.28", + "futures 0.3.29", "indoc", "pin-project", "product-config", @@ -1916,10 +2343,32 @@ dependencies = [ "tracing", ] +[[package]] +name = "stackable-opa-user-info-fetcher" +version = "0.0.0-dev" +dependencies = [ + "axum", + "clap", + "futures 0.3.29", + "hyper", + "moka", + "pin-project", + "reqwest", + "semver", + "serde", + "serde_json", + "snafu 0.7.5", + "stackable-opa-crd", + "stackable-operator", + "tokio", + "tracing", + "url", +] + [[package]] name = "stackable-operator" -version = "0.56.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.56.1#beeb39436024fa5f61d840402c26ee56fc5fbd29" +version = "0.59.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.59.0#52a420d2caf250b76730699f8f076bb8d3d0907d" dependencies = [ "chrono", "clap", @@ -1927,17 +2376,19 @@ dependencies = [ "derivative", "dockerfile-parser", "either", - "futures 0.3.28", + "futures 0.3.29", "json-patch", "k8s-openapi", "kube", "lazy_static", "opentelemetry", "opentelemetry-jaeger", + "opentelemetry_sdk", "product-config", "rand", "regex", "schemars", + "semver", "serde", "serde_json", "serde_yaml", @@ -1949,17 +2400,18 @@ dependencies = [ "tracing", "tracing-opentelemetry", "tracing-subscriber", + "url", ] [[package]] name = "stackable-operator-derive" -version = "0.56.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.56.1#beeb39436024fa5f61d840402c26ee56fc5fbd29" +version = "0.59.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=0.59.0#52a420d2caf250b76730699f8f076bb8d3d0907d" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -1979,15 +2431,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2003,33 +2455,79 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.38" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] @@ -2060,7 +2558,7 @@ dependencies = [ "byteorder", "integer-encoding", "log", - "ordered-float 2.10.0", + "ordered-float 2.10.1", "threadpool", ] @@ -2081,9 +2579,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", @@ -2093,9 +2591,9 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2110,13 +2608,23 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", ] [[package]] @@ -2142,9 +2650,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -2169,9 +2677,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -2182,7 +2690,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.2", + "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -2213,7 +2721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ "base64", - "bitflags 2.4.0", + "bitflags 2.4.1", "bytes", "futures-core", "futures-util", @@ -2241,11 +2749,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2254,20 +2761,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2275,21 +2782,22 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] [[package]] name = "tracing-opentelemetry" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75327c6b667828ddc28f5e3f169036cb793c3f588d83bf0f262a7f062ffed3c8" +checksum = "c67ac25c5407e7b961fafc6f7e9aa5958fd297aada2d20fa2ae1737357e55596" dependencies = [ + "js-sys", "once_cell", "opentelemetry", "opentelemetry_sdk", @@ -2298,13 +2806,14 @@ dependencies = [ "tracing-core", "tracing-log", "tracing-subscriber", + "web-time", ] [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -2327,6 +2836,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "triomphe" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" + [[package]] name = "try-lock" version = "0.2.4" @@ -2345,6 +2860,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -2380,15 +2904,15 @@ checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -2407,6 +2931,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" @@ -2425,6 +2958,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2442,9 +2985,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2452,24 +2995,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2477,28 +3032,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57099a701fb3a8043f993e8228dc24229c7b942e2b009a1b962e54489ba1d3bf" dependencies = [ "js-sys", "wasm-bindgen", @@ -2520,6 +3085,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2527,12 +3101,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -2541,7 +3115,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "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 0.52.0", ] [[package]] @@ -2550,13 +3133,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "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]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -2565,59 +3163,131 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" -version = "0.5.16" +version = "0.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907" +checksum = "0383266b19108dfc6314a56047aa545a1b4d1be60e799b4dbdd407b56402704b" dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "xml-rs" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +[[package]] +name = "zerocopy" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index ae889011..30902602 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] -members = ["rust/crd", "rust/operator-binary"] +members = ["rust/crd", "rust/operator-binary", "rust/user-info-fetcher"] +resolver = "2" [workspace.package] version = "0.0.0-dev" @@ -9,21 +10,27 @@ edition = "2021" repository = "https://github.com/stackabletech/opa-operator" [workspace.dependencies] +axum = "0.6" built = { version = "0.6", features = ["chrono", "git2"] } clap = "4.3" +derivative = "2.2" fnv = "1.0" futures = { version = "0.3", features = ["compat"] } +hyper = "0.14" indoc = "2.0" +moka = { version = "0.12", features = ["future"] } pin-project = "1.1" product-config = { git = "https://github.com/stackabletech/product-config.git", tag = "0.6.0" } +reqwest = { version = "0.11", features = ["json"] } semver = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" snafu = "0.7" -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.56.1" } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.59.0" } strum = { version = "0.25", features = ["derive"] } tokio = { version = "1.29", features = ["full"] } tracing = "0.1" +url = "2.4" # [patch."https://github.com/stackabletech/operator-rs.git"] # stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "main" } diff --git a/deploy/helm/opa-operator/crds/crds.yaml b/deploy/helm/opa-operator/crds/crds.yaml index 82330865..5b23309e 100644 --- a/deploy/helm/opa-operator/crds/crds.yaml +++ b/deploy/helm/opa-operator/crds/crds.yaml @@ -27,6 +27,7 @@ spec: clusterConfig: default: listenerClass: cluster-internal + userInfo: null description: Global OPA cluster configuration that applies to all roles and role groups. properties: listenerClass: @@ -46,6 +47,112 @@ spec: - external-unstable - external-stable type: string + userInfo: + description: Configures how to fetch additional metadata about users (such as group memberships) from an external directory service. + nullable: true + properties: + backend: + default: + none: {} + description: The backend directory service to use. + oneOf: + - required: + - none + - required: + - keycloak + properties: + keycloak: + description: Backend that fetches user information from Keycloak. + properties: + adminRealm: + description: |- + The Keycloak realm that OPA's Keycloak account (as specified by `credentialsSecretName` exists in). + + Typically `master`. + type: string + clientCredentialsSecret: + description: |- + Name of a Secret that contains client credentials of a Keycloak account with permission to read user metadata. + + Must contain the fields `clientId` and `clientSecret`. + type: string + hostname: + description: Hostname of the identity provider, e.g. `my.keycloak.corp`. + type: string + port: + description: Port of the identity provider. If TLS is used defaults to `443`, otherwise to `80`. + format: uint16 + minimum: 0.0 + nullable: true + type: integer + rootPath: + default: / + description: Root HTTP path of the identity provider. Defaults to `/`. + type: string + tls: + description: Use a TLS connection. If not specified no TLS will be used. + nullable: true + properties: + verification: + description: The verification method used to verify the certificates of the server and/or the client. + oneOf: + - required: + - none + - required: + - server + properties: + none: + description: Use TLS but don't verify certificates. + type: object + server: + description: Use TLS and a CA certificate to verify the server. + properties: + caCert: + description: CA cert to verify the server. + oneOf: + - required: + - webPki + - required: + - secretClass + properties: + secretClass: + description: Name of the [SecretClass](https://docs.stackable.tech/home/nightly/secret-operator/secretclass) which will provide the CA certificate. Note that a SecretClass does not need to have a key but can also work with just a CA certificate, so if you got provided with a CA cert but don't have access to the key you can still use this method. + type: string + webPki: + description: Use TLS and the CA certificates trusted by the common web browsers to verify the server. This can be useful when you e.g. use public AWS S3 or other public available services. + type: object + type: object + required: + - caCert + type: object + type: object + required: + - verification + type: object + userRealm: + description: The Keycloak realm that user metadata should be resolved from. + type: string + required: + - adminRealm + - clientCredentialsSecret + - hostname + - userRealm + type: object + none: + description: Dummy backend that adds no extra user information. + type: object + type: object + cache: + default: + entryTimeToLive: 1m + description: Caching configuration. + properties: + entryTimeToLive: + default: 1m + description: How long metadata about each user should be cached for. + type: string + type: object + type: object vectorAggregatorConfigMapName: description: Name of the Vector aggregator discovery ConfigMap. It must contain the key `ADDRESS` with the address of the Vector aggregator. nullable: true @@ -83,14 +190,14 @@ spec: type: string pullPolicy: default: Always - description: '[Pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) used when pulling the Images' + description: '[Pull policy](https://kubernetes.io/docs/concepts/containers/images/#image-pull-policy) used when pulling the image.' enum: - IfNotPresent - Always - Never type: string pullSecrets: - description: '[Image pull secrets](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) to pull images from a private registry' + description: '[Image pull secrets](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) to pull images from a private registry.' items: description: LocalObjectReference contains enough information to let you locate the referenced object inside the same namespace. properties: @@ -120,93 +227,565 @@ spec: config: default: {} properties: - gracefulShutdownTimeout: - description: Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. - nullable: true - type: string - logging: + affinity: default: - enableVectorAgent: null - containers: {} - description: Logging configuration + podAffinity: null + podAntiAffinity: null + nodeAffinity: null + nodeSelector: null + description: These configuration settings control [Pod placement](https://docs.stackable.tech/home/nightly/concepts/operations/pod_placement). properties: - containers: - additionalProperties: - anyOf: - - required: - - custom - - {} - description: Log configuration of the container - properties: - console: - description: Configuration for the console appender - nullable: true + nodeAffinity: + description: Node affinity is a group of node affinity scheduling rules. + nullable: true + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). properties: - level: - description: The log level threshold. Log events with a lower log level are discarded. - enum: - - TRACE - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - nullable: true - type: string + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight type: object - custom: - description: Custom log configuration provided in a ConfigMap + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + nodeSelector: + nullable: true + type: object + podAffinity: + description: Pod affinity is a group of inter pod affinity scheduling rules. + nullable: true + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) properties: - configMap: - description: ConfigMap containing the log configuration files - nullable: true - type: string + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight type: object - file: - description: Configuration for the file appender - nullable: true + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running properties: - level: - description: The log level threshold. Log events with a lower log level are discarded. - enum: - - TRACE - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - nullable: true - type: string - type: object - loggers: - additionalProperties: - description: Configuration of a logger - properties: - level: - description: The log level threshold. Log events with a lower log level are discarded. - enum: - - TRACE - - DEBUG - - INFO - - WARN - - ERROR - - FATAL - - NONE - nullable: true + labelSelector: + description: A label query over a set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: type: string - type: object - default: {} - description: Configuration per logger + type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey type: object - type: object - description: Log configuration per container + type: array type: object - enableVectorAgent: - description: Wether or not to deploy a container with the Vector log agent + podAntiAffinity: + description: Pod anti affinity is a group of inter pod anti affinity scheduling rules. nullable: true - type: boolean + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + gracefulShutdownTimeout: + description: Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. + nullable: true + type: string + logging: + default: + enableVectorAgent: null + containers: {} + description: Logging configuration, learn more in the [logging concept documentation](https://docs.stackable.tech/home/nightly/concepts/logging). + properties: + containers: + additionalProperties: + anyOf: + - required: + - custom + - {} + description: Log configuration of the container + properties: + console: + description: Configuration for the console appender + nullable: true + properties: + level: + description: The log level threshold. Log events with a lower log level are discarded. + enum: + - TRACE + - DEBUG + - INFO + - WARN + - ERROR + - FATAL + - NONE + nullable: true + type: string + type: object + custom: + description: Custom log configuration provided in a ConfigMap + properties: + configMap: + description: ConfigMap containing the log configuration files + nullable: true + type: string + type: object + file: + description: Configuration for the file appender + nullable: true + properties: + level: + description: The log level threshold. Log events with a lower log level are discarded. + enum: + - TRACE + - DEBUG + - INFO + - WARN + - ERROR + - FATAL + - NONE + nullable: true + type: string + type: object + loggers: + additionalProperties: + description: Configuration of a logger + properties: + level: + description: The log level threshold. Log events with a lower log level are discarded. + enum: + - TRACE + - DEBUG + - INFO + - WARN + - ERROR + - FATAL + - NONE + nullable: true + type: string + type: object + default: {} + description: Configuration per logger + type: object + type: object + description: Log configuration per container. + type: object + enableVectorAgent: + description: Wether or not to deploy a container with the Vector log agent. + nullable: true + type: boolean type: object resources: default: @@ -217,6 +796,7 @@ spec: min: null max: null storage: {} + description: Resource usage is configured here, this includes CPU usage, memory usage and disk storage usage, if this role needs any. properties: cpu: default: @@ -224,21 +804,22 @@ spec: max: null properties: max: - description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." + description: The maximum amount of CPU cores that can be requested by Pods. Equivalent to the `limit` for Pod resource configuration. Cores are specified either as a decimal point number or as milli units. For example:`1.5` will be 1.5 cores, also written as `1500m`. nullable: true type: string min: - description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." + description: The minimal amount of CPU cores that Pods need to run. Equivalent to the `request` for Pod resource configuration. Cores are specified either as a decimal point number or as milli units. For example:`1.5` will be 1.5 cores, also written as `1500m`. nullable: true type: string type: object memory: properties: limit: - description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." + description: 'The maximum amount of memory that should be available to the Pod. Specified as a byte [Quantity](https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/quantity/), which means these suffixes are supported: E, P, T, G, M, k. You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki. For example, the following represent roughly the same value: `128974848, 129e6, 129M, 128974848000m, 123Mi`' nullable: true type: string runtimeLimits: + description: Additional options that can be specified. type: object type: object storage: @@ -251,15 +832,17 @@ spec: type: string type: object default: {} + description: The `configOverrides` can be used to configure properties in product config files that are not exposed in the CRD. Read the [config overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#config-overrides) and consult the operator specific usage guide documentation for details on the available config files and settings for the specific product. type: object envOverrides: additionalProperties: type: string default: {} + description: '`envOverrides` configure environment variables to be set in the Pods. It is a map from strings to strings - environment variables and the value to set. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about the product specific environment variables that are available.' type: object podOverrides: default: {} - description: See PodTemplateSpec (https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) for more details + description: In the `podOverrides` property you can define a [PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) to override any property that can be set on a Kubernetes Pod. Read the [Pod overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#pod-overrides) for more information. properties: metadata: properties: @@ -2876,200 +3459,672 @@ spec: optional: type: boolean type: object - downwardAPI: + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + type: object + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + type: string + resource: + type: string + type: object + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + user: + type: string + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + type: object + type: object + type: array + type: object + type: object + roleConfig: + default: {} + description: This is a product-agnostic RoleConfig, with nothing in it. It is used e.g. by products that have nothing configurable at role level. + type: object + roleGroups: + additionalProperties: + properties: + cliOverrides: + additionalProperties: + type: string + default: {} + type: object + config: + default: {} + properties: + affinity: + default: + podAffinity: null + podAntiAffinity: null + nodeAffinity: null + nodeSelector: null + description: These configuration settings control [Pod placement](https://docs.stackable.tech/home/nightly/concepts/operations/pod_placement). + properties: + nodeAffinity: + description: Node affinity is a group of node affinity scheduling rules. + nullable: true + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. The terms are ORed. + items: + description: A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements by node's labels. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements by node's fields. + items: + description: A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: The label key that the selector applies to. + type: string + operator: + description: Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: 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. If the operator is Gt or Lt, the values array must have a single element, which will be interpreted as an integer. This array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + nodeSelector: + nullable: true + type: object + podAffinity: + description: Pod affinity is a group of inter pod affinity scheduling rules. + nullable: true + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Pod anti affinity is a group of inter pod anti affinity scheduling rules. + nullable: true + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated with the corresponding weight. properties: - items: - items: - properties: - fieldRef: + labelSelector: + description: A label query over a set of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: - apiVersion: + key: + description: key is the label key that the selector applies to. type: string - fieldPath: + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator type: object - mode: - format: int32 - type: integer - path: + type: array + matchLabels: + additionalProperties: type: string - resourceFieldRef: + description: 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. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: - containerName: - type: string - divisor: + key: + description: key is the label key that the selector applies to. type: string - resource: + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator type: object - type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey type: object - secret: + weight: + description: weight associated with matching the corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the anti-affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to a pod label update), the system may or may not try to eventually evict the pod from its node. When there are multiple elements, the lists of nodes corresponding to each podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-located is defined as running on a node whose value of the label with key matches that of any node on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, in this case pods. properties: - items: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: + description: key is the label key that the selector applies to. type: string - mode: - format: int32 - type: integer - path: + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator type: object type: array - name: - type: string - optional: - type: boolean + matchLabels: + additionalProperties: + type: string + description: 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. + type: object type: object - serviceAccountToken: + namespaceSelector: + description: A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector ({}) matches all namespaces. properties: - audience: - type: string - expirationSeconds: - format: int64 - type: integer - path: - type: string + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: 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. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: 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. + type: object type: object - type: object - type: array - type: object - quobyte: - properties: - group: - type: string - readOnly: - type: boolean - registry: - type: string - tenant: - type: string - user: - type: string - volume: - type: string - type: object - rbd: - properties: - fsType: - type: string - image: - type: string - keyring: - type: string - monitors: - items: - type: string - type: array - pool: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - user: - type: string - type: object - scaleIO: - properties: - fsType: - type: string - gateway: - type: string - protectionDomain: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - sslEnabled: - type: boolean - storageMode: - type: string - storagePool: - type: string - system: - type: string - volumeName: - type: string - type: object - secret: - properties: - defaultMode: - format: int32 - type: integer - items: - items: - properties: - key: - type: string - mode: - format: int32 - type: integer - path: + namespaces: + description: namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed. type: string + required: + - topologyKey type: object type: array - optional: - type: boolean - secretName: - type: string - type: object - storageos: - properties: - fsType: - type: string - readOnly: - type: boolean - secretRef: - properties: - name: - type: string - type: object - volumeName: - type: string - volumeNamespace: - type: string - type: object - vsphereVolume: - properties: - fsType: - type: string - storagePolicyID: - type: string - storagePolicyName: - type: string - volumePath: - type: string type: object type: object - type: array - type: object - type: object - roleConfig: - default: {} - description: This is a product-agnostic RoleConfig, with nothing in it. It is used e.g. by products that have nothing configurable at role level. - type: object - roleGroups: - additionalProperties: - properties: - cliOverrides: - additionalProperties: - type: string - default: {} - type: object - config: - default: {} - properties: gracefulShutdownTimeout: description: Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. nullable: true @@ -3078,7 +4133,7 @@ spec: default: enableVectorAgent: null containers: {} - description: Logging configuration + description: Logging configuration, learn more in the [logging concept documentation](https://docs.stackable.tech/home/nightly/concepts/logging). properties: containers: additionalProperties: @@ -3151,10 +4206,10 @@ spec: description: Configuration per logger type: object type: object - description: Log configuration per container + description: Log configuration per container. type: object enableVectorAgent: - description: Wether or not to deploy a container with the Vector log agent + description: Wether or not to deploy a container with the Vector log agent. nullable: true type: boolean type: object @@ -3167,6 +4222,7 @@ spec: min: null max: null storage: {} + description: Resource usage is configured here, this includes CPU usage, memory usage and disk storage usage, if this role needs any. properties: cpu: default: @@ -3174,21 +4230,22 @@ spec: max: null properties: max: - description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." + description: The maximum amount of CPU cores that can be requested by Pods. Equivalent to the `limit` for Pod resource configuration. Cores are specified either as a decimal point number or as milli units. For example:`1.5` will be 1.5 cores, also written as `1500m`. nullable: true type: string min: - description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." + description: The minimal amount of CPU cores that Pods need to run. Equivalent to the `request` for Pod resource configuration. Cores are specified either as a decimal point number or as milli units. For example:`1.5` will be 1.5 cores, also written as `1500m`. nullable: true type: string type: object memory: properties: limit: - description: "Quantity is a fixed-point representation of a number. It provides convenient marshaling/unmarshaling in JSON and YAML, in addition to String() and AsInt64() accessors.\n\nThe serialization format is:\n\n``` ::= \n\n\t(Note that may be empty, from the \"\" case in .)\n\n ::= 0 | 1 | ... | 9 ::= | ::= | . | . | . ::= \"+\" | \"-\" ::= | ::= | | ::= Ki | Mi | Gi | Ti | Pi | Ei\n\n\t(International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)\n\n ::= m | \"\" | k | M | G | T | P | E\n\n\t(Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)\n\n ::= \"e\" | \"E\" ```\n\nNo matter which of the three exponent forms is used, no quantity may represent a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal places. Numbers larger or more precise will be capped or rounded up. (E.g.: 0.1m will rounded up to 1m.) This may be extended in the future if we require larger or smaller quantities.\n\nWhen a Quantity is parsed from a string, it will remember the type of suffix it had, and will use the same type again when it is serialized.\n\nBefore serializing, Quantity will be put in \"canonical form\". This means that Exponent/suffix will be adjusted up or down (with a corresponding increase or decrease in Mantissa) such that:\n\n- No precision is lost - No fractional digits will be emitted - The exponent (or suffix) is as large as possible.\n\nThe sign will be omitted unless the number is negative.\n\nExamples:\n\n- 1.5 will be serialized as \"1500m\" - 1.5Gi will be serialized as \"1536Mi\"\n\nNote that the quantity will NEVER be internally represented by a floating point number. That is the whole point of this exercise.\n\nNon-canonical values will still parse as long as they are well formed, but will be re-emitted in their canonical form. (So always use canonical form, or don't diff.)\n\nThis format is intended to make it difficult to use these numbers without writing some sort of special handling code in the hopes that that will cause implementors to also use a fixed point implementation." + description: 'The maximum amount of memory that should be available to the Pod. Specified as a byte [Quantity](https://kubernetes.io/docs/reference/kubernetes-api/common-definitions/quantity/), which means these suffixes are supported: E, P, T, G, M, k. You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki. For example, the following represent roughly the same value: `128974848, 129e6, 129M, 128974848000m, 123Mi`' nullable: true type: string runtimeLimits: + description: Additional options that can be specified. type: object type: object storage: @@ -3201,15 +4258,17 @@ spec: type: string type: object default: {} + description: The `configOverrides` can be used to configure properties in product config files that are not exposed in the CRD. Read the [config overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#config-overrides) and consult the operator specific usage guide documentation for details on the available config files and settings for the specific product. type: object envOverrides: additionalProperties: type: string default: {} + description: '`envOverrides` configure environment variables to be set in the Pods. It is a map from strings to strings - environment variables and the value to set. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about the product specific environment variables that are available.' type: object podOverrides: default: {} - description: See PodTemplateSpec (https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) for more details + description: In the `podOverrides` property you can define a [PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core) to override any property that can be set on a Kubernetes Pod. Read the [Pod overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#pod-overrides) for more information. properties: metadata: properties: @@ -6010,37 +7069,6 @@ spec: minimum: 0.0 nullable: true type: integer - selector: - description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects. - nullable: true - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: 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. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: 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. - type: object - type: object type: object type: object required: diff --git a/docs/modules/opa/pages/usage-guide/policies.adoc b/docs/modules/opa/pages/usage-guide/policies.adoc index 3f9ce3dd..3758ae5c 100644 --- a/docs/modules/opa/pages/usage-guide/policies.adoc +++ b/docs/modules/opa/pages/usage-guide/policies.adoc @@ -2,7 +2,7 @@ Users can define policies by using Rego, OPAs https://www.openpolicyagent.org/docs/latest/policy-language/[policy language]. -Policy definitionas are deployed as `ConfigMap` resources as described in xref:implementation-notes.adoc[implementation notes]. +Policy definitions are deployed as `ConfigMap` resources as described in xref:implementation-notes.adoc[implementation notes]. Here is an example: diff --git a/docs/modules/opa/pages/usage-guide/resources.adoc b/docs/modules/opa/pages/usage-guide/resources.adoc index c001bd60..d02c4248 100644 --- a/docs/modules/opa/pages/usage-guide/resources.adoc +++ b/docs/modules/opa/pages/usage-guide/resources.adoc @@ -1,6 +1,6 @@ = Resource requests -include:concepts:stackable_resource_requests.adoc[] +include::concepts:stackable_resource_requests.adoc[] A minimal OPA setup consists of 1 Pod per Node (DaemonSet) and has the following https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/[resource requirements] per scheduled Pod: diff --git a/docs/modules/opa/pages/usage-guide/user-info-fetcher.adoc b/docs/modules/opa/pages/usage-guide/user-info-fetcher.adoc new file mode 100644 index 00000000..6b4ecf73 --- /dev/null +++ b/docs/modules/opa/pages/usage-guide/user-info-fetcher.adoc @@ -0,0 +1,137 @@ += User info fetcher + +The _User info fetcher_ allows for additional information to be obtained from the configured backend (for example, Keycloak). +You can then write Rego rules for OpenPolicyAgent which make an HTTP request to the User info fetcher and make use of the additional information returned for the username or user id. + +You can enable the User info fetcher sidecar as follows: + +[source,yaml] +---- +apiVersion: opa.stackable.tech/v1alpha1 +kind: OpaCluster +metadata: + name: opa +spec: + image: + productVersion: 0.57.0 + clusterConfig: + userInfo: # <1> + backend: + keycloak: + hostname: keycloak.my-namespace.svc.cluster.local + port: 8443 + tls: + verification: + server: + caCert: + secretClass: tls # <2> + clientCredentialsSecret: user-info-fetcher-client-credentials # <3> + adminRealm: master # <4> + userRealm: master # <4> + cache: # optional, enabled by default + entryTimeToLive: 60s # optional, defaults to 60s + servers: + roleGroups: + default: {} +--- +apiVersion: v1 +kind: Secret +metadata: + name: user-info-fetcher-client-credentials +stringData: + clientId: user-info-fetcher # <3> + clientSecret: user-info-fetcher-client-secret # <3> +---- + +<1> Enable the `user-info-fetcher` sidecar +<2> Enable TLS verification using the CA from the `tls` secretClass. +<3> Obtain Keycloak API credentials from the specified secret. The Secret must have `clientId` and `clientSecret` entries. +<4> Refer to the applicable realm in your Keycloak server. + +Currently the following backends are supported: + +* <<_keycloak>> + +== Keycloak + +// todo: maybe this section should be under a Tutorial? +Fetch groups and extra credentials, but not roles. + +// TODO: Document how to use it in OPA regorules, e.g. to authorize based on group membership +== Example rego rule + +.About unencrypted HTTP +[NOTE] +=============================== +*NOTE* The User info fetcher serves endpoints over clear-text HTTP. + +It is intended to only be accessed from the OPA Server via _localhost_ and to not be exposed outside of the Pod. +=============================== + +[source,rego] +---- +package test # <1> + +# Define a function to lookup by username +userInfoByUsername(username) := http.send({ + "method": "POST", + "url": "http://127.0.0.1:9476/user", + "body": {"username": username}, <2> + "headers": {"Content-Type": "application/json"}, + "raise_error": true +}).body + +# Define a function to lookup by a stable identifier +userInfoById(id) := http.send({ + "method": "POST", + "url": "http://127.0.0.1:9476/user", + "body": {"id": id}, <3> + "headers": {"Content-Type": "application/json"}, + "raise_error": true +}).body + +currentUserInfoByUsername := userInfoByUsername(input.username) +currentUserInfoById := userInfoById(input.id) +---- + +<1> The package name is important in the OPA URL used by the product. +<2> Lookup by username +<3> Lookup by id + +For more information on the request and response payloads, see <<_user_info_fetcher_api>> + +== User info fetcher API + +HTTP Post Requests must be sent to the `/user` endpoint with the following header set: `Content-Type: application/json`. + +You can either lookup the user info by stable identifier: + +[source,json] +---- +{ + "id": "af07f12c-a2db-40a7-93e0-874537bdf3f5", +} +---- + +or by the username: + +[source,json] +---- +{ + "username": "alice", +} +---- + +If the user is found, the following response structure will be returned: + +[source,json] +---- +{ + "id": "af07f12c-a2db-40a7-93e0-874537bdf3f5", + "username": "alice", + "groups": [ + "/superset-admin" + ], + "customAttributes": {} +} +---- diff --git a/docs/modules/opa/partials/nav.adoc b/docs/modules/opa/partials/nav.adoc index 5c26fbe2..ba3ffed8 100644 --- a/docs/modules/opa/partials/nav.adoc +++ b/docs/modules/opa/partials/nav.adoc @@ -4,6 +4,7 @@ * xref:opa:usage-guide/index.adoc[] ** xref:opa:usage-guide/listenerclass.adoc[] ** xref:opa:usage-guide/policies.adoc[] +// ** xref:opa:usage-guide/user-info-fetcher.adoc[] Not yet ready for public consumption ** xref:opa:usage-guide/resources.adoc[] ** xref:opa:usage-guide/logging.adoc[] ** xref:opa:usage-guide/monitoring.adoc[] diff --git a/rust/crd/Cargo.toml b/rust/crd/Cargo.toml index b0e2c7dc..770863d6 100644 --- a/rust/crd/Cargo.toml +++ b/rust/crd/Cargo.toml @@ -9,6 +9,7 @@ repository.workspace = true publish = false [dependencies] +derivative.workspace = true semver.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/rust/crd/src/lib.rs b/rust/crd/src/lib.rs index 015d5645..e212f01a 100644 --- a/rust/crd/src/lib.rs +++ b/rust/crd/src/lib.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{ commons::{ + affinity::StackableAffinity, cluster_operation::ClusterOperation, product_image_selection::ProductImage, resources::{ @@ -24,6 +25,8 @@ use stackable_operator::{ }; use strum::{Display, EnumIter, EnumString}; +pub mod user_info_fetcher; + pub const APP_NAME: &str = "opa"; pub const OPERATOR_NAME: &str = "opa.stackable.tech"; @@ -99,6 +102,10 @@ pub struct OpaClusterConfig { /// will be used to expose the service, and ListenerClass names will stay the same, allowing for a non-breaking change. #[serde(default)] pub listener_class: CurrentlySupportedListenerClasses, + /// Configures how to fetch additional metadata about users (such as group memberships) + /// from an external directory service. + #[serde(default)] + pub user_info: Option, } // TODO: Temporary solution until listener-operator is finished @@ -179,11 +186,14 @@ pub enum Container { serde(rename_all = "camelCase") )] pub struct OpaConfig { + #[fragment_attrs(serde(default))] + pub resources: Resources, + #[fragment_attrs(serde(default))] pub logging: Logging, #[fragment_attrs(serde(default))] - pub resources: Resources, + pub affinity: StackableAffinity, /// Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. #[fragment_attrs(serde(default))] @@ -205,6 +215,9 @@ impl OpaConfig { }, storage: OpaStorageConfigFragment {}, }, + // There is no point in having a default affinity, as exactly one OPA Pods should run on every node. + // We only have the affinity configurable to let users limit the nodes the OPA Pods run on. + affinity: Default::default(), graceful_shutdown_timeout: Some(DEFAULT_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT), } } @@ -297,15 +310,6 @@ impl OpaCluster { )) } - pub fn node_selector(&self, role_group: &str) -> Option> { - self.spec - .servers - .role_groups - .get(role_group) - .and_then(|rg| rg.selector.as_ref()) - .and_then(|selector| selector.match_labels.clone()) - } - /// Retrieve and merge resource configs for role and role groups pub fn merged_config( &self, diff --git a/rust/crd/src/user_info_fetcher.rs b/rust/crd/src/user_info_fetcher.rs new file mode 100644 index 00000000..b588eed7 --- /dev/null +++ b/rust/crd/src/user_info_fetcher.rs @@ -0,0 +1,86 @@ +use derivative::Derivative; +use serde::{Deserialize, Serialize}; +use stackable_operator::{ + commons::authentication::tls::TlsClientDetails, + schemars::{self, JsonSchema}, + time::Duration, +}; + +#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + /// The backend directory service to use. + #[serde(default)] + pub backend: Backend, + + /// Caching configuration. + #[serde(default)] + pub cache: Cache, +} + +#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum Backend { + /// Dummy backend that adds no extra user information. + None {}, + + /// Backend that fetches user information from Keycloak. + Keycloak(KeycloakBackend), +} + +impl Default for Backend { + fn default() -> Self { + Self::None {} + } +} + +#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct KeycloakBackend { + /// Hostname of the identity provider, e.g. `my.keycloak.corp`. + pub hostname: String, + + /// Port of the identity provider. If TLS is used defaults to `443`, otherwise to `80`. + pub port: Option, + + /// Root HTTP path of the identity provider. Defaults to `/`. + #[serde(default = "default_root_path")] + pub root_path: String, + + /// Use a TLS connection. If not specified no TLS will be used. + #[serde(flatten)] + pub tls: TlsClientDetails, + + /// Name of a Secret that contains client credentials of a Keycloak account with permission to read user metadata. + /// + /// Must contain the fields `clientId` and `clientSecret`. + pub client_credentials_secret: String, + + /// The Keycloak realm that OPA's Keycloak account (as specified by `credentialsSecretName` exists in). + /// + /// Typically `master`. + pub admin_realm: String, + + /// The Keycloak realm that user metadata should be resolved from. + pub user_realm: String, +} + +fn default_root_path() -> String { + "/".to_string() +} + +#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize, Derivative)] +#[derivative(Default)] +#[serde(rename_all = "camelCase")] +pub struct Cache { + /// How long metadata about each user should be cached for. + #[derivative(Default(value = "Cache::default_entry_time_to_live()"))] + #[serde(default = "Cache::default_entry_time_to_live")] + pub entry_time_to_live: Duration, +} + +impl Cache { + const fn default_entry_time_to_live() -> Duration { + Duration::from_minutes_unchecked(1) + } +} diff --git a/rust/operator-binary/src/controller.rs b/rust/operator-binary/src/controller.rs index 15ca64a0..27919046 100644 --- a/rust/operator-binary/src/controller.rs +++ b/rust/operator-binary/src/controller.rs @@ -1,4 +1,3 @@ -//! Ensures that `Pod`s are configured and running for each [`OpaCluster`] use std::{ borrow::Cow, collections::{BTreeMap, HashMap}, @@ -10,22 +9,26 @@ use product_config::{types::PropertyNameKind, ProductConfigManager}; use serde_json::json; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_opa_crd::{ - Container, OpaCluster, OpaClusterStatus, OpaConfig, OpaRole, APP_NAME, + user_info_fetcher, Container, OpaCluster, OpaClusterStatus, OpaConfig, OpaRole, APP_NAME, DEFAULT_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT, OPERATOR_NAME, }; use stackable_operator::{ builder::{ resources::ResourceRequirementsBuilder, ConfigMapBuilder, ContainerBuilder, - FieldPathEnvVar, ObjectMetaBuilder, PodBuilder, PodSecurityContextBuilder, VolumeBuilder, + FieldPathEnvVar, ObjectMetaBuilder, ObjectMetaBuilderError, PodBuilder, + PodSecurityContextBuilder, VolumeBuilder, }, cluster_resources::{ClusterResourceApplyStrategy, ClusterResources}, - commons::{product_image_selection::ResolvedProductImage, rbac::build_rbac_resources}, + commons::{ + authentication::tls::TlsClientDetailsError, product_image_selection::ResolvedProductImage, + rbac::build_rbac_resources, + }, k8s_openapi::{ api::{ apps::v1::{DaemonSet, DaemonSetSpec}, core::v1::{ - ConfigMap, EmptyDirVolumeSource, EnvVar, HTTPGetAction, Probe, Service, - ServicePort, ServiceSpec, + ConfigMap, EmptyDirVolumeSource, EnvVar, HTTPGetAction, Probe, SecretVolumeSource, + Service, ServicePort, ServiceSpec, }, }, apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString}, @@ -35,7 +38,7 @@ use stackable_operator::{ runtime::{controller::Action, reflector::ObjectRef}, Resource as KubeResource, ResourceExt, }, - labels::{role_group_selector_labels, role_selector_labels, ObjectLabels}, + kvp::{Label, LabelError, Labels, ObjectLabels}, logging::controller::ReconcilerError, memory::{BinaryMultiple, MemoryQuantity}, product_config_utils::{transform_all_roles_to_config, validate_all_roles_and_groups_config}, @@ -83,6 +86,8 @@ const LOG_VOLUME_NAME: &str = "log"; const STACKABLE_LOG_DIR: &str = "/stackable/log"; const BUNDLES_VOLUME_NAME: &str = "bundles"; const BUNDLES_DIR: &str = "/bundles"; +const USER_INFO_FETCHER_CREDENTIALS_VOLUME_NAME: &str = "credentials"; +const USER_INFO_FETCHER_CREDENTIALS_DIR: &str = "/stackable/credentials"; const DOCKER_IMAGE_BASE_NAME: &str = "opa"; @@ -117,6 +122,7 @@ pub struct Ctx { pub client: stackable_operator::client::Client, pub product_config: ProductConfigManager, pub opa_bundle_builder_clusterrole: String, + pub user_info_fetcher_image: String, } #[derive(Snafu, Debug, EnumDiscriminants)] @@ -237,6 +243,20 @@ pub enum Error { GracefulShutdown { source: crate::operations::graceful_shutdown::Error, }, + + #[snafu(display("failed to serialize user info fetcher configuration"))] + SerializeUserInfoFetcherConfig { source: serde_json::Error }, + + #[snafu(display("failed to build label"))] + BuildLabel { source: LabelError }, + + #[snafu(display("failed to build object meta data"))] + ObjectMeta { source: ObjectMetaBuilderError }, + + #[snafu(display( + "failed to build volume or volume mount spec for the Keycloak backend TLS config" + ))] + VolumeAndMounts { source: TlsClientDetailsError }, } type Result = std::result::Result; @@ -303,12 +323,12 @@ pub async fn reconcile_opa(opa: Arc, ctx: Arc) -> Result, ctx: Arc) -> Result, ) -> Result { + let prometheus_label = + Label::try_from(("prometheus.io/scrape", "true")).context(BuildLabelSnafu)?; + + let metadata = ObjectMetaBuilder::new() + .name_and_namespace(opa) + .name(&rolegroup.object_name()) + .ownerreference_from_resource(opa, None, Some(true)) + .context(ObjectMissingMetadataForOwnerRefSnafu)? + .with_recommended_labels(build_recommended_labels( + opa, + &resolved_product_image.app_version_label, + &rolegroup.role, + &rolegroup.role_group, + )) + .context(ObjectMetaSnafu)? + .with_label(prometheus_label) + .build(); + + let service_selector_labels = + Labels::role_group_selector(opa, APP_NAME, &rolegroup.role, &rolegroup.role_group) + .context(BuildLabelSnafu)?; + + let service_spec = ServiceSpec { + // Internal communication does not need to be exposed + type_: Some("ClusterIP".to_string()), + cluster_ip: Some("None".to_string()), + ports: Some(service_ports()), + selector: Some(service_selector_labels.into()), + publish_not_ready_addresses: Some(true), + ..ServiceSpec::default() + }; + Ok(Service { - metadata: ObjectMetaBuilder::new() - .name_and_namespace(opa) - .name(&rolegroup.object_name()) - .ownerreference_from_resource(opa, None, Some(true)) - .context(ObjectMissingMetadataForOwnerRefSnafu)? - .with_recommended_labels(build_recommended_labels( - opa, - &resolved_product_image.app_version_label, - &rolegroup.role, - &rolegroup.role_group, - )) - .with_label("prometheus.io/scrape", "true") - .build(), - spec: Some(ServiceSpec { - // Internal communication does not need to be exposed - type_: Some("ClusterIP".to_string()), - cluster_ip: Some("None".to_string()), - ports: Some(service_ports()), - selector: Some(role_group_selector_labels( - opa, - APP_NAME, - &rolegroup.role, - &rolegroup.role_group, - )), - publish_not_ready_addresses: Some(true), - ..ServiceSpec::default() - }), + metadata, + spec: Some(service_spec), status: None, }) } @@ -519,23 +556,31 @@ fn build_server_rolegroup_config_map( ) -> Result { let mut cm_builder = ConfigMapBuilder::new(); + let metadata = ObjectMetaBuilder::new() + .name_and_namespace(opa) + .name(rolegroup.object_name()) + .ownerreference_from_resource(opa, None, Some(true)) + .context(ObjectMissingMetadataForOwnerRefSnafu)? + .with_recommended_labels(build_recommended_labels( + opa, + &resolved_product_image.app_version_label, + &rolegroup.role, + &rolegroup.role_group, + )) + .context(ObjectMetaSnafu)? + .build(); + cm_builder - .metadata( - ObjectMetaBuilder::new() - .name_and_namespace(opa) - .name(rolegroup.object_name()) - .ownerreference_from_resource(opa, None, Some(true)) - .context(ObjectMissingMetadataForOwnerRefSnafu)? - .with_recommended_labels(build_recommended_labels( - opa, - &resolved_product_image.app_version_label, - &rolegroup.role, - &rolegroup.role_group, - )) - .build(), - ) + .metadata(metadata) .add_data(CONFIG_FILE, build_config_file()); + if let Some(user_info) = &opa.spec.cluster_config.user_info { + cm_builder.add_data( + "user-info-fetcher.json", + serde_json::to_string_pretty(user_info).context(SerializeUserInfoFetcherConfigSnafu)?, + ); + } + extend_role_group_config_map( rolegroup, vector_aggregator_address, @@ -560,6 +605,7 @@ fn build_server_rolegroup_config_map( /// /// We run an OPA on each node, because we want to avoid requiring network roundtrips for services making /// policy queries (which are often chained in serial, and block other tasks in the products). +#[allow(clippy::too_many_arguments)] fn build_server_rolegroup_daemonset( opa: &OpaCluster, resolved_product_image: &ResolvedProductImage, @@ -567,6 +613,7 @@ fn build_server_rolegroup_daemonset( rolegroup_ref: &RoleGroupRef, server_config: &HashMap>, merged_config: &OpaConfig, + user_info_fetcher_image: &str, sa_name: &str, ) -> Result { let role = opa.role(opa_role); @@ -585,6 +632,8 @@ fn build_server_rolegroup_daemonset( }) .collect::>(); + let mut pb = PodBuilder::new(); + let prepare_container_name = Container::Prepare.to_string(); let mut cb_prepare = ContainerBuilder::new(&prepare_container_name).context(IllegalContainerNameSnafu)?; @@ -703,53 +752,99 @@ fn build_server_rolegroup_daemonset( ..Probe::default() }); - let mut pb = PodBuilder::new(); - - pb.metadata_builder(|m| { - m.with_recommended_labels(build_recommended_labels( + let pb_metadata = ObjectMetaBuilder::new() + .with_recommended_labels(build_recommended_labels( opa, &resolved_product_image.app_version_label, &rolegroup_ref.role, &rolegroup_ref.role_group, )) - }) - .add_init_container(cb_prepare.build()) - .add_container(cb_opa.build()) - .add_container(cb_bundle_builder.build()) - .image_pull_secrets_from_product_image(resolved_product_image) - .node_selector_opt(opa.node_selector(&rolegroup_ref.role_group)) - .add_volume( - VolumeBuilder::new(CONFIG_VOLUME_NAME) - .with_config_map(rolegroup_ref.object_name()) - .build(), - ) - .add_volume( - VolumeBuilder::new(BUNDLES_VOLUME_NAME) - .with_empty_dir(None::, None) - .build(), - ) - .add_volume( - VolumeBuilder::new(LOG_VOLUME_NAME) - .empty_dir(EmptyDirVolumeSource { - medium: None, - size_limit: Some(product_logging::framework::calculate_log_volume_size_limit( - &[ - MAX_OPA_BUNDLE_BUILDER_LOG_FILE_SIZE, - MAX_OPA_LOG_FILE_SIZE, - MAX_PREPARE_LOG_FILE_SIZE, - ], - )), - }) - .build(), - ) - .service_account_name(sa_name) - .security_context( - PodSecurityContextBuilder::new() - .run_as_user(1000) - .run_as_group(0) - .fs_group(1000) - .build(), - ); + .context(ObjectMetaSnafu)? + .build(); + + pb.metadata(pb_metadata) + .add_init_container(cb_prepare.build()) + .add_container(cb_opa.build()) + .add_container(cb_bundle_builder.build()) + .image_pull_secrets_from_product_image(resolved_product_image) + .affinity(&merged_config.affinity) + .add_volume( + VolumeBuilder::new(CONFIG_VOLUME_NAME) + .with_config_map(rolegroup_ref.object_name()) + .build(), + ) + .add_volume( + VolumeBuilder::new(BUNDLES_VOLUME_NAME) + .with_empty_dir(None::, None) + .build(), + ) + .add_volume( + VolumeBuilder::new(LOG_VOLUME_NAME) + .empty_dir(EmptyDirVolumeSource { + medium: None, + size_limit: Some(product_logging::framework::calculate_log_volume_size_limit( + &[ + MAX_OPA_BUNDLE_BUILDER_LOG_FILE_SIZE, + MAX_OPA_LOG_FILE_SIZE, + MAX_PREPARE_LOG_FILE_SIZE, + ], + )), + }) + .build(), + ) + .service_account_name(sa_name) + .security_context( + PodSecurityContextBuilder::new() + .run_as_user(1000) + .run_as_group(0) + .fs_group(1000) + .build(), + ); + + if let Some(user_info) = &opa.spec.cluster_config.user_info { + let mut cb_user_info_fetcher = + ContainerBuilder::new("user-info-fetcher").context(IllegalContainerNameSnafu)?; + + cb_user_info_fetcher + .image_from_product_image(resolved_product_image) // inherit the pull policy and pull secrets, and then... + .image(user_info_fetcher_image) // ...override the image + .command(vec!["stackable-opa-user-info-fetcher".to_string()]) + .add_env_var("CONFIG", format!("{CONFIG_DIR}/user-info-fetcher.json")) + .add_env_var("CREDENTIALS_DIR", USER_INFO_FETCHER_CREDENTIALS_DIR) + .add_volume_mount(CONFIG_VOLUME_NAME, CONFIG_DIR) + .resources( + ResourceRequirementsBuilder::new() + .with_cpu_request("100m") + .with_cpu_limit("200m") + .with_memory_request("128Mi") + .with_memory_limit("128Mi") + .build(), + ); + + match &user_info.backend { + user_info_fetcher::Backend::None {} => {} + user_info_fetcher::Backend::Keycloak(keycloak) => { + pb.add_volume( + VolumeBuilder::new(USER_INFO_FETCHER_CREDENTIALS_VOLUME_NAME) + .secret(SecretVolumeSource { + secret_name: Some(keycloak.client_credentials_secret.clone()), + ..Default::default() + }) + .build(), + ); + cb_user_info_fetcher.add_volume_mount( + USER_INFO_FETCHER_CREDENTIALS_VOLUME_NAME, + USER_INFO_FETCHER_CREDENTIALS_DIR, + ); + keycloak + .tls + .add_volumes_and_mounts(&mut pb, vec![&mut cb_user_info_fetcher]) + .context(VolumeAndMountsSnafu)?; + } + } + + pb.add_container(cb_user_info_fetcher.build()); + } if merged_config.logging.enable_vector_agent { pb.add_container(product_logging::framework::vector_container( @@ -772,32 +867,40 @@ fn build_server_rolegroup_daemonset( pod_template.merge_from(role.config.pod_overrides.clone()); pod_template.merge_from(role_group.config.pod_overrides.clone()); + let metadata = ObjectMetaBuilder::new() + .name_and_namespace(opa) + .name(&rolegroup_ref.object_name()) + .ownerreference_from_resource(opa, None, Some(true)) + .context(ObjectMissingMetadataForOwnerRefSnafu)? + .with_recommended_labels(build_recommended_labels( + opa, + &resolved_product_image.app_version_label, + &rolegroup_ref.role, + &rolegroup_ref.role_group, + )) + .context(ObjectMetaSnafu)? + .build(); + + let daemonset_match_labels = Labels::role_group_selector( + opa, + APP_NAME, + &rolegroup_ref.role, + &rolegroup_ref.role_group, + ) + .context(BuildLabelSnafu)?; + + let daemonset_spec = DaemonSetSpec { + selector: LabelSelector { + match_labels: Some(daemonset_match_labels.into()), + ..LabelSelector::default() + }, + template: pod_template, + ..DaemonSetSpec::default() + }; + Ok(DaemonSet { - metadata: ObjectMetaBuilder::new() - .name_and_namespace(opa) - .name(&rolegroup_ref.object_name()) - .ownerreference_from_resource(opa, None, Some(true)) - .context(ObjectMissingMetadataForOwnerRefSnafu)? - .with_recommended_labels(build_recommended_labels( - opa, - &resolved_product_image.app_version_label, - &rolegroup_ref.role, - &rolegroup_ref.role_group, - )) - .build(), - spec: Some(DaemonSetSpec { - selector: LabelSelector { - match_labels: Some(role_group_selector_labels( - opa, - APP_NAME, - &rolegroup_ref.role, - &rolegroup_ref.role_group, - )), - ..LabelSelector::default() - }, - template: pod_template, - ..DaemonSetSpec::default() - }), + metadata, + spec: Some(daemonset_spec), status: None, }) } diff --git a/rust/operator-binary/src/discovery.rs b/rust/operator-binary/src/discovery.rs index c8b186f5..433d061e 100644 --- a/rust/operator-binary/src/discovery.rs +++ b/rust/operator-binary/src/discovery.rs @@ -3,7 +3,7 @@ use crate::controller::{build_recommended_labels, APP_PORT}; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_opa_crd::{OpaCluster, OpaRole}; use stackable_operator::{ - builder::{ConfigMapBuilder, ObjectMetaBuilder}, + builder::{ConfigMapBuilder, ObjectMetaBuilder, ObjectMetaBuilderError}, commons::product_image_selection::ResolvedProductImage, k8s_openapi::api::core::v1::{ConfigMap, Service}, kube::{runtime::reflector::ObjectRef, Resource, ResourceExt}, @@ -16,14 +16,20 @@ pub enum Error { source: stackable_operator::error::Error, opa: ObjectRef, }, + #[snafu(display("object has no name associated"))] NoName, + #[snafu(display("object has no namespace associated"))] NoNamespace, + #[snafu(display("failed to build ConfigMap"))] BuildConfigMap { source: stackable_operator::error::Error, }, + + #[snafu(display("failed to build object meta data"))] + ObjectMeta { source: ObjectMetaBuilderError }, } /// Builds discovery [`ConfigMap`]s for connecting to a [`OpaCluster`] for all expected scenarios @@ -60,23 +66,25 @@ fn build_discovery_configmap( .context(NoNamespaceSnafu)?, APP_PORT ); + + let metadata = ObjectMetaBuilder::new() + .name_and_namespace(opa) + .name(name) + .ownerreference_from_resource(owner, None, Some(true)) + .with_context(|_| ObjectMissingMetadataForOwnerRefSnafu { + opa: ObjectRef::from_obj(opa), + })? + .with_recommended_labels(build_recommended_labels( + opa, + &resolved_product_image.app_version_label, + &OpaRole::Server.to_string(), + "discovery", + )) + .context(ObjectMetaSnafu)? + .build(); + ConfigMapBuilder::new() - .metadata( - ObjectMetaBuilder::new() - .name_and_namespace(opa) - .name(name) - .ownerreference_from_resource(owner, None, Some(true)) - .with_context(|_| ObjectMissingMetadataForOwnerRefSnafu { - opa: ObjectRef::from_obj(opa), - })? - .with_recommended_labels(build_recommended_labels( - opa, - &resolved_product_image.app_version_label, - &OpaRole::Server.to_string(), - "discovery", - )) - .build(), - ) + .metadata(metadata) .add_data("OPA", url) .build() .context(BuildConfigMapSnafu) diff --git a/rust/operator-binary/src/main.rs b/rust/operator-binary/src/main.rs index 96782fa0..b5552cfc 100644 --- a/rust/operator-binary/src/main.rs +++ b/rust/operator-binary/src/main.rs @@ -46,6 +46,10 @@ struct OpaRun { #[clap(long, env)] opa_bundle_builder_clusterrole: String, + /// The full image tag of the operator, used to deploy the user_info_fetcher. + #[clap(long, env)] + operator_image: String, + #[clap(flatten)] common: ProductOperatorRun, } @@ -55,10 +59,11 @@ async fn main() -> Result<(), error::Error> { let opts = Opts::parse(); match opts.cmd { Command::Crd => { - OpaCluster::print_yaml_schema()?; + OpaCluster::print_yaml_schema(built_info::CARGO_PKG_VERSION)?; } Command::Run(OpaRun { opa_bundle_builder_clusterrole: opa_builder_clusterrole, + operator_image, common: ProductOperatorRun { product_config, @@ -91,6 +96,7 @@ async fn main() -> Result<(), error::Error> { product_config, watch_namespace, opa_builder_clusterrole, + operator_image, ) .await?; } @@ -107,6 +113,7 @@ async fn create_controller( product_config: ProductConfigManager, watch_namespace: WatchNamespace, opa_bundle_builder_clusterrole: String, + user_info_fetcher_image: String, ) -> OperatorResult<()> { let opa_api: Api = watch_namespace.get_api(&client); let daemonsets_api: Api = watch_namespace.get_api(&client); @@ -126,6 +133,7 @@ async fn create_controller( client: client.clone(), product_config, opa_bundle_builder_clusterrole, + user_info_fetcher_image, }), ) .map(|res| { diff --git a/rust/user-info-fetcher/Cargo.toml b/rust/user-info-fetcher/Cargo.toml new file mode 100644 index 00000000..c27ddd95 --- /dev/null +++ b/rust/user-info-fetcher/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "stackable-opa-user-info-fetcher" +description = "Fetches user metadata from directory services" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +publish = false + +[dependencies] +stackable-opa-crd = { path = "../crd" } + +axum.workspace = true +clap.workspace = true +futures.workspace = true +hyper.workspace = true +moka.workspace = true +pin-project.workspace = true +reqwest.workspace = true +semver.workspace = true +serde_json.workspace = true +serde.workspace = true +snafu.workspace = true +stackable-operator.workspace = true +tokio.workspace = true +tracing.workspace = true +url.workspace = true diff --git a/rust/user-info-fetcher/README.md b/rust/user-info-fetcher/README.md new file mode 100644 index 00000000..6303e308 --- /dev/null +++ b/rust/user-info-fetcher/README.md @@ -0,0 +1,10 @@ +# User info fetcher + +Fetches user metadata from a directory service, and exposes it in a normalized format for OPA rules to read. + +It is deployed by the Stackable OPA Operator, and is not recommended for standalone use. + +## Supported backends + +- `none` - Dummy backend +- `keycloak` - Keycloak diff --git a/rust/user-info-fetcher/src/backend/keycloak.rs b/rust/user-info-fetcher/src/backend/keycloak.rs new file mode 100644 index 00000000..878167a9 --- /dev/null +++ b/rust/user-info-fetcher/src/backend/keycloak.rs @@ -0,0 +1,195 @@ +use std::collections::HashMap; + +use hyper::StatusCode; +use serde::Deserialize; +use snafu::{OptionExt, ResultExt, Snafu}; +use stackable_opa_crd::user_info_fetcher as crd; +use stackable_operator::commons::authentication::oidc; + +use crate::{http_error, util::send_json_request, Credentials, UserInfo, UserInfoRequest}; + +#[derive(Snafu, Debug)] +pub enum Error { + #[snafu(display("failed to get access_token"))] + AccessToken { source: reqwest::Error }, + + #[snafu(display("failed to search for user"))] + SearchForUser { source: reqwest::Error }, + + #[snafu(display("unable to find user with id {user_id:?}"))] + UserNotFoundById { + source: reqwest::Error, + user_id: String, + }, + + #[snafu(display("unable to find user with username {username:?}"))] + UserNotFoundByName { username: String }, + + #[snafu(display("more than one user was returned when there should be one or none"))] + TooManyUsersReturned, + + #[snafu(display( + "failed to request groups for user with username {username:?} (user_id: {user_id:?})" + ))] + RequestUserGroups { + username: String, + user_id: String, + source: reqwest::Error, + }, + + #[snafu(display("failed to parse OIDC endpoint url"))] + ParseOidcEndpointUrl { source: oidc::Error }, + + #[snafu(display("failed to construct OIDC endpoint path"))] + ConstructOidcEndpointPath { source: url::ParseError }, +} + +impl http_error::Error for Error { + fn status_code(&self) -> StatusCode { + match self { + Self::AccessToken { .. } => StatusCode::BAD_GATEWAY, + Self::SearchForUser { .. } => StatusCode::BAD_GATEWAY, + Self::UserNotFoundById { .. } => StatusCode::NOT_FOUND, + Self::UserNotFoundByName { .. } => StatusCode::NOT_FOUND, + Self::TooManyUsersReturned {} => StatusCode::INTERNAL_SERVER_ERROR, + Self::RequestUserGroups { .. } => StatusCode::BAD_GATEWAY, + Self::ParseOidcEndpointUrl { .. } => StatusCode::INTERNAL_SERVER_ERROR, + Self::ConstructOidcEndpointPath { .. } => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + +#[derive(Deserialize)] +struct OAuthResponse { + access_token: String, +} + +/// The minimal structure of [UserRepresentation] that is returned by [`/users`][users] and [`/users/{id}`][user-by-id]. +///
Some fields, such as `groups` are never present. See [keycloak/keycloak#20292][issue-20292]
+/// +/// [users]: https://www.keycloak.org/docs-api/22.0.1/rest-api/index.html#_get_adminrealmsrealmusers +/// [user-by-id]: https://www.keycloak.org/docs-api/22.0.1/rest-api/index.html#_get_adminrealmsrealmusersid +/// [UserRepresentation]: https://www.keycloak.org/docs-api/22.0.1/rest-api/index.html#UserRepresentation +/// [issue-20292]: https://github.com/keycloak/keycloak/issues/20294 +#[derive(Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +struct UserMetadata { + id: String, + username: String, + #[serde(default)] + attributes: HashMap>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GroupMembership { + path: String, +} + +pub(crate) async fn get_user_info( + req: &UserInfoRequest, + http: &reqwest::Client, + credentials: &Credentials, + config: &crd::KeycloakBackend, +) -> Result { + let crd::KeycloakBackend { + client_credentials_secret: _, + admin_realm, + user_realm, + hostname, + port, + root_path, + tls, + } = config; + + // We re-use existent functionality from operator-rs, besides it being a bit of miss-use. + // Some attributes (such as principal_claim) are irrelevant, and will not be read by the code-flow we trigger. + let wrapping_auth_provider = oidc::AuthenticationProvider::new( + hostname.clone(), + *port, + root_path.clone(), + tls.clone(), + String::new(), + Vec::new(), + None, + ); + let keycloak_url = wrapping_auth_provider + .endpoint_url() + .context(ParseOidcEndpointUrlSnafu)?; + + let authn = send_json_request::( + http.post( + keycloak_url + .join(&format!( + "realms/{admin_realm}/protocol/openid-connect/token" + )) + .context(ConstructOidcEndpointPathSnafu)?, + ) + .basic_auth(&credentials.client_id, Some(&credentials.client_secret)) + .form(&[("grant_type", "client_credentials")]), + ) + .await + .context(AccessTokenSnafu)?; + + let users_base_url = keycloak_url + .join(&format!("admin/realms/{user_realm}/users/")) + .context(ConstructOidcEndpointPathSnafu)?; + + let user_info = match req { + UserInfoRequest::UserInfoRequestById(req) => { + let user_id = req.id.clone(); + send_json_request::( + http.get( + users_base_url + .join(&req.id) + .context(ConstructOidcEndpointPathSnafu)?, + ) + .bearer_auth(&authn.access_token), + ) + .await + .context(UserNotFoundByIdSnafu { user_id })? + } + UserInfoRequest::UserInfoRequestByName(req) => { + let username = &req.username; + let users_url = users_base_url + .join(&format!("?username={username}&exact=true")) + .context(ConstructOidcEndpointPathSnafu)?; + + let users = send_json_request::>( + http.get(users_url).bearer_auth(&authn.access_token), + ) + .await + .context(SearchForUserSnafu)?; + + if users.len() > 1 { + return TooManyUsersReturnedSnafu.fail(); + } + + users + .first() + .cloned() + .context(UserNotFoundByNameSnafu { username })? + } + }; + + let groups = send_json_request::>( + http.get( + users_base_url + .join(&format!("{}/groups", user_info.id)) + .context(ConstructOidcEndpointPathSnafu)?, + ) + .bearer_auth(&authn.access_token), + ) + .await + .context(RequestUserGroupsSnafu { + username: user_info.username.clone(), + user_id: user_info.id.clone(), + })?; + + Ok(UserInfo { + id: Some(user_info.id), + username: Some(user_info.username), + groups: groups.into_iter().map(|g| g.path).collect(), + custom_attributes: user_info.attributes, + }) +} diff --git a/rust/user-info-fetcher/src/backend/mod.rs b/rust/user-info-fetcher/src/backend/mod.rs new file mode 100644 index 00000000..94a7ca84 --- /dev/null +++ b/rust/user-info-fetcher/src/backend/mod.rs @@ -0,0 +1 @@ +pub mod keycloak; diff --git a/rust/user-info-fetcher/src/http_error.rs b/rust/user-info-fetcher/src/http_error.rs new file mode 100644 index 00000000..a7cd51bf --- /dev/null +++ b/rust/user-info-fetcher/src/http_error.rs @@ -0,0 +1,55 @@ +use std::sync::Arc; + +use axum::{response::IntoResponse, Json}; +use hyper::StatusCode; +use serde::Serialize; + +pub trait Error: std::error::Error { + fn status_code(&self) -> StatusCode; +} +impl Error for Arc { + fn status_code(&self) -> StatusCode { + let inner: &T = self; + inner.status_code() + } +} + +pub struct JsonResponse { + pub error: E, +} + +impl From for JsonResponse { + fn from(error: E) -> Self { + Self { error } + } +} + +impl IntoResponse for JsonResponse { + fn into_response(self) -> axum::response::Response { + ( + self.error.status_code(), + Json(Container { + error: Payload { + message: self.error.to_string(), + causes: std::iter::successors(self.error.source(), |err| err.source()) + .map(|err| err.to_string()) + .collect(), + }, + }), + ) + .into_response() + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Container { + error: Payload, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Payload { + message: String, + causes: Vec, +} diff --git a/rust/user-info-fetcher/src/main.rs b/rust/user-info-fetcher/src/main.rs new file mode 100644 index 00000000..db924c82 --- /dev/null +++ b/rust/user-info-fetcher/src/main.rs @@ -0,0 +1,267 @@ +use std::{ + collections::HashMap, + net::AddrParseError, + path::{Path, PathBuf}, + sync::Arc, +}; + +use axum::{extract::State, routing::post, Json, Router}; +use clap::Parser; +use futures::{future, pin_mut, FutureExt}; +use moka::future::Cache; +use reqwest::ClientBuilder; +use serde::{Deserialize, Serialize}; +use snafu::{ResultExt, Snafu}; +use stackable_opa_crd::user_info_fetcher as crd; +use tokio::{fs::File, io::AsyncReadExt}; + +mod backend; +mod http_error; +mod util; + +pub const APP_NAME: &str = "opa-user-info-fetcher"; + +#[derive(clap::Parser)] +pub struct Args { + #[clap(long, env)] + config: PathBuf, + #[clap(long, env)] + credentials_dir: PathBuf, + #[clap(flatten)] + common: stackable_operator::cli::ProductOperatorRun, +} + +#[derive(Clone)] +struct AppState { + config: Arc, + http: reqwest::Client, + credentials: Arc, + user_info_cache: Cache, +} + +struct Credentials { + // TODO: Find a better way of sharing behavior between different backends + client_id: String, + client_secret: String, +} + +#[derive(Snafu, Debug)] +enum StartupError { + #[snafu(display("unable to read config file from {path:?}"))] + ReadConfigFile { + source: std::io::Error, + path: PathBuf, + }, + + #[snafu(display("failed to parse config file"))] + ParseConfig { source: serde_json::Error }, + + #[snafu(display("failed to parse listen address"))] + ParseListenAddr { source: AddrParseError }, + + #[snafu(display("failed to register SIGTERM handler"))] + RegisterSigterm { source: std::io::Error }, + + #[snafu(display("failed to run server"))] + RunServer { source: hyper::Error }, + + #[snafu(display("failed to construct http client"))] + ConstructHttpClient { source: reqwest::Error }, + + #[snafu(display("failed to open ca certificate"))] + OpenCaCert { source: std::io::Error }, + + #[snafu(display("failed to read ca certificate"))] + ReadCaCert { source: std::io::Error }, + + #[snafu(display("failed to parse ca certificate"))] + ParseCaCert { source: reqwest::Error }, +} + +async fn read_config_file(path: &Path) -> Result { + tokio::fs::read_to_string(path) + .await + .context(ReadConfigFileSnafu { path }) +} + +#[tokio::main] +async fn main() -> Result<(), StartupError> { + let args = Args::parse(); + + stackable_operator::logging::initialize_logging( + "OPA_OPERATOR_LOG", + APP_NAME, + args.common.tracing_target, + ); + + let shutdown_requested = tokio::signal::ctrl_c().map(|_| ()); + #[cfg(unix)] + let shutdown_requested = { + let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) + .context(RegisterSigtermSnafu)?; + async move { + let sigterm = sigterm.recv().map(|_| ()); + pin_mut!(shutdown_requested, sigterm); + future::select(shutdown_requested, sigterm).await; + } + }; + + let config = Arc::::new( + serde_json::from_str(&read_config_file(&args.config).await?).context(ParseConfigSnafu)?, + ); + let credentials = Arc::new(match &config.backend { + // TODO: factor this out into each backend (e.g. when we add LDAP support) + crd::Backend::None {} => Credentials { + client_id: "".to_string(), + client_secret: "".to_string(), + }, + crd::Backend::Keycloak(_) => Credentials { + client_id: read_config_file(&args.credentials_dir.join("clientId")).await?, + client_secret: read_config_file(&args.credentials_dir.join("clientSecret")).await?, + }, + }); + + let mut client_builder = ClientBuilder::new(); + + // TODO: I'm not so sure we should be doing all this keycloak specific stuff here. + // We could factor it out in the provider specific implementation (e.g. when we add LDAP support). + // I know it is for setting up the client, but an idea: make a trait for implementing backends + // The trait can do all this for a genric client using an implementation on the trait (eg: get_http_client() which will call self.uses_tls()) + if let crd::Backend::Keycloak(keycloak) = &config.backend { + if keycloak.tls.uses_tls() && !keycloak.tls.uses_tls_verification() { + client_builder = client_builder.danger_accept_invalid_certs(true); + } + if let Some(tls_ca_cert_mount_path) = keycloak.tls.tls_ca_cert_mount_path() { + let mut buf = Vec::new(); + File::open(tls_ca_cert_mount_path) + .await + .context(OpenCaCertSnafu)? + .read_to_end(&mut buf) + .await + .context(ReadCaCertSnafu)?; + let ca_cert = reqwest::Certificate::from_pem(&buf).context(ParseCaCertSnafu)?; + + client_builder = client_builder + .tls_built_in_root_certs(false) + .add_root_certificate(ca_cert); + } + } + let http = client_builder.build().context(ConstructHttpClientSnafu)?; + + let user_info_cache = { + let crd::Cache { entry_time_to_live } = config.cache; + Cache::builder() + .name("user-info") + .time_to_live(*entry_time_to_live) + .build() + }; + let app = Router::new() + .route("/user", post(get_user_info)) + .with_state(AppState { + config, + http, + credentials, + user_info_cache, + }); + axum::Server::bind(&"127.0.0.1:9476".parse().context(ParseListenAddrSnafu)?) + .serve(app.into_make_service()) + .with_graceful_shutdown(shutdown_requested) + .await + .context(RunServerSnafu) +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone)] +#[serde(rename_all = "camelCase", untagged)] +enum UserInfoRequest { + UserInfoRequestById(UserInfoRequestById), + UserInfoRequestByName(UserInfoRequestByName), +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone)] +#[serde(rename_all = "camelCase")] +struct UserInfoRequestById { + id: String, +} + +#[derive(Debug, Deserialize, PartialEq, Eq, Hash, Clone)] +#[serde(rename_all = "camelCase")] +struct UserInfoRequestByName { + username: String, +} + +#[derive(Serialize, Clone, Debug, Default)] +#[serde(rename_all = "camelCase")] +struct UserInfo { + /// This might be null in case the id is not known (e.g. the backend does not have this info). + id: Option, + /// This might be null in case the username is not known (e.g. the backend does not have this info). + username: Option, + groups: Vec, + custom_attributes: HashMap>, +} + +#[derive(Snafu, Debug)] +#[snafu(module)] +enum GetUserInfoError { + #[snafu(display("failed to get user information from Keycloak"))] + Keycloak { source: backend::keycloak::Error }, +} + +impl http_error::Error for GetUserInfoError { + fn status_code(&self) -> hyper::StatusCode { + // todo: the warn here loses context about the scope in which the error occurred, eg: stackable_opa_user_info_fetcher::backend::keycloak + // Also, we should make the log level (warn vs error) more dynamic in the backend's impl `http_error::Error for Error` + tracing::warn!( + error = self as &dyn std::error::Error, + "Error while processing request" + ); + match self { + Self::Keycloak { source } => source.status_code(), + } + } +} + +async fn get_user_info( + State(state): State, + Json(req): Json, +) -> Result, http_error::JsonResponse>> { + let AppState { + config, + http, + credentials, + user_info_cache, + } = state; + Ok(Json( + user_info_cache + .try_get_with_by_ref(&req, async { + match &config.backend { + crd::Backend::None {} => { + let user_id = match &req { + UserInfoRequest::UserInfoRequestById(UserInfoRequestById { id }) => { + Some(id) + } + _ => None, + }; + let username = match &req { + UserInfoRequest::UserInfoRequestByName(UserInfoRequestByName { + username, + }) => Some(username), + _ => None, + }; + Ok(UserInfo { + id: user_id.cloned(), + username: username.cloned(), + groups: vec![], + custom_attributes: HashMap::new(), + }) + } + crd::Backend::Keycloak(keycloak) => { + backend::keycloak::get_user_info(&req, &http, &credentials, keycloak) + .await + .context(get_user_info_error::KeycloakSnafu) + } + } + }) + .await?, + )) +} diff --git a/rust/user-info-fetcher/src/util.rs b/rust/user-info-fetcher/src/util.rs new file mode 100644 index 00000000..25189af9 --- /dev/null +++ b/rust/user-info-fetcher/src/util.rs @@ -0,0 +1,8 @@ +use reqwest::RequestBuilder; +use serde::de::DeserializeOwned; + +pub async fn send_json_request( + req: RequestBuilder, +) -> Result { + req.send().await?.error_for_status()?.json().await +} diff --git a/tests/templates/kuttl/keycloak-user-info/00-limit-range.yaml b/tests/templates/kuttl/keycloak-user-info/00-limit-range.yaml new file mode 100644 index 00000000..7b6cb30e --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/00-limit-range.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: LimitRange +metadata: + name: limit-request-ratio +spec: + limits: + - type: "Container" + maxLimitRequestRatio: + cpu: 5 + memory: 1 diff --git a/tests/templates/kuttl/keycloak-user-info/01-assert.yaml.j2 b/tests/templates/kuttl/keycloak-user-info/01-assert.yaml.j2 new file mode 100644 index 00000000..50b1d4c3 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/01-assert.yaml.j2 @@ -0,0 +1,10 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +{% if lookup('env', 'VECTOR_AGGREGATOR') %} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: vector-aggregator-discovery +{% endif %} diff --git a/tests/templates/kuttl/keycloak-user-info/01-install-vector-aggregator-discovery-configmap.yaml.j2 b/tests/templates/kuttl/keycloak-user-info/01-install-vector-aggregator-discovery-configmap.yaml.j2 new file mode 100644 index 00000000..2d6a0df5 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/01-install-vector-aggregator-discovery-configmap.yaml.j2 @@ -0,0 +1,9 @@ +{% if lookup('env', 'VECTOR_AGGREGATOR') %} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: vector-aggregator-discovery +data: + ADDRESS: {{ lookup('env', 'VECTOR_AGGREGATOR') }} +{% endif %} diff --git a/tests/templates/kuttl/keycloak-user-info/02-assert.yaml b/tests/templates/kuttl/keycloak-user-info/02-assert.yaml new file mode 100644 index 00000000..943a1340 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/02-assert.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 300 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: keycloak +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/keycloak-user-info/02-install-keycloak.yaml.j2 b/tests/templates/kuttl/keycloak-user-info/02-install-keycloak.yaml.j2 new file mode 100644 index 00000000..b959ac46 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/02-install-keycloak.yaml.j2 @@ -0,0 +1,117 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + kubectl apply -n $NAMESPACE -f - << EOF + --- + apiVersion: secrets.stackable.tech/v1alpha1 + kind: SecretClass + metadata: + name: keycloak-tls-$NAMESPACE + spec: + backend: + autoTls: + ca: + autoGenerate: true + secret: + name: keycloak-tls-ca-$NAMESPACE + namespace: $NAMESPACE + --- + apiVersion: apps/v1 + kind: Deployment + metadata: + name: keycloak + labels: + app: keycloak + spec: + replicas: 1 + selector: + matchLabels: + app: keycloak + template: + metadata: + labels: + app: keycloak + spec: + containers: + - name: keycloak + image: quay.io/keycloak/keycloak:{{ test_scenario['values']['keycloak'] }} + args: + - start + - --hostname-strict=false + - --https-key-store-file=/tls/keystore.p12 + - --https-key-store-password=changeit + env: + - name: KEYCLOAK_ADMIN + value: admin + - name: KEYCLOAK_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-admin-credentials + key: admin + ports: + - name: https + containerPort: 8443 + readinessProbe: + httpGet: + scheme: HTTPS + path: /realms/master + port: https + resources: + limits: + cpu: 1 + memory: 512Mi + requests: + cpu: 500m + memory: 512Mi + volumeMounts: + - name: data + mountPath: /opt/keycloak/data/ + - name: tls + mountPath: /tls/ + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsUser: 1000 + volumes: + - name: data + emptyDir: {} + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: keycloak-tls-$NAMESPACE + secrets.stackable.tech/format: tls-pkcs12 + secrets.stackable.tech/format.compatibility.tls-pkcs12.password: changeit + secrets.stackable.tech/scope: service=keycloak,node + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" + --- + apiVersion: v1 + kind: Secret + metadata: + name: keycloak-admin-credentials + stringData: + admin: "adminadmin" + --- + apiVersion: v1 + kind: Service + metadata: + name: keycloak + labels: + app: keycloak + spec: + ports: + - name: https + port: 8443 + targetPort: 8443 + selector: + app: keycloak + EOF diff --git a/tests/templates/kuttl/keycloak-user-info/03-assert.yaml b/tests/templates/kuttl/keycloak-user-info/03-assert.yaml new file mode 100644 index 00000000..1c8d3401 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/03-assert.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 600 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: setup-keycloak +status: + succeeded: 1 diff --git a/tests/templates/kuttl/keycloak-user-info/03-setup-keycloak.yaml.j2 b/tests/templates/kuttl/keycloak-user-info/03-setup-keycloak.yaml.j2 new file mode 100644 index 00000000..b4124a60 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/03-setup-keycloak.yaml.j2 @@ -0,0 +1,140 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + kubectl apply -n $NAMESPACE -f - << EOF + --- + apiVersion: v1 + kind: Secret + metadata: + name: keycloak-users + stringData: + alice: "alicealice" + bob: "bobbob" + --- + apiVersion: v1 + kind: Secret + metadata: + name: user-info-fetcher-client-credentials + stringData: + clientId: user-info-fetcher + clientSecret: user-info-fetcher-client-secret + --- + apiVersion: batch/v1 + kind: Job + metadata: + name: setup-keycloak + spec: + backoffLimit: 0 + template: + spec: + restartPolicy: Never + containers: + - name: setup-keycloak + image: docker.stackable.tech/stackable/testing-tools:0.2.0-stackable0.0.0-dev + env: + - name: KEYCLOAK_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-admin-credentials + key: admin + - name: ALICE_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-users + key: alice + - name: BOB_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak-users + key: bob + - name: USER_INFO_FETCHER_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: user-info-fetcher-client-credentials + key: clientSecret + command: + - bash + - -x + - -euo + - pipefail + - -c + - | + COMMON_FLAGS="--config kcadm.conf --truststore /tls/truststore.p12 --trustpass changeit" + + echo "Configuring credentials" + kcadm.sh config credentials \$COMMON_FLAGS --server https://keycloak.$NAMESPACE.svc.cluster.local:8443 --realm master --user admin --password "\$KEYCLOAK_ADMIN_PASSWORD" + + echo "Adding users" + ALICE_UUID=\$(kcadm.sh create users \$COMMON_FLAGS -s username=alice -s firstName=Alice -s lastName=Adams -s email=alice@mycorp.org -s enabled=true -o | jq -r .id) + BOB_UUID=\$( kcadm.sh create users \$COMMON_FLAGS -s username=bob -s firstName=Bob -s lastName=Bush -s email=bob@mycorp.org -s enabled=true -o | jq -r .id) + + echo "Setting user passwords" + kcadm.sh set-password \$COMMON_FLAGS --username alice --new-password "\$ALICE_PASSWORD" + kcadm.sh set-password \$COMMON_FLAGS --username bob --new-password "\$BOB_PASSWORD" + + echo "Creating the superset-admin group" + SUPERSET_ADMIN_GROUP_UUID=\$(kcadm.sh create groups \$COMMON_FLAGS -s name=superset-admin -o | jq -r .id) + echo "Adding alice to the superset-admin group" + kcadm.sh update \$COMMON_FLAGS "users/\$ALICE_UUID/groups/\$SUPERSET_ADMIN_GROUP_UUID" + + echo "Creating user-info-fetcher client" + kcadm.sh create clients \$COMMON_FLAGS -f - << EOF || true + { + "clientId": "user-info-fetcher", + "enabled": true, + "protocol": "openid-connect", + "clientAuthenticatorType": "client-secret", + "secret": "\$USER_INFO_FETCHER_CLIENT_SECRET", + "redirectUris": [ + "*" + ], + "webOrigins": [ + "*" + ], + "publicClient": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "attributes": { + "oidc.ciba.grant.enabled": "true", + "oauth2.device.authorization.grant.enabled": false + } + } + EOF + + kcadm.sh add-roles --uusername service-account-user-info-fetcher --rolename admin \$COMMON_FLAGS + resources: + limits: + cpu: 1 + memory: 256Mi + requests: + cpu: 200m + memory: 256Mi + volumeMounts: + - name: tls + mountPath: /tls + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsUser: 1000 + volumes: + - name: tls + ephemeral: + volumeClaimTemplate: + metadata: + annotations: + secrets.stackable.tech/class: keycloak-tls-$NAMESPACE + secrets.stackable.tech/format: tls-pkcs12 + secrets.stackable.tech/format.compatibility.tls-pkcs12.password: changeit + secrets.stackable.tech/scope: pod + spec: + storageClassName: secrets.stackable.tech + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "1" + EOF diff --git a/tests/templates/kuttl/keycloak-user-info/10-assert.yaml b/tests/templates/kuttl/keycloak-user-info/10-assert.yaml new file mode 100644 index 00000000..819abc51 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/10-assert.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +metadata: + name: install-opa +timeout: 300 +commands: + - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 301s diff --git a/tests/templates/kuttl/keycloak-user-info/10-install-opa.yaml.j2 b/tests/templates/kuttl/keycloak-user-info/10-install-opa.yaml.j2 new file mode 100644 index 00000000..1f283835 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/10-install-opa.yaml.j2 @@ -0,0 +1,54 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + kubectl apply -n $NAMESPACE -f - < 0 }} + roleGroups: + default: {} diff --git a/tests/templates/kuttl/keycloak-user-info/20-assert.yaml b/tests/templates/kuttl/keycloak-user-info/20-assert.yaml new file mode 100644 index 00000000..7912d1c5 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/20-assert.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +metadata: + name: test-regorule +timeout: 300 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: test-regorule +status: + readyReplicas: 1 + replicas: 1 diff --git a/tests/templates/kuttl/keycloak-user-info/20-install-test-regorule.yaml b/tests/templates/kuttl/keycloak-user-info/20-install-test-regorule.yaml new file mode 100644 index 00000000..f6770ed2 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/20-install-test-regorule.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: test-regorule + labels: + app: test-regorule +spec: + replicas: 1 + selector: + matchLabels: + app: test-regorule + template: + metadata: + labels: + app: test-regorule + spec: + containers: + - name: test-regorule + image: docker.stackable.tech/stackable/testing-tools:0.2.0-stackable0.0.0-dev + stdin: true + tty: true + resources: + requests: + memory: "128Mi" + cpu: "512m" + limits: + memory: "128Mi" + cpu: "1" diff --git a/tests/templates/kuttl/keycloak-user-info/30-assert.yaml b/tests/templates/kuttl/keycloak-user-info/30-assert.yaml new file mode 100644 index 00000000..0d0ef28f --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/30-assert.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +metadata: + name: test-regorule +commands: + - script: kubectl exec -n $NAMESPACE test-regorule-0 -- python /tmp/test-regorule.py -u 'http://test-opa-server-default:8081/v1/data/test' diff --git a/tests/templates/kuttl/keycloak-user-info/30-prepare-test-regorule.yaml b/tests/templates/kuttl/keycloak-user-info/30-prepare-test-regorule.yaml new file mode 100644 index 00000000..09ce40e1 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/30-prepare-test-regorule.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: kubectl cp -n $NAMESPACE ./test-regorule.py test-regorule-0:/tmp diff --git a/tests/templates/kuttl/keycloak-user-info/test-regorule.py b/tests/templates/kuttl/keycloak-user-info/test-regorule.py new file mode 100755 index 00000000..829ab507 --- /dev/null +++ b/tests/templates/kuttl/keycloak-user-info/test-regorule.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +import requests +import argparse +import json + +# todo: make the test more comprehensive to check customAttributes +users_and_groups = { + "alice": ["/superset-admin"], + "bob": [], +} + + +def assertions(username, response, opa_attribute, expected_groups, expected_attributes={}): + assert "result" in response + assert opa_attribute in response["result"] + + # repeated the right hand side for better output on error + assert "customAttributes" in response["result"][opa_attribute] + assert "groups" in response["result"][opa_attribute] + assert "id" in response["result"][opa_attribute] + assert "username" in response["result"][opa_attribute] + + # todo: split out group assertions + print(f"Testing for {username} in groups {expected_groups}") + groups = sorted(response["result"][opa_attribute]["groups"]) + expected_groups = sorted(expected_groups) + assert groups == expected_groups, f"got {groups}, expected: {expected_groups}" + + # todo: split out customAttribute assertions + print(f"Testing for {username} with customAttributes {expected_attributes}") + custom_attributes = response["result"][opa_attribute]["customAttributes"] + assert custom_attributes == expected_attributes, f"got {custom_attributes}, expected: {expected_attributes}" + + +if __name__ == "__main__": + all_args = argparse.ArgumentParser() + all_args.add_argument("-u", "--url", required=True, help="OPA service url") + args = vars(all_args.parse_args()) + params = {'strict-builtin-errors': 'true'} + + def make_request(payload): + return requests.post(args['url'], data=json.dumps(payload), params=params).json() + + for username, groups in users_and_groups.items(): + try: + # todo: try this out locally until it works + # url = 'http://test-opa-svc:8081/v1/data' + payload = {'input': {'username': username}} + response = make_request(payload) + assertions(username, response, "currentUserInfoByUsername", groups, {}) + + # do the reverse lookup + user_id = response["result"]["currentUserInfoByUsername"]["id"] + payload = {'input': {'id': user_id}} + response = make_request(payload) + assertions(username, response, "currentUserInfoById", groups, {}) + except Exception as e: + if response is not None: + print(f"something went wrong. last response: {response}") + raise e + + print("Test successful!") diff --git a/tests/templates/kuttl/smoke/10-assert.yaml.j2 b/tests/templates/kuttl/smoke/10-assert.yaml.j2 index 1e492864..6b86f13f 100644 --- a/tests/templates/kuttl/smoke/10-assert.yaml.j2 +++ b/tests/templates/kuttl/smoke/10-assert.yaml.j2 @@ -1,7 +1,5 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -metadata: - name: install-opa timeout: 300 commands: - script: kubectl -n $NAMESPACE wait --for=condition=available opaclusters.opa.stackable.tech/test-opa --timeout 301s diff --git a/tests/templates/kuttl/smoke/20-assert.yaml b/tests/templates/kuttl/smoke/20-assert.yaml index 7912d1c5..6041b967 100644 --- a/tests/templates/kuttl/smoke/20-assert.yaml +++ b/tests/templates/kuttl/smoke/20-assert.yaml @@ -1,8 +1,6 @@ --- apiVersion: kuttl.dev/v1beta1 kind: TestAssert -metadata: - name: test-regorule timeout: 300 --- apiVersion: apps/v1 diff --git a/tests/test-definition.yaml b/tests/test-definition.yaml index 102ed396..ddfdd079 100644 --- a/tests/test-definition.yaml +++ b/tests/test-definition.yaml @@ -7,6 +7,9 @@ dimensions: - name: opa-latest values: - 0.57.0 + - name: keycloak + values: + - 23.0.1 tests: - name: smoke dimensions: @@ -20,6 +23,10 @@ tests: - name: cluster-operation dimensions: - opa-latest + - name: keycloak-user-info + dimensions: + - opa-latest + - keycloak suites: - name: nightly patch: