diff --git a/Cargo.lock b/Cargo.lock index 9cfc5054..29a1ec67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,7 +353,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", "itoa", @@ -381,7 +381,7 @@ dependencies = [ "axum-macros", "bytes", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.2.0", @@ -414,7 +414,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "mime", "rustversion", @@ -431,7 +431,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "mime", @@ -464,7 +464,7 @@ dependencies = [ "arc-swap", "bytes", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.2.0", @@ -789,10 +789,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.88" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" dependencies = [ + "jobserver", "libc", ] @@ -810,9 +811,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", @@ -858,18 +859,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -939,6 +940,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -1424,9 +1434,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "docker_credential" -version = "1.2.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8765d595e4f1c66eb5b94450209b316516366d403984664efda0d9b28a55ff9e" +checksum = "1e3839d8c19b1fa5c2f1ad6d6c6cd15ea0d8536d955673d78dadd46271fe8afc" dependencies = [ "base64 0.21.7", "serde", @@ -1641,6 +1651,24 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flagset" version = "0.4.5" @@ -1896,7 +1924,7 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -1957,7 +1985,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.11", + "http 0.2.12", "indexmap 2.2.5", "slab", "tokio", @@ -1976,7 +2004,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 1.0.0", + "http 1.1.0", "indexmap 2.2.5", "slab", "tokio", @@ -2056,9 +2084,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -2067,9 +2095,9 @@ dependencies = [ [[package]] name = "http" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -2092,7 +2120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http 0.2.11", + "http 0.2.12", "pin-project-lite", ] @@ -2103,7 +2131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.0.0", + "http 1.1.0", ] [[package]] @@ -2114,7 +2142,7 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "pin-project-lite", ] @@ -2157,7 +2185,7 @@ dependencies = [ "futures-core", "futures-util", "h2 0.3.24", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", @@ -2180,7 +2208,7 @@ dependencies = [ "futures-channel", "futures-util", "h2 0.4.2", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "httparse", "httpdate", @@ -2197,7 +2225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http 0.2.11", + "http 0.2.12", "hyper 0.14.28", "log", "rustls 0.21.10", @@ -2226,7 +2254,7 @@ checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "hyper 1.2.0", "pin-project-lite", @@ -2378,6 +2406,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -2435,11 +2472,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -2530,7 +2576,7 @@ dependencies = [ "either", "futures", "home", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", "hyper-rustls", @@ -2562,7 +2608,7 @@ checksum = "a6b42844e9172f631b8263ea9ce003b9251da13beb1401580937ad206dd82f4c" dependencies = [ "chrono", "form_urlencoded", - "http 0.2.11", + "http 0.2.12", "json-patch", "k8s-openapi", "once_cell", @@ -2771,6 +2817,15 @@ dependencies = [ "rustix 0.38.31", ] +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -2855,12 +2910,29 @@ dependencies = [ "syn 2.0.52", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "ndk-context" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3001,7 +3073,7 @@ dependencies = [ "base64 0.13.1", "chrono", "getrandom", - "http 0.2.11", + "http 0.2.12", "rand", "reqwest", "serde", @@ -3041,7 +3113,7 @@ checksum = "2ac5b780ce1bd6c3c2ff72a3013f4b2d56d53ae03b20d424e99d2f6556125138" dependencies = [ "futures", "futures-util", - "http 0.2.11", + "http 0.2.12", "http-auth", "jwt", "lazy_static", @@ -3067,7 +3139,7 @@ dependencies = [ "bytes", "chrono", "futures-util", - "http 0.2.11", + "http 0.2.12", "http-auth", "jwt", "lazy_static", @@ -3139,7 +3211,7 @@ dependencies = [ "dyn-clone", "ed25519-dalek", "hmac", - "http 0.2.11", + "http 0.2.12", "itertools 0.10.5", "log", "oauth2", @@ -3190,12 +3262,12 @@ checksum = "f24cda83b20ed2433c68241f918d0f6fdec8b1d43b7a9590ab4420c5095ca930" dependencies = [ "async-trait", "futures-core", - "http 0.2.11", + "http 0.2.12", "opentelemetry", "opentelemetry-proto", "opentelemetry-semantic-conventions", "opentelemetry_sdk", - "prost", + "prost 0.11.9", "thiserror", "tokio", "tonic", @@ -3209,7 +3281,7 @@ checksum = "a2e155ce5cc812ea3d1dffbd1539aed653de4bf4882d60e6e04dcf0901d674e1" dependencies = [ "opentelemetry", "opentelemetry_sdk", - "prost", + "prost 0.11.9", "tonic", ] @@ -3476,6 +3548,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.2.5", +] + [[package]] name = "phf" version = "0.11.2" @@ -3584,18 +3666,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", @@ -3756,11 +3838,13 @@ dependencies = [ "axum-server", "clap", "daemonize", + "futures", "http-body-util", "humansize", "itertools 0.12.1", "k8s-openapi", "lazy_static", + "mime", "mockall", "mockall_double", "num_cpus", @@ -3768,8 +3852,10 @@ dependencies = [ "opentelemetry-otlp", "opentelemetry_sdk", "policy-evaluator", + "pprof", "procfs", "rayon", + "regex", "rstest", "semver", "serde", @@ -3834,6 +3920,30 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pprof" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5c97c51bd34c7e742402e216abdeb44d415fbe6ae41d56b114723e953711cb" +dependencies = [ + "backtrace", + "cfg-if", + "findshlibs", + "libc", + "log", + "nix", + "once_cell", + "parking_lot", + "prost 0.12.3", + "prost-build", + "prost-derive 0.12.3", + "sha2", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3866,6 +3976,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.52", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -3941,7 +4061,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +dependencies = [ + "bytes", + "prost-derive 0.12.3", +] + +[[package]] +name = "prost-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" +dependencies = [ + "bytes", + "heck", + "itertools 0.11.0", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.12.3", + "prost-types", + "regex", + "syn 2.0.52", + "tempfile", + "which", ] [[package]] @@ -3957,6 +4109,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +dependencies = [ + "anyhow", + "itertools 0.11.0", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "prost-types" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" +dependencies = [ + "prost 0.12.3", +] + [[package]] name = "psm" version = "0.1.21" @@ -4072,7 +4246,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.5", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -4087,9 +4261,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -4126,7 +4300,7 @@ dependencies = [ "futures-core", "futures-util", "h2 0.3.24", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", "hyper-rustls", @@ -4872,6 +5046,29 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "symbolic-common" +version = "12.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a99812da4020a67e76c4eb41f08c87364c14170495ff780f30dd519c221a68" +dependencies = [ + "cpp_demangle 0.4.3", + "rustc-demangle", + "symbolic-common", +] + [[package]] name = "syn" version = "1.0.109" @@ -5157,13 +5354,13 @@ dependencies = [ "futures-core", "futures-util", "h2 0.3.24", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", "hyper-timeout", "percent-encoding", "pin-project", - "prost", + "prost 0.11.9", "tokio", "tokio-stream", "tower", @@ -5230,7 +5427,7 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 0.2.11", + "http 0.2.12", "http-body 0.4.6", "http-range-header", "mime", @@ -5248,7 +5445,7 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "bitflags 2.4.2", "bytes", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "pin-project-lite", @@ -5656,9 +5853,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5666,9 +5863,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -5681,9 +5878,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -5693,9 +5890,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5703,9 +5900,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -5716,9 +5913,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" @@ -5971,7 +6168,7 @@ dependencies = [ "anyhow", "bincode", "cfg-if", - "cpp_demangle", + "cpp_demangle 0.3.5", "gimli", "ittapi", "log", @@ -6187,9 +6384,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -6207,9 +6404,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71" +checksum = "d1b04c569c83a9bb971dd47ec6fd48753315f4bf989b9b04a2e7ca4d7f0dc950" dependencies = [ "core-foundation", "home", @@ -6228,6 +6425,18 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.31", +] + [[package]] name = "wiggle" version = "17.0.2" diff --git a/Cargo.toml b/Cargo.toml index 924ac90a..b5527c2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,12 +14,14 @@ edition = "2021" anyhow = "1.0" clap = { version = "4.5", features = ["cargo", "env"] } daemonize = "0.5" +futures = "0.3" humansize = "2.1" itertools = "0.12.1" k8s-openapi = { version = "0.21.1", default-features = false, features = [ "v1_29", ] } lazy_static = "1.4.0" +mime = "0.3" num_cpus = "1.16.0" opentelemetry-otlp = { version = "0.14.0", features = ["metrics", "tonic"] } opentelemetry = { version = "0.21", default-features = false, features = [ @@ -28,8 +30,10 @@ opentelemetry = { version = "0.21", default-features = false, features = [ ] } opentelemetry_sdk = { version = "0.21", features = ["rt-tokio"] } procfs = "0.16" +pprof = { version = "0.13", features = ["prost-codec"] } policy-evaluator = { git = "https://github.com/kubewarden/policy-evaluator", tag = "v0.16.1" } rayon = "1.9" +regex = "1.10" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9.32" @@ -42,7 +46,7 @@ tracing-opentelemetry = "0.22.0" tracing-subscriber = { version = "0.3", features = ["ansi", "fmt", "json"] } semver = { version = "1.0.22", features = ["serde"] } mockall_double = "0.3" -axum = { version = "0.7.4", features = ["macros"] } +axum = { version = "0.7.4", features = ["macros", "query"] } axum-server = { version = "0.6", features = ["tls-rustls"] } tower-http = { version = "0.5.2", features = ["trace"] } diff --git a/src/api/handlers.rs b/src/api/handlers.rs index d8a6ae50..33db05c1 100644 --- a/src/api/handlers.rs +++ b/src/api/handlers.rs @@ -1,6 +1,6 @@ use axum::{ - extract::{self, FromRequest}, - http::StatusCode, + extract::{self, FromRequest, Query}, + http::{header, StatusCode}, response::IntoResponse, Json, }; @@ -8,19 +8,23 @@ use policy_evaluator::{ admission_request::AdmissionRequest, admission_response::AdmissionResponse, policy_evaluator::ValidateRequest, }; -use serde::Serialize; + +use serde::{Deserialize, Serialize}; use std::sync::Arc; use tokio::task; use tracing::{debug, error, Span}; -use crate::api::{ - admission_review::{AdmissionReviewRequest, AdmissionReviewResponse}, - api_error::ApiError, - raw_review::{RawReviewRequest, RawReviewResponse}, - service::{evaluate, RequestOrigin}, - state::ApiServerState, +use crate::{ + api::{ + admission_review::{AdmissionReviewRequest, AdmissionReviewResponse}, + api_error::ApiError, + raw_review::{RawReviewRequest, RawReviewResponse}, + service::{evaluate, RequestOrigin}, + state::ApiServerState, + }, + profiling, }; -use crate::evaluation::errors::EvaluationError; +use crate::{evaluation::errors::EvaluationError, profiling::ReportGenerationError}; // create an extractor that internally uses `axum::Json` but has a custom rejection #[derive(FromRequest)] @@ -169,6 +173,50 @@ pub(crate) async fn validate_raw_handler( Ok(Json(RawReviewResponse::new(response))) } +#[derive(Deserialize)] +pub(crate) struct ProfileParams { + /// profiling frequency (Hz) + #[serde(default = "profiling::default_profiling_frequency")] + pub frequency: i32, + + /// profiling time interval (seconds) + #[serde(default = "profiling::default_profiling_interval")] + pub interval: u64, +} + +// Generate a pprof CPU profile using google's pprof format +// The report is generated and sent to the user as binary data +pub(crate) async fn pprof_get_cpu( + profiling_params: Query, +) -> Result { + let frequency = profiling_params.frequency; + let interval = profiling_params.interval; + + let end = async move { + tokio::time::sleep(tokio::time::Duration::from_secs(interval)).await; + Ok(()) + }; + + let body = profiling::start_one_cpu_profile(end, frequency) + .await + .map_err(handle_pprof_error)?; + + let mut headers = header::HeaderMap::new(); + headers.insert( + header::CONTENT_DISPOSITION, + r#"attachment; filename="cpu_profile"#.parse().unwrap(), + ); + headers.insert( + header::CONTENT_LENGTH, + body.len().to_string().parse().unwrap(), + ); + headers.insert( + header::CONTENT_TYPE, + mime::APPLICATION_OCTET_STREAM.to_string().parse().unwrap(), + ); + + Ok((headers, body)) +} pub(crate) async fn readiness_handler() -> StatusCode { StatusCode::OK } @@ -261,3 +309,15 @@ fn handle_evaluation_error(error: EvaluationError) -> (StatusCode, ApiError) { } } } + +fn handle_pprof_error(error: ReportGenerationError) -> (StatusCode, ApiError) { + error!("pprof error: {}", error); + + ( + StatusCode::INTERNAL_SERVER_ERROR, + ApiError { + status: StatusCode::INTERNAL_SERVER_ERROR, + message: "Something went wrong".to_owned(), + }, + ) +} diff --git a/src/cli.rs b/src/cli.rs index 2ca54a3e..afe3ecde 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,5 @@ use clap::builder::PossibleValue; -use clap::{crate_authors, crate_description, crate_name, crate_version, Arg, Command}; +use clap::{crate_authors, crate_description, crate_name, crate_version, Arg, ArgAction, Command}; use itertools::Itertools; use lazy_static::lazy_static; use policy_evaluator::burrego; @@ -21,11 +21,7 @@ lazy_static! { } pub(crate) fn build_cli() -> Command { - Command::new(crate_name!()) - .author(crate_authors!()) - .version(crate_version!()) - .about(crate_description!()) - .arg( + let mut args = vec![ Arg::new("log-level") .long("log-level") .value_name("LOG_LEVEL") @@ -39,8 +35,6 @@ pub(crate) fn build_cli() -> Command { PossibleValue::new("error"), ]) .help("Log level"), - ) - .arg( Arg::new("log-fmt") .long("log-fmt") .value_name("LOG_FMT") @@ -52,169 +46,132 @@ pub(crate) fn build_cli() -> Command { PossibleValue::new("otlp"), ]) .help("Log output format"), - ) - .arg( Arg::new("log-no-color") .long("log-no-color") .env("NO_COLOR") - .required(false) + .action(ArgAction::SetTrue) .help("Disable colored output for logs"), - ) - .arg( Arg::new("address") .long("addr") .value_name("BIND_ADDRESS") .default_value("0.0.0.0") .env("KUBEWARDEN_BIND_ADDRESS") .help("Bind against ADDRESS"), - ) - .arg( Arg::new("port") .long("port") .value_name("PORT") .default_value("3000") .env("KUBEWARDEN_PORT") .help("Listen on PORT"), - ) - .arg( Arg::new("workers") .long("workers") .value_name("WORKERS_NUMBER") .env("KUBEWARDEN_WORKERS") .help("Number of workers thread to create"), - ) - .arg( Arg::new("cert-file") .long("cert-file") .value_name("CERT_FILE") .default_value("") .env("KUBEWARDEN_CERT_FILE") .help("Path to an X.509 certificate file for HTTPS"), - ) - .arg( Arg::new("key-file") .long("key-file") .value_name("KEY_FILE") .default_value("") .env("KUBEWARDEN_KEY_FILE") .help("Path to an X.509 private key file for HTTPS"), - ) - .arg( Arg::new("policies") .long("policies") .value_name("POLICIES_FILE") .env("KUBEWARDEN_POLICIES") .default_value("policies.yml") .help("YAML file holding the policies to be loaded and their settings"), - ) - .arg( Arg::new("policies-download-dir") .long("policies-download-dir") .value_name("POLICIES_DOWNLOAD_DIR") .default_value(".") .env("KUBEWARDEN_POLICIES_DOWNLOAD_DIR") .help("Download path for the policies"), - ) - .arg( Arg::new("sigstore-cache-dir") .long("sigstore-cache-dir") .value_name("SIGSTORE_CACHE_DIR") .default_value("sigstore-data") .env("KUBEWARDEN_SIGSTORE_CACHE_DIR") .help("Directory used to cache sigstore data"), - ) - .arg( Arg::new("sources-path") .long("sources-path") .value_name("SOURCES_PATH") .env("KUBEWARDEN_SOURCES_PATH") .help("YAML file holding source information (https, registry insecure hosts, custom CA's...)"), - ) - .arg( Arg::new("verification-path") .long("verification-path") .value_name("VERIFICATION_CONFIG_PATH") .env("KUBEWARDEN_VERIFICATION_CONFIG_PATH") .help("YAML file holding verification information (URIs, keys, annotations...)"), - ) - .arg( Arg::new("docker-config-json-path") .long("docker-config-json-path") .value_name("DOCKER_CONFIG") .env("KUBEWARDEN_DOCKER_CONFIG_JSON_PATH") .help("Path to a Docker config.json-like path. Can be used to indicate registry authentication details"), - ) - .arg( Arg::new("enable-metrics") .long("enable-metrics") .env("KUBEWARDEN_ENABLE_METRICS") - .required(false) + .action(ArgAction::SetTrue) .help("Enable metrics"), - ) - .arg( - Arg::new("enable-verification") - .long("enable-verification") - .env("KUBEWARDEN_ENABLE_VERIFICATION") - .required(false) - .help("Enable Sigstore verification"), - ) - .arg( Arg::new("always-accept-admission-reviews-on-namespace") .long("always-accept-admission-reviews-on-namespace") .value_name("NAMESPACE") .env("KUBEWARDEN_ALWAYS_ACCEPT_ADMISSION_REVIEWS_ON_NAMESPACE") .required(false) .help("Always accept AdmissionReviews that target the given namespace"), - ) - .arg( Arg::new("disable-timeout-protection") .long("disable-timeout-protection") + .action(ArgAction::SetTrue) .env("KUBEWARDEN_DISABLE_TIMEOUT_PROTECTION") - .required(false) .help("Disable policy timeout protection"), - ) - .arg( Arg::new("policy-timeout") .long("policy-timeout") .env("KUBEWARDEN_POLICY_TIMEOUT") .value_name("MAXIMUM_EXECUTION_TIME_SECONDS") .default_value("2") .help("Interrupt policy evaluation after the given time"), - ) - .arg( Arg::new("daemon") .long("daemon") .env("KUBEWARDEN_DAEMON") - .required(false) + .action(ArgAction::SetTrue) .help("If set, runs policy-server in detached mode as a daemon"), - ) - .arg( Arg::new("daemon-pid-file") .long("daemon-pid-file") .env("KUBEWARDEN_DAEMON_PID_FILE") .default_value("policy-server.pid") .help("Path to pid file, used only when running in daemon mode"), - ) - .arg( Arg::new("daemon-stdout-file") .long("daemon-stdout-file") .env("KUBEWARDEN_DAEMON_STDOUT_FILE") .required(false) .help("Path to file holding stdout, used only when running in daemon mode"), - ) - .arg( Arg::new("daemon-stderr-file") .long("daemon-stderr-file") .env("KUBEWARDEN_DAEMON_STDERR_FILE") .required(false) .help("Path to file holding stderr, used only when running in daemon mode"), - ) - .arg( Arg::new("ignore-kubernetes-connection-failure") .long("ignore-kubernetes-connection-failure") .env("KUBEWARDEN_IGNORE_KUBERNETES_CONNECTION_FAILURE") - .required(false) + .action(ArgAction::SetTrue) .help("Do not exit with an error if the Kubernetes connection fails. This will cause context aware policies to break when there's no connection with Kubernetes."), - ) + Arg::new("enable-pprof") + .long("enable-pprof") + .env("KUBEWARDEN_ENABLE_PPROF") + .action(ArgAction::SetTrue) + .help("Enable pprof profiling"), + ]; + args.sort_by(|a, b| a.get_id().cmp(b.get_id())); + + Command::new(crate_name!()) + .author(crate_authors!()) + .version(crate_version!()) + .about(crate_description!()) .long_version(VERSION_AND_BUILTINS.as_str()) + .args(args) } diff --git a/src/config.rs b/src/config.rs index 10d07869..4dd91c6b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -41,6 +41,7 @@ pub struct Config { pub log_fmt: String, pub log_no_color: bool, pub daemon: bool, + pub enable_pprof: bool, pub daemon_pid_file: String, pub daemon_stdout_file: Option, pub daemon_stderr_file: Option, @@ -61,7 +62,10 @@ impl Config { .get_one::("policies-download-dir") .map(PathBuf::from) .expect("This should not happen, there's a default value for policies-download-dir"); - let policy_evaluation_limit_seconds = if matches.contains_id("disable-timeout-protection") { + let policy_evaluation_limit_seconds = if *matches + .get_one::("disable-timeout-protection") + .expect("clap should have set a default value") + { None } else { Some( @@ -82,16 +86,24 @@ impl Config { .get_one::("always-accept-admission-reviews-on-namespace") .map(|s| s.to_owned()); - let metrics_enabled = matches.contains_id("enable-metrics"); - let ignore_kubernetes_connection_failure = - matches.contains_id("ignore-kubernetes-connection-failure"); + let metrics_enabled = matches + .get_one::("enable-metrics") + .expect("clap should have set a default value") + .to_owned(); + let ignore_kubernetes_connection_failure = matches + .get_one::("ignore-kubernetes-connection-failure") + .expect("clap should have set a default value") + .to_owned(); let verification_config = verification_config(matches)?; let sigstore_cache_dir = matches .get_one::("sigstore-cache-dir") .map(PathBuf::from) .expect("This should not happen, there's a default value for sigstore-cache-dir"); - let daemon = matches.contains_id("daemon"); + let daemon = matches + .get_one::("daemon") + .expect("clap should have set a default value") + .to_owned(); let daemon_pid_file = matches .get_one::("daemon-pid-file") .expect("This should not happen, there's a default value for daemon-pid-file") @@ -107,7 +119,10 @@ impl Config { .get_one::("log-fmt") .expect("This should not happen, there's a default value for log-fmt") .to_owned(); - let log_no_color = matches.contains_id("log-no-color"); + let log_no_color = matches + .get_one::("log-no-color") + .expect("clap should have assigned a default value") + .to_owned(); let (cert_file, key_file) = tls_files(matches)?; let tls_config = if cert_file.is_empty() { None @@ -117,6 +132,10 @@ impl Config { key_file, }) }; + let enable_pprof = matches + .get_one::("enable-pprof") + .expect("clap should have assigned a default value") + .to_owned(); Ok(Self { addr, @@ -138,6 +157,7 @@ impl Config { daemon_pid_file, daemon_stdout_file, daemon_stderr_file, + enable_pprof, }) } } @@ -288,6 +308,9 @@ fn read_policies_file(path: &Path) -> Result> { #[cfg(test)] mod tests { use super::*; + use crate::cli; + use std::io::Write; + use tempfile::NamedTempFile; #[test] fn get_settings_when_data_is_provided() { @@ -440,4 +463,41 @@ example: let settings = json_data.unwrap(); assert!(settings.is_empty()); } + + #[test] + fn boolean_flags() { + let policies_yaml = r#" +--- +example: + url: file:///tmp/namespace-validate-policy.wasm + settings: {} +"#; + let mut temp_file = NamedTempFile::new().unwrap(); + temp_file.write_all(policies_yaml.as_bytes()).unwrap(); + let file_path = temp_file.into_temp_path(); + let policies_flag = format!("--policies={}", file_path.to_str().unwrap()); + + let boolean_flags = [ + "--enable-pprof", + "--log-no-color", + "--daemon", + "--enable-metrics", + ]; + + for provide_flag in [true, false] { + let cli = cli::build_cli(); + + let mut flags = vec!["policy-server", &policies_flag]; + if provide_flag { + flags.extend(boolean_flags); + } + + let matches = cli.clone().try_get_matches_from(flags).unwrap(); + let config = Config::from_args(&matches).unwrap(); + assert_eq!(provide_flag, config.enable_pprof); + assert_eq!(provide_flag, config.log_no_color); + assert_eq!(provide_flag, config.daemon); + assert_eq!(provide_flag, config.metrics_enabled); + } + } } diff --git a/src/lib.rs b/src/lib.rs index b3cc3f5a..6d07fe1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,13 @@ mod policy_downloader; #[cfg(test)] mod test_utils; +#[cfg(test)] +mod cli; + pub mod api; pub mod config; pub mod metrics; +pub mod profiling; pub mod tracing; use ::tracing::{debug, error, info, Level}; @@ -29,7 +33,7 @@ use tokio::time; use tower_http::trace::{self, TraceLayer}; use crate::api::handlers::{ - audit_handler, readiness_handler, validate_handler, validate_raw_handler, + audit_handler, pprof_get_cpu, readiness_handler, validate_handler, validate_raw_handler, }; use crate::api::state::ApiServerState; use crate::evaluation::{ @@ -176,7 +180,7 @@ impl PolicyServer { None }; - let router = Router::new() + let mut router = Router::new() .route("/audit/:policy_id", post(audit_handler)) .route("/validate/:policy_id", post(validate_handler)) .route("/validate_raw/:policy_id", post(validate_raw_handler)) @@ -187,6 +191,10 @@ impl PolicyServer { .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) .on_response(trace::DefaultOnResponse::new().level(Level::INFO)), ); + if config.enable_pprof { + let pprof_router = Router::new().route("/debug/pprof/cpu", get(pprof_get_cpu)); + router = Router::new().merge(router).merge(pprof_router); + } Ok(Self { router, diff --git a/src/profiling.rs b/src/profiling.rs new file mode 100644 index 00000000..15b52080 --- /dev/null +++ b/src/profiling.rs @@ -0,0 +1,149 @@ +use futures::{ + future::BoxFuture, + task::{Context, Poll}, + Future, FutureExt, +}; +use lazy_static::lazy_static; +use pprof::protos::Message; +use regex::Regex; +use std::{pin::Pin, sync::Mutex}; +use thiserror::Error; + +lazy_static! { + // If it's some it means there are already a CPU profiling. + static ref CPU_PROFILE_ACTIVE: Mutex> = Mutex::new(None); + + // To normalize thread names. + static ref THREAD_NAME_RE: Regex = + Regex::new(r"^(?P[a-z-_ :]+?)(-?\d)*$").unwrap(); + static ref THREAD_NAME_REPLACE_SEPERATOR_RE: Regex = Regex::new(r"[_ ]").unwrap(); +} + +#[derive(Debug, Error)] +pub enum ReportGenerationError { + #[error("CPU profile already running")] + CPUAlreadyProfiling, + + #[error("pprof error: {0}")] + PprofError(#[from] pprof::Error), + + #[error("cannot encode report to pprof format: {0}")] + EncodeError(String), +} + +/// Default frequency of sampling. 99Hz to avoid coincide with special periods +pub fn default_profiling_frequency() -> i32 { + 99 +} + +/// Default profiling interval time - 30 seconds +pub fn default_profiling_interval() -> u64 { + 30 +} + +/// Trigger one cpu profile. +pub async fn start_one_cpu_profile( + end: F, + frequency: i32, +) -> Result, ReportGenerationError> +where + F: Future> + Send + 'static, +{ + if CPU_PROFILE_ACTIVE.lock().unwrap().is_some() { + return Err(ReportGenerationError::CPUAlreadyProfiling); + } + + let on_start = || { + let mut activate = CPU_PROFILE_ACTIVE.lock().unwrap(); + assert!(activate.is_none()); + *activate = Some(()); + let guard = pprof::ProfilerGuardBuilder::default() + .frequency(frequency) + .blocklist(&["libc", "libgcc", "pthread", "vdso"]) + .build()?; + Ok(guard) + }; + + let on_end = move |guard: pprof::ProfilerGuard<'static>| { + let report = guard + .report() + .frames_post_processor(move |frames| { + let name = extract_thread_name(&frames.thread_name); + frames.thread_name = name; + }) + .build()?; + let mut body = Vec::new(); + let profile = report.pprof()?; + + profile + .encode(&mut body) + .map_err(|e| ReportGenerationError::EncodeError(e.to_string()))?; + + drop(guard); + *CPU_PROFILE_ACTIVE.lock().unwrap() = None; + + Ok(body) + }; + + ProfileRunner::new(on_start, on_end, end.boxed())?.await +} + +fn extract_thread_name(thread_name: &str) -> String { + THREAD_NAME_RE + .captures(thread_name) + .and_then(|cap| { + cap.name("thread_name").map(|thread_name| { + THREAD_NAME_REPLACE_SEPERATOR_RE + .replace_all(thread_name.as_str(), "-") + .into_owned() + }) + }) + .unwrap_or_else(|| thread_name.to_owned()) +} + +type OnEndFn = Box Result + Send + 'static>; + +struct ProfileRunner { + item: Option, + on_end: Option>, + end: BoxFuture<'static, Result<(), ReportGenerationError>>, +} + +impl Unpin for ProfileRunner {} + +impl ProfileRunner { + fn new( + on_start: F1, + on_end: F2, + end: BoxFuture<'static, Result<(), ReportGenerationError>>, + ) -> Result + where + F1: FnOnce() -> Result, + F2: FnOnce(I) -> Result + Send + 'static, + { + let item = on_start()?; + Ok(ProfileRunner { + item: Some(item), + on_end: Some(Box::new(on_end) as OnEndFn), + end, + }) + } +} + +impl Future for ProfileRunner { + type Output = Result; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.end.as_mut().poll(cx) { + Poll::Ready(res) => { + let item = self.item.take().unwrap(); + let on_end = self.on_end.take().unwrap(); + let r = match (res, on_end(item)) { + (Ok(_), r) => r, + (Err(errmsg), _) => Err(errmsg), + }; + Poll::Ready(r) + } + Poll::Pending => Poll::Pending, + } + } +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index d45ca845..d45752c9 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -69,6 +69,7 @@ pub(crate) async fn app() -> Router { daemon_pid_file: "policy_server.pid".to_owned(), daemon_stdout_file: None, daemon_stderr_file: None, + enable_pprof: true, }; let server = PolicyServer::new_from_config(config).await.unwrap();