From 80e209d333c91664e43332864886ce513f052f2b Mon Sep 17 00:00:00 2001 From: tahahawa Date: Fri, 27 Jun 2025 00:10:36 -0400 Subject: [PATCH 1/6] Add ADR --- adr/013-monitoring-notifications.md | 78 +++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 adr/013-monitoring-notifications.md diff --git a/adr/013-monitoring-notifications.md b/adr/013-monitoring-notifications.md new file mode 100644 index 0000000..83e73e6 --- /dev/null +++ b/adr/013-monitoring-notifications.md @@ -0,0 +1,78 @@ +# Architecture Decision Record: Monitoring Notifications + +Initial Author: Taha Hawa + +Initial Date: 2025-06-26 + +Last Updated Date: 2025-06-26 + +## Status + +Proposed + +## Context + +We need to send notifications (typically from AlertManager/Prometheus) and we need to receive said notifications on mobile devices for sure in some way, whether it's push messages, SMS, phone call, email, etc or all of the above. + +## Decision + +We should go with https://ntfy.sh except host it ourselves. + +`ntfy` is an open source solution written in Go that has the features we need. + +## Rationale + +`ntfy` has pretty much everything we need (push notifications, email forwarding, receives via webhook), and nothing/not much we don't. Good fit, lightweight. + +## Consequences + +Pros: + +- topics, with ACLs +- lightweight +- reliable +- easy to configure +- mobile app + - the mobile app can listen via websocket, poll, or receive via Firebase/GCM on Android, or similar on iOS. +- Forward to email +- Text-to-Speech phone call messages using Twilio integration +- Operates based on simple HTTP requests/Webhooks, easily usable via AlertManager + +Cons: + +- No SMS pushes +- SQLite DB, makes it harder to HA/scale + +## Alternatives considered + +[AWS SNS](https://aws.amazon.com/sns/): +Pros: + +- highly reliable +- no hosting needed + +Cons: + +- no control, not self hosted +- costs (per usage) + +[Apprise](https://github.com/caronc/apprise): +Pros: + +- Way more ways of sending notifications +- Can use ntfy as one of the backends/ways of sending + +Cons: + +- Way too overkill for what we need in terms of features + +[Gotify](https://github.com/gotify/server): +Pros: + +- simple, lightweight, golang, etc + +Cons: + +- Pushes topics are per-user + +## Additional Notes -- 2.39.5 From 6a29969c7f55b04558d7e3ed9ade6ac118928a2d Mon Sep 17 00:00:00 2001 From: tahahawa Date: Fri, 27 Jun 2025 02:28:44 -0400 Subject: [PATCH 2/6] working on ntfy score --- Cargo.lock | 729 ++++++++++++------ Cargo.toml | 20 +- examples/kube-rs/Cargo.toml | 2 +- examples/ntfy/Cargo.toml | 12 + examples/ntfy/src/main.rs | 43 ++ harmony/Cargo.toml | 1 + harmony/src/domain/topology/k8s.rs | 81 +- .../monitoring/kube_prometheus/helm/config.rs | 5 +- harmony/src/modules/monitoring/mod.rs | 1 + .../modules/monitoring/ntfy/helm/config.rs | 6 + .../src/modules/monitoring/ntfy/helm/mod.rs | 2 + .../monitoring/ntfy/helm/ntfy_helm_chart.rs | 91 +++ harmony/src/modules/monitoring/ntfy/mod.rs | 2 + harmony/src/modules/monitoring/ntfy/ntfy.rs | 33 + 14 files changed, 797 insertions(+), 231 deletions(-) create mode 100644 examples/ntfy/Cargo.toml create mode 100644 examples/ntfy/src/main.rs create mode 100644 harmony/src/modules/monitoring/ntfy/helm/config.rs create mode 100644 harmony/src/modules/monitoring/ntfy/helm/mod.rs create mode 100644 harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs create mode 100644 harmony/src/modules/monitoring/ntfy/mod.rs create mode 100644 harmony/src/modules/monitoring/ntfy/ntfy.rs diff --git a/Cargo.lock b/Cargo.lock index 2769781..db8298b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -60,6 +60,7 @@ checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "const-random", + "getrandom 0.3.3", "once_cell", "version_check", "zerocopy", @@ -97,9 +98,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -112,39 +113,45 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "arc-swap" version = "1.7.1" @@ -167,6 +174,40 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.88" @@ -186,9 +227,20 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "getrandom 0.2.16", + "instant", + "rand 0.8.5", +] [[package]] name = "backtrace" @@ -225,9 +277,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "bcrypt-pbkdf" @@ -297,9 +349,9 @@ dependencies = [ [[package]] name = "bollard" -version = "0.19.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af706e9dc793491dd382c99c22fde6e9934433d4cc0d6a4b34eb2cdc57a5c917" +checksum = "899ca34eb6924d6ec2a77c6f7f5c7339e60fd68235eaf91edd5a15f12958bb06" dependencies = [ "base64 0.22.1", "bollard-stubs", @@ -330,9 +382,9 @@ dependencies = [ [[package]] name = "bollard-stubs" -version = "1.48.2-rc.28.0.4" +version = "1.48.3-rc.28.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cdf0fccd5341b38ae0be74b74410bdd5eceeea8876dc149a13edfe57e3b259" +checksum = "64ea257e555d16a2c01e5593f40b73865cdf12efbceda33c6d14a2d8d1490368" dependencies = [ "serde", "serde_json", @@ -353,9 +405,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -444,18 +496,18 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.22" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "chacha20" @@ -504,9 +556,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -514,9 +566,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -526,9 +578,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", @@ -538,15 +590,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "color-eyre" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e1761c0e16f8883bbbb8ce5990867f4f06bf11a0253da6495a04ce4b6ef0ec" +checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" dependencies = [ "backtrace", "color-spantrace", @@ -559,9 +611,9 @@ dependencies = [ [[package]] name = "color-spantrace" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ddd8d5bfda1e11a501d0a7303f3bfed9aa632ebdb859be40d0fd70478ed70d5" +checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" dependencies = [ "once_cell", "owo-colors", @@ -571,9 +623,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "compact_str" @@ -589,6 +641,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -636,9 +697,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -708,7 +769,7 @@ dependencies = [ "bitflags 2.9.1", "crossterm_winapi", "futures-core", - "mio 1.0.3", + "mio 1.0.4", "parking_lot", "rustix 0.38.44", "signal-hook", @@ -727,9 +788,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" @@ -920,7 +981,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -942,21 +1003,22 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dockerfile_builder" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ac372e31c7dd054d0fc69ca96ca36ee8d1cf79881683ad6f783c47aba3dc6e2" +checksum = "5a170fdd68a190665b78d1bbc31a7f34f454ed7cbb12af88cacbb1c89b3d4af1" dependencies = [ + "anyhow", "dockerfile_builder_macros", - "eyre", + "url", ] [[package]] name = "dockerfile_builder_macros" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b627d9019ce257916c7ada6f233cf22e1e5246b6d9426b20610218afb7fd3ec9" +checksum = "2b9226ce489dd608c6b1abe85f5ae281559d73d921a98a30a399069ad5ee71bd" dependencies = [ - "eyre", + "anyhow", "proc-macro2", "quote", "syn", @@ -1007,6 +1069,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.15.0" @@ -1052,6 +1126,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -1093,12 +1187,33 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", ] [[package]] @@ -1131,7 +1246,7 @@ dependencies = [ "harmony_macros", "http 1.3.1", "inquire", - "k8s-openapi", + "k8s-openapi 0.25.0", "kube", "log", "serde_yaml", @@ -1179,6 +1294,16 @@ dependencies = [ "url", ] +[[package]] +name = "example-ntfy" +version = "0.1.0" +dependencies = [ + "harmony", + "harmony_cli", + "tokio", + "url", +] + [[package]] name = "example-opnsense" version = "0.1.0" @@ -1258,9 +1383,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -1457,7 +1582,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1512,7 +1637,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -1531,7 +1656,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.9.0", + "indexmap 2.10.0", "slab", "tokio", "tokio-util", @@ -1553,6 +1678,7 @@ dependencies = [ "email_address", "env_logger", "fqdn", + "futures-util", "harmony_macros", "harmony_types", "helm-wrapper-rs", @@ -1560,7 +1686,7 @@ dependencies = [ "http 1.3.1", "inquire", "k3d-rs", - "k8s-openapi", + "k8s-openapi 0.24.0", "kube", "lazy_static", "libredfish", @@ -1658,9 +1784,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -1669,11 +1795,11 @@ dependencies = [ [[package]] name = "headers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "headers-core", "http 1.3.1", @@ -1699,22 +1825,22 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "helm-wrapper-rs" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc9253a7bbf4ba8ff6052d5ab7ddc6e2ca17cd8481d15636fb9f64611653880c" +checksum = "34f18b42189de0a77db495697b9d5cedf1743ae3784ecb77f9a42c1fff336e82" dependencies = [ "log", "non-blank-string-rs", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -1755,6 +1881,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "hostname" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +dependencies = [ + "cfg-if", + "libc", + "windows-link", +] + [[package]] name = "http" version = "0.2.12" @@ -1929,11 +2066,10 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http 1.3.1", "hyper 1.6.0", "hyper-util", @@ -1990,22 +2126,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration 0.6.1", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2096,9 +2238,9 @@ checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", @@ -2112,9 +2254,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" @@ -2179,12 +2321,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "serde", ] @@ -2234,6 +2376,15 @@ dependencies = [ "syn", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2273,9 +2424,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "log", @@ -2286,9 +2437,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", @@ -2305,6 +2456,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + [[package]] name = "jsonpath-rust" version = "0.7.5" @@ -2318,6 +2481,16 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "jsonwebtoken" version = "9.3.1" @@ -2346,7 +2519,7 @@ dependencies = [ "octocrab", "pretty_assertions", "regex", - "reqwest 0.12.15", + "reqwest 0.12.20", "sha2", "tokio", "url", @@ -2365,15 +2538,28 @@ dependencies = [ "serde_json", ] +[[package]] +name = "k8s-openapi" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa60a41b57ae1a0a071af77dbcf89fc9819cfe66edaf2beeb204c34459dcf0b2" +dependencies = [ + "base64 0.22.1", + "chrono", + "serde", + "serde_json", +] + [[package]] name = "kube" version = "0.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32053dc495efad4d188c7b33cc7c02ef4a6e43038115348348876efd39a53cba" dependencies = [ - "k8s-openapi", + "k8s-openapi 0.24.0", "kube-client", "kube-core", + "kube-runtime", ] [[package]] @@ -2397,9 +2583,10 @@ dependencies = [ "hyper-timeout", "hyper-util", "jsonpath-rust", - "k8s-openapi", + "k8s-openapi 0.24.0", "kube-core", "pem", + "rand 0.8.5", "rustls", "rustls-pemfile 2.2.0", "secrecy", @@ -2408,6 +2595,7 @@ dependencies = [ "serde_yaml", "thiserror 2.0.12", "tokio", + "tokio-tungstenite", "tokio-util", "tower", "tower-http", @@ -2423,13 +2611,43 @@ dependencies = [ "chrono", "form_urlencoded", "http 1.3.1", - "k8s-openapi", + "json-patch", + "k8s-openapi 0.24.0", "serde", "serde-value", "serde_json", "thiserror 2.0.12", ] +[[package]] +name = "kube-runtime" +version = "0.98.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a41af186a0fe80c71a13a13994abdc3ebff80859ca6a4b8a6079948328c135b" +dependencies = [ + "ahash", + "async-broadcast", + "async-stream", + "async-trait", + "backoff", + "educe", + "futures", + "hashbrown 0.15.4", + "hostname", + "json-patch", + "jsonptr", + "k8s-openapi 0.24.0", + "kube-client", + "parking_lot", + "pin-project", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -2441,9 +2659,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libm" @@ -2466,9 +2684,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" dependencies = [ "bitflags 2.9.1", "libc", @@ -2494,9 +2712,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2523,7 +2741,7 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -2534,9 +2752,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mime" @@ -2546,9 +2764,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] @@ -2561,20 +2779,20 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.48.0", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -2678,9 +2896,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -2741,6 +2959,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -2749,9 +2973,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags 2.9.1", "cfg-if", @@ -2781,9 +3005,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.108" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -2847,9 +3071,9 @@ dependencies = [ [[package]] name = "owo-colors" -version = "4.2.0" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564" +checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e" [[package]] name = "p256" @@ -2890,10 +3114,16 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.12.3" +name = "parking" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -2901,9 +3131,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -2978,9 +3208,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror 2.0.12", @@ -2989,9 +3219,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -2999,9 +3229,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", @@ -3012,11 +3242,10 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -3122,9 +3351,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -3231,9 +3460,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -3323,9 +3552,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] @@ -3341,6 +3570,26 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "regex" version = "1.11.1" @@ -3412,9 +3661,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64 0.22.1", "bytes", @@ -3429,31 +3678,28 @@ dependencies = [ "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.2.0", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.2", - "system-configuration 0.6.1", "tokio", "tokio-native-tls", "tokio-util", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "windows-registry", ] [[package]] @@ -3639,9 +3885,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc_version" @@ -3680,9 +3926,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" dependencies = [ "log", "once_cell", @@ -3758,9 +4004,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -3786,6 +4032,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -3846,7 +4104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags 2.9.1", - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3986,15 +4244,16 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", + "indexmap 2.10.0", + "schemars", "serde", "serde_derive", "serde_json", @@ -4007,7 +4266,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "itoa", "ryu", "serde", @@ -4069,7 +4328,7 @@ checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio 0.8.11", - "mio 1.0.3", + "mio 1.0.4", "signal-hook", ] @@ -4112,33 +4371,30 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snafu" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +checksum = "320b01e011bf8d5d7a4a4a4be966d9160968935849c83b918827f6a435e7f627" dependencies = [ "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +checksum = "1961e2ef424c1424204d3a5d6975f934f56b6d50ff5732382d84ebf460e147f7" dependencies = [ "heck", "proc-macro2", @@ -4148,9 +4404,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4271,9 +4527,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -4427,12 +4683,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -4487,14 +4742,14 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.3", + "mio 1.0.4", "pin-project-lite", "signal-hook-registry", "socket2", @@ -4544,6 +4799,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.15" @@ -4554,6 +4821,7 @@ dependencies = [ "futures-core", "futures-sink", "pin-project-lite", + "slab", "tokio", ] @@ -4584,7 +4852,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.10.0", "serde", "serde_spanned", "toml_datetime", @@ -4617,9 +4885,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "base64 0.22.1", "bitflags 2.9.1", @@ -4662,9 +4930,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", @@ -4673,9 +4941,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -4723,6 +4991,23 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http 1.3.1", + "httparse", + "log", + "rand 0.9.1", + "sha1", + "thiserror 2.0.12", + "utf-8", +] + [[package]] name = "typeid" version = "1.0.3" @@ -4816,6 +5101,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -4830,20 +5121,22 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ "getrandom 0.3.3", + "js-sys", "rand 0.9.1", "uuid-macro-internal", + "wasm-bindgen", ] [[package]] name = "uuid-macro-internal" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dcd78c4f979627a754f5522cea6e6a25e55139056535fe6e69c506cd64a862" +checksum = "26b682e8c381995ea03130e381928e0e005b7c9eb483c6c8682f50e07b33c2b7" dependencies = [ "proc-macro2", "quote", @@ -4888,9 +5181,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -5030,15 +5323,15 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] @@ -5065,44 +5358,35 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-registry" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ + "windows-link", "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-strings", ] [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -5134,6 +5418,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -5167,9 +5460,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -5424,18 +5717,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index c081c86..da05b64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,19 +24,31 @@ log = "0.4" env_logger = "0.11" derive-new = "0.7" async-trait = "0.1" -tokio = { version = "1.40", features = ["io-std", "fs", "macros", "rt-multi-thread"] } +tokio = { version = "1.40", features = [ + "io-std", + "fs", + "macros", + "rt-multi-thread", +] } cidr = { features = ["serde"], version = "0.2" } russh = "0.45" russh-keys = "0.45" rand = "0.8" url = "2.5" -kube = "0.98" +kube = { version = "0.98", features = [ + "config", + "client", + "runtime", + "rustls-tls", + "ws", + "jsonpatch", +] } k8s-openapi = { version = "0.24", features = ["v1_30"] } serde_yaml = "0.9" serde-value = "0.7" http = "1.2" inquire = "0.7" -convert_case = "0.8" +convert_case = "0.8" chrono = "0.4" similar = "2" -uuid = { version = "1.11", features = [ "v4", "fast-rng", "macro-diagnostics" ] } +uuid = { version = "1.11", features = ["v4", "fast-rng", "macro-diagnostics"] } diff --git a/examples/kube-rs/Cargo.toml b/examples/kube-rs/Cargo.toml index 34fc64c..e4cf5b7 100644 --- a/examples/kube-rs/Cargo.toml +++ b/examples/kube-rs/Cargo.toml @@ -15,7 +15,7 @@ log = { workspace = true } env_logger = { workspace = true } url = { workspace = true } kube = "0.98.0" -k8s-openapi = { version = "0.24.0", features = [ "v1_30" ] } +k8s-openapi = { version = "0.25.0", features = ["v1_30"] } http = "1.2.0" serde_yaml = "0.9.34" inquire.workspace = true diff --git a/examples/ntfy/Cargo.toml b/examples/ntfy/Cargo.toml new file mode 100644 index 0000000..1c441b8 --- /dev/null +++ b/examples/ntfy/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "example-ntfy" +edition = "2024" +version.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +harmony = { version = "0.1.0", path = "../../harmony" } +harmony_cli = { version = "0.1.0", path = "../../harmony_cli" } +tokio.workspace = true +url.workspace = true diff --git a/examples/ntfy/src/main.rs b/examples/ntfy/src/main.rs new file mode 100644 index 0000000..86c1012 --- /dev/null +++ b/examples/ntfy/src/main.rs @@ -0,0 +1,43 @@ +use std::{collections::HashMap, str::FromStr}; + +use harmony::{ + inventory::Inventory, + maestro::Maestro, + modules::helm::chart::{HelmChartScore, HelmRepository, NonBlankString}, + topology::K8sAnywhereTopology, +}; + +#[tokio::main] +async fn main() { + let mut ntfy_overrides: HashMap = HashMap::new(); + ntfy_overrides.insert( + NonBlankString::from_str("image.tag").unwrap(), + "v2.12.0".to_string(), + ); + + let ntfy_chart = HelmChartScore { + namespace: Some(NonBlankString::from_str("monitoring").unwrap()), + release_name: NonBlankString::from_str("ntfy").unwrap(), + chart_name: NonBlankString::from_str("sarab97/ntfy").unwrap(), + chart_version: Some(NonBlankString::from_str("0.1.7").unwrap()), + values_overrides: Some(ntfy_overrides), + values_yaml: None, + create_namespace: true, + install_only: false, + repository: Some(HelmRepository::new( + "sarab97".to_string(), + url::Url::parse("https://charts.sarabsingh.com").unwrap(), + true, + )), + }; + + let mut maestro = Maestro::::initialize( + Inventory::autoload(), + K8sAnywhereTopology::from_env(), + ) + .await + .unwrap(); + + maestro.register_all(vec![Box::new(ntfy_chart)]); + harmony_cli::init(maestro, None).await.unwrap(); +} diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index f84bd63..f56c120 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -54,3 +54,4 @@ fqdn = { version = "0.4.6", features = [ temp-dir = "0.1.14" dyn-clone = "1.0.19" similar.workspace = true +futures-util = "0.3.31" diff --git a/harmony/src/domain/topology/k8s.rs b/harmony/src/domain/topology/k8s.rs index 9565a3d..52f28aa 100644 --- a/harmony/src/domain/topology/k8s.rs +++ b/harmony/src/domain/topology/k8s.rs @@ -1,14 +1,28 @@ use derive_new::new; -use k8s_openapi::{ClusterResourceScope, NamespaceResourceScope}; +use futures_util::TryStreamExt; +use k8s_openapi::{ + ClusterResourceScope, NamespaceResourceScope, + api::{apps::v1::Deployment, core::v1::Pod}, +}; +use kube::runtime::conditions; +use kube::runtime::wait::{Condition, await_condition}; use kube::{ - Api, Client, Config, Error, Resource, - api::{Patch, PatchParams}, + Client, Config, Error, Resource, + api::{ + Api, AttachParams, AttachedProcess, DeleteParams, ListParams, Patch, PatchParams, + PostParams, ResourceExt, WatchEvent, WatchParams, + }, config::{KubeConfigOptions, Kubeconfig}, core::ErrorResponse, + runtime::{ + WatchStreamExt, metadata_watcher, + reflector::Lookup, + watcher::{self, watch_object}, + }, }; use log::{debug, error, trace}; use serde::de::DeserializeOwned; -use similar::TextDiff; +use similar::{DiffableStr, TextDiff}; #[derive(new)] pub struct K8sClient { @@ -22,6 +36,65 @@ impl K8sClient { }) } + pub async fn wait_until_deployment_ready( + &self, + name: String, + namespace: Option<&str>, + ) -> Result<(), Error> { + let api: Api; + + if let Some(ns) = namespace { + api = Api::namespaced(self.client.clone(), ns); + } else { + api = Api::default_namespaced(self.client.clone()); + } + + // need to upgrade to latest kube-rs version https://docs.rs/kube-runtime/latest/kube_runtime/wait/conditions/fn.is_deployment_completed.html + let establish = await_condition(api, name.as_str(), conditions::is_deployment_completed()); + let _ = tokio::time::timeout(std::time::Duration::from_secs(300), establish).await?; + + Ok(()) + } + + pub async fn exec_pod( + &self, + name: String, + namespace: Option<&str>, + command: Vec, + ) -> Result<(), String> { + let api: Api; + + if let Some(ns) = namespace { + api = Api::namespaced(self.client.clone(), ns); + } else { + api = Api::default_namespaced(self.client.clone()); + } + let pod_list = api + .list(&ListParams::default().labels(format!("app.kubernetes.io/name={name}").as_str())) + .await + .expect("couldn't get list of pods"); + + if pod_list.items.len() > 1 { + return Err("too many pods".into()); + } else { + api.exec( + pod_list + .items + .first() + .expect("couldn't get pod") + .name() + .expect("couldn't get pod name") + .into_owned() + .as_str(), + command, + &AttachParams::default(), + ) + .await; + } + + Ok(()) + } + /// Apply a resource in namespace /// /// See `kubectl apply` for more information on the expected behavior of this function diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs index ecbf8d8..8e83fd7 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs @@ -1,9 +1,6 @@ use serde::Serialize; -use crate::modules::monitoring::{ - alert_rule::prometheus_alert_rule::AlertManagerRuleGroup, - kube_prometheus::types::{AlertManagerAdditionalPromRules, AlertManagerChannelConfig}, -}; +use crate::modules::monitoring::kube_prometheus::types::{AlertManagerAdditionalPromRules, AlertManagerChannelConfig}; #[derive(Debug, Clone, Serialize)] pub struct KubePrometheusConfig { diff --git a/harmony/src/modules/monitoring/mod.rs b/harmony/src/modules/monitoring/mod.rs index 0e4f8a4..3521772 100644 --- a/harmony/src/modules/monitoring/mod.rs +++ b/harmony/src/modules/monitoring/mod.rs @@ -1,3 +1,4 @@ pub mod alert_channel; pub mod alert_rule; pub mod kube_prometheus; +pub mod ntfy; diff --git a/harmony/src/modules/monitoring/ntfy/helm/config.rs b/harmony/src/modules/monitoring/ntfy/helm/config.rs new file mode 100644 index 0000000..14a2fdd --- /dev/null +++ b/harmony/src/modules/monitoring/ntfy/helm/config.rs @@ -0,0 +1,6 @@ +use serde::Serialize; + +#[derive(Debug, Clone, Serialize)] +pub struct NtfyConfig { + pub namespace: String, +} diff --git a/harmony/src/modules/monitoring/ntfy/helm/mod.rs b/harmony/src/modules/monitoring/ntfy/helm/mod.rs new file mode 100644 index 0000000..9a39996 --- /dev/null +++ b/harmony/src/modules/monitoring/ntfy/helm/mod.rs @@ -0,0 +1,2 @@ +pub mod config; +pub mod ntfy_helm_chart; diff --git a/harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs b/harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs new file mode 100644 index 0000000..fd3c4aa --- /dev/null +++ b/harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs @@ -0,0 +1,91 @@ +use non_blank_string_rs::NonBlankString; +use std::{ + str::FromStr, + sync::{Arc, Mutex}, +}; + +use crate::modules::{ + helm::chart::{HelmChartScore, HelmRepository}, + monitoring::ntfy::helm::config::NtfyConfig, +}; + +pub fn ntfy_helm_chart_score(config: Arc>) -> HelmChartScore { + let config = config.lock().unwrap(); + + let values = format!( + r#" +replicaCount: 1 + +image: + repository: binwiederhier/ntfy + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "v2.12.0" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + # annotations: + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + # name: "" + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false +# annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: ntfy.host.com + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + + +autoscaling: + enabled: false + +config: + enabled: true + data: +# base-url: "https://ntfy.something.com" + auth-file: "/var/cache/ntfy/user.db" + auth-default-access: "deny-all" + cache-file: "/var/cache/ntfy/cache.db" + attachment-cache-dir: "/var/cache/ntfy/attachments" + behind-proxy: true + # web-root: "disable" + enable-signup: false + enable-login: "true" + +persistence: + enabled: true + size: 200Mi +"#, + ); + + HelmChartScore { + namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()), + release_name: NonBlankString::from_str("ntfy").unwrap(), + chart_name: NonBlankString::from_str("sarab97/ntfy").unwrap(), + chart_version: Some(NonBlankString::from_str("0.1.7").unwrap()), + values_overrides: None, + values_yaml: Some(values.to_string()), + create_namespace: true, + install_only: false, + repository: Some(HelmRepository::new( + "sarab97".to_string(), + url::Url::parse("https://charts.sarabsingh.com").unwrap(), + true, + )), + } +} diff --git a/harmony/src/modules/monitoring/ntfy/mod.rs b/harmony/src/modules/monitoring/ntfy/mod.rs new file mode 100644 index 0000000..df90432 --- /dev/null +++ b/harmony/src/modules/monitoring/ntfy/mod.rs @@ -0,0 +1,2 @@ +pub mod helm; +pub mod ntfy; diff --git a/harmony/src/modules/monitoring/ntfy/ntfy.rs b/harmony/src/modules/monitoring/ntfy/ntfy.rs new file mode 100644 index 0000000..0573b6d --- /dev/null +++ b/harmony/src/modules/monitoring/ntfy/ntfy.rs @@ -0,0 +1,33 @@ +use std::sync::{Arc, Mutex}; + +use crate::{ + interpret::{InterpretError, Outcome}, + inventory::Inventory, + modules::monitoring::ntfy::helm::{config::NtfyConfig, ntfy_helm_chart::ntfy_helm_chart_score}, + score::Score, + topology::{HelmCommand, K8sclient, Topology}, +}; + +pub struct Ntfy { + pub config: Arc>, +} + +impl Ntfy { + async fn install_ntfy( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result { + let result = ntfy_helm_chart_score(self.config.clone()) + .create_interpret() + .execute(inventory, topology) + .await; + + let client = topology.k8s_client().await.expect("couldn't get k8s client"); + + client.wait_until_deployment_ready("ntfy", self.config.get_mut().expect("couldn't get config").namespace); + client. + + result + } +} -- 2.39.5 From 1adc2db5d9b80f2e1cd4d52fd1140b9f267f5a4b Mon Sep 17 00:00:00 2001 From: tahahawa Date: Mon, 30 Jun 2025 11:57:06 -0400 Subject: [PATCH 3/6] it works! --- Cargo.lock | 110 ++++++++++-------- Cargo.toml | 4 +- examples/kube-rs/Cargo.toml | 2 +- examples/ntfy/src/main.rs | 32 +---- harmony/Cargo.toml | 1 + harmony/src/domain/interpret/mod.rs | 8 ++ harmony/src/domain/topology/k8s.rs | 59 ++++++---- .../monitoring/kube_prometheus/helm/config.rs | 4 +- .../modules/monitoring/ntfy/helm/config.rs | 6 - .../src/modules/monitoring/ntfy/helm/mod.rs | 1 - .../monitoring/ntfy/helm/ntfy_helm_chart.rs | 16 +-- harmony/src/modules/monitoring/ntfy/ntfy.rs | 84 ++++++++++--- 12 files changed, 190 insertions(+), 137 deletions(-) delete mode 100644 harmony/src/modules/monitoring/ntfy/helm/config.rs diff --git a/Cargo.lock b/Cargo.lock index db8298b..a4671df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,14 +232,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "backoff" -version = "0.4.0" +name = "backon" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +checksum = "302eaff5357a264a2c42f127ecb8bac761cf99749fc3dc95677e2743991f99e7" dependencies = [ - "getrandom 0.2.16", - "instant", - "rand 0.8.5", + "fastrand", + "gloo-timers", + "tokio", ] [[package]] @@ -930,6 +930,26 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "des" version = "0.8.1" @@ -1246,7 +1266,7 @@ dependencies = [ "harmony_macros", "http 1.3.1", "inquire", - "k8s-openapi 0.25.0", + "k8s-openapi", "kube", "log", "serde_yaml", @@ -1614,6 +1634,18 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "group" version = "0.13.0" @@ -1686,7 +1718,7 @@ dependencies = [ "http 1.3.1", "inquire", "k3d-rs", - "k8s-openapi 0.24.0", + "k8s-openapi", "kube", "lazy_static", "libredfish", @@ -1707,6 +1739,7 @@ dependencies = [ "temp-dir", "temp-file", "tokio", + "tokio-util", "url", "uuid", ] @@ -2376,15 +2409,6 @@ dependencies = [ "syn", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -2458,9 +2482,9 @@ dependencies = [ [[package]] name = "json-patch" -version = "3.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +checksum = "159294d661a039f7644cea7e4d844e6b25aaf71c1ffe9d73a96d768c24b0faf4" dependencies = [ "jsonptr", "serde", @@ -2483,9 +2507,9 @@ dependencies = [ [[package]] name = "jsonptr" -version = "0.6.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" dependencies = [ "serde", "serde_json", @@ -2525,19 +2549,6 @@ dependencies = [ "url", ] -[[package]] -name = "k8s-openapi" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c75b990324f09bef15e791606b7b7a296d02fc88a344f6eba9390970a870ad5" -dependencies = [ - "base64 0.22.1", - "chrono", - "serde", - "serde-value", - "serde_json", -] - [[package]] name = "k8s-openapi" version = "0.25.0" @@ -2552,11 +2563,11 @@ dependencies = [ [[package]] name = "kube" -version = "0.98.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32053dc495efad4d188c7b33cc7c02ef4a6e43038115348348876efd39a53cba" +checksum = "778f98664beaf4c3c11372721e14310d1ae00f5e2d9aabcf8906c881aa4e9f51" dependencies = [ - "k8s-openapi 0.24.0", + "k8s-openapi", "kube-client", "kube-core", "kube-runtime", @@ -2564,9 +2575,9 @@ dependencies = [ [[package]] name = "kube-client" -version = "0.98.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d34ad38cdfbd1fa87195d42569f57bb1dda6ba5f260ee32fef9570b7937a0c9" +checksum = "7cb276b85b6e94ded00ac8ea2c68fcf4697ea0553cb25fddc35d4a0ab718db8d" dependencies = [ "base64 0.22.1", "bytes", @@ -2583,12 +2594,10 @@ dependencies = [ "hyper-timeout", "hyper-util", "jsonpath-rust", - "k8s-openapi 0.24.0", + "k8s-openapi", "kube-core", "pem", - "rand 0.8.5", "rustls", - "rustls-pemfile 2.2.0", "secrecy", "serde", "serde_json", @@ -2604,15 +2613,16 @@ dependencies = [ [[package]] name = "kube-core" -version = "0.98.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97aa830b288a178a90e784d1b0f1539f2d200d2188c7b4a3146d9dc983d596f3" +checksum = "e3c56ff45deb0031f2a476017eed60c06872251f271b8387ad8020b8fef60960" dependencies = [ "chrono", + "derive_more", "form_urlencoded", "http 1.3.1", "json-patch", - "k8s-openapi 0.24.0", + "k8s-openapi", "serde", "serde-value", "serde_json", @@ -2621,22 +2631,20 @@ dependencies = [ [[package]] name = "kube-runtime" -version = "0.98.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a41af186a0fe80c71a13a13994abdc3ebff80859ca6a4b8a6079948328c135b" +checksum = "2f1326e946fadf6248febdf8a1c001809c3899ccf48cb9768cbc536b741040dc" dependencies = [ "ahash", "async-broadcast", "async-stream", - "async-trait", - "backoff", + "backon", "educe", "futures", "hashbrown 0.15.4", "hostname", "json-patch", - "jsonptr", - "k8s-openapi 0.24.0", + "k8s-openapi", "kube-client", "parking_lot", "pin-project", diff --git a/Cargo.toml b/Cargo.toml index da05b64..92bfc4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ russh = "0.45" russh-keys = "0.45" rand = "0.8" url = "2.5" -kube = { version = "0.98", features = [ +kube = { version = "1.1.0", features = [ "config", "client", "runtime", @@ -43,7 +43,7 @@ kube = { version = "0.98", features = [ "ws", "jsonpatch", ] } -k8s-openapi = { version = "0.24", features = ["v1_30"] } +k8s-openapi = { version = "0.25", features = ["v1_30"] } serde_yaml = "0.9" serde-value = "0.7" http = "1.2" diff --git a/examples/kube-rs/Cargo.toml b/examples/kube-rs/Cargo.toml index e4cf5b7..7d81470 100644 --- a/examples/kube-rs/Cargo.toml +++ b/examples/kube-rs/Cargo.toml @@ -14,7 +14,7 @@ harmony_macros = { path = "../../harmony_macros" } log = { workspace = true } env_logger = { workspace = true } url = { workspace = true } -kube = "0.98.0" +kube = "1.1.0" k8s-openapi = { version = "0.25.0", features = ["v1_30"] } http = "1.2.0" serde_yaml = "0.9.34" diff --git a/examples/ntfy/src/main.rs b/examples/ntfy/src/main.rs index 86c1012..f359a61 100644 --- a/examples/ntfy/src/main.rs +++ b/examples/ntfy/src/main.rs @@ -1,36 +1,10 @@ -use std::{collections::HashMap, str::FromStr}; - use harmony::{ - inventory::Inventory, - maestro::Maestro, - modules::helm::chart::{HelmChartScore, HelmRepository, NonBlankString}, + inventory::Inventory, maestro::Maestro, modules::monitoring::ntfy::ntfy::NtfyScore, topology::K8sAnywhereTopology, }; #[tokio::main] async fn main() { - let mut ntfy_overrides: HashMap = HashMap::new(); - ntfy_overrides.insert( - NonBlankString::from_str("image.tag").unwrap(), - "v2.12.0".to_string(), - ); - - let ntfy_chart = HelmChartScore { - namespace: Some(NonBlankString::from_str("monitoring").unwrap()), - release_name: NonBlankString::from_str("ntfy").unwrap(), - chart_name: NonBlankString::from_str("sarab97/ntfy").unwrap(), - chart_version: Some(NonBlankString::from_str("0.1.7").unwrap()), - values_overrides: Some(ntfy_overrides), - values_yaml: None, - create_namespace: true, - install_only: false, - repository: Some(HelmRepository::new( - "sarab97".to_string(), - url::Url::parse("https://charts.sarabsingh.com").unwrap(), - true, - )), - }; - let mut maestro = Maestro::::initialize( Inventory::autoload(), K8sAnywhereTopology::from_env(), @@ -38,6 +12,8 @@ async fn main() { .await .unwrap(); - maestro.register_all(vec![Box::new(ntfy_chart)]); + maestro.register_all(vec![Box::new(NtfyScore { + namespace: "monitoring".to_string(), + })]); harmony_cli::init(maestro, None).await.unwrap(); } diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index f56c120..00582ef 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -55,3 +55,4 @@ temp-dir = "0.1.14" dyn-clone = "1.0.19" similar.workspace = true futures-util = "0.3.31" +tokio-util = "0.7.15" diff --git a/harmony/src/domain/interpret/mod.rs b/harmony/src/domain/interpret/mod.rs index 5e928cb..f5fc151 100644 --- a/harmony/src/domain/interpret/mod.rs +++ b/harmony/src/domain/interpret/mod.rs @@ -124,3 +124,11 @@ impl From for InterpretError { } } } + +impl From for InterpretError { + fn from(value: String) -> Self { + Self { + msg: format!("InterpretError : {value}"), + } + } +} diff --git a/harmony/src/domain/topology/k8s.rs b/harmony/src/domain/topology/k8s.rs index 52f28aa..32da4ac 100644 --- a/harmony/src/domain/topology/k8s.rs +++ b/harmony/src/domain/topology/k8s.rs @@ -1,24 +1,17 @@ use derive_new::new; -use futures_util::TryStreamExt; +use futures_util::StreamExt; use k8s_openapi::{ ClusterResourceScope, NamespaceResourceScope, api::{apps::v1::Deployment, core::v1::Pod}, }; use kube::runtime::conditions; -use kube::runtime::wait::{Condition, await_condition}; +use kube::runtime::wait::await_condition; use kube::{ Client, Config, Error, Resource, - api::{ - Api, AttachParams, AttachedProcess, DeleteParams, ListParams, Patch, PatchParams, - PostParams, ResourceExt, WatchEvent, WatchParams, - }, + api::{Api, AttachParams, ListParams, Patch, PatchParams, ResourceExt}, config::{KubeConfigOptions, Kubeconfig}, core::ErrorResponse, - runtime::{ - WatchStreamExt, metadata_watcher, - reflector::Lookup, - watcher::{self, watch_object}, - }, + runtime::reflector::Lookup, }; use log::{debug, error, trace}; use serde::de::DeserializeOwned; @@ -40,7 +33,8 @@ impl K8sClient { &self, name: String, namespace: Option<&str>, - ) -> Result<(), Error> { + timeout: Option, + ) -> Result<(), String> { let api: Api; if let Some(ns) = namespace { @@ -49,18 +43,22 @@ impl K8sClient { api = Api::default_namespaced(self.client.clone()); } - // need to upgrade to latest kube-rs version https://docs.rs/kube-runtime/latest/kube_runtime/wait/conditions/fn.is_deployment_completed.html let establish = await_condition(api, name.as_str(), conditions::is_deployment_completed()); - let _ = tokio::time::timeout(std::time::Duration::from_secs(300), establish).await?; + let t = if let Some(t) = timeout { t } else { 300 }; + let res = tokio::time::timeout(std::time::Duration::from_secs(t), establish).await; - Ok(()) + if let Ok(r) = res { + return Ok(()); + } else { + return Err("timed out while waiting for deployment".to_string()); + } } pub async fn exec_pod( &self, name: String, namespace: Option<&str>, - command: Vec, + command: Vec<&str>, ) -> Result<(), String> { let api: Api; @@ -74,10 +72,8 @@ impl K8sClient { .await .expect("couldn't get list of pods"); - if pod_list.items.len() > 1 { - return Err("too many pods".into()); - } else { - api.exec( + let res = api + .exec( pod_list .items .first() @@ -90,9 +86,28 @@ impl K8sClient { &AttachParams::default(), ) .await; - } - Ok(()) + match res { + Err(e) => return Err(e.to_string()), + Ok(mut process) => { + let status = process + .take_status() + .expect("Couldn't get status") + .await + .expect("Couldn't unwrap status"); + + if let Some(s) = status.status { + debug!("Status: {}", s); + if s == "Success" { + return Ok(()); + } else { + return Err(s); + } + } else { + return Err("Couldn't get inner status of pod exec".to_string()); + } + } + } } /// Apply a resource in namespace diff --git a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs index 8e83fd7..c0f614d 100644 --- a/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs +++ b/harmony/src/modules/monitoring/kube_prometheus/helm/config.rs @@ -1,6 +1,8 @@ use serde::Serialize; -use crate::modules::monitoring::kube_prometheus::types::{AlertManagerAdditionalPromRules, AlertManagerChannelConfig}; +use crate::modules::monitoring::kube_prometheus::types::{ + AlertManagerAdditionalPromRules, AlertManagerChannelConfig, +}; #[derive(Debug, Clone, Serialize)] pub struct KubePrometheusConfig { diff --git a/harmony/src/modules/monitoring/ntfy/helm/config.rs b/harmony/src/modules/monitoring/ntfy/helm/config.rs deleted file mode 100644 index 14a2fdd..0000000 --- a/harmony/src/modules/monitoring/ntfy/helm/config.rs +++ /dev/null @@ -1,6 +0,0 @@ -use serde::Serialize; - -#[derive(Debug, Clone, Serialize)] -pub struct NtfyConfig { - pub namespace: String, -} diff --git a/harmony/src/modules/monitoring/ntfy/helm/mod.rs b/harmony/src/modules/monitoring/ntfy/helm/mod.rs index 9a39996..40d1524 100644 --- a/harmony/src/modules/monitoring/ntfy/helm/mod.rs +++ b/harmony/src/modules/monitoring/ntfy/helm/mod.rs @@ -1,2 +1 @@ -pub mod config; pub mod ntfy_helm_chart; diff --git a/harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs b/harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs index fd3c4aa..db7d9c4 100644 --- a/harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs +++ b/harmony/src/modules/monitoring/ntfy/helm/ntfy_helm_chart.rs @@ -1,17 +1,9 @@ use non_blank_string_rs::NonBlankString; -use std::{ - str::FromStr, - sync::{Arc, Mutex}, -}; +use std::str::FromStr; -use crate::modules::{ - helm::chart::{HelmChartScore, HelmRepository}, - monitoring::ntfy::helm::config::NtfyConfig, -}; - -pub fn ntfy_helm_chart_score(config: Arc>) -> HelmChartScore { - let config = config.lock().unwrap(); +use crate::modules::helm::chart::{HelmChartScore, HelmRepository}; +pub fn ntfy_helm_chart_score(namespace: String) -> HelmChartScore { let values = format!( r#" replicaCount: 1 @@ -74,7 +66,7 @@ persistence: ); HelmChartScore { - namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()), + namespace: Some(NonBlankString::from_str(&namespace).unwrap()), release_name: NonBlankString::from_str("ntfy").unwrap(), chart_name: NonBlankString::from_str("sarab97/ntfy").unwrap(), chart_version: Some(NonBlankString::from_str("0.1.7").unwrap()), diff --git a/harmony/src/modules/monitoring/ntfy/ntfy.rs b/harmony/src/modules/monitoring/ntfy/ntfy.rs index 0573b6d..33e7b73 100644 --- a/harmony/src/modules/monitoring/ntfy/ntfy.rs +++ b/harmony/src/modules/monitoring/ntfy/ntfy.rs @@ -1,33 +1,91 @@ -use std::sync::{Arc, Mutex}; +use async_trait::async_trait; +use log::debug; +use serde::Serialize; use crate::{ - interpret::{InterpretError, Outcome}, + data::{Id, Version}, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, - modules::monitoring::ntfy::helm::{config::NtfyConfig, ntfy_helm_chart::ntfy_helm_chart_score}, + modules::monitoring::ntfy::helm::ntfy_helm_chart::ntfy_helm_chart_score, score::Score, topology::{HelmCommand, K8sclient, Topology}, }; -pub struct Ntfy { - pub config: Arc>, +#[derive(Debug, Clone, Serialize)] +pub struct NtfyScore { + pub namespace: String, } -impl Ntfy { - async fn install_ntfy( +impl Score for NtfyScore { + fn create_interpret(&self) -> Box> { + Box::new(NtfyInterpret { + score: self.clone(), + }) + } + + fn name(&self) -> String { + format!("Ntfy") + } +} + +#[derive(Debug, Serialize)] +pub struct NtfyInterpret { + pub score: NtfyScore, +} + +#[async_trait] +impl Interpret for NtfyInterpret { + async fn execute( &self, inventory: &Inventory, topology: &T, ) -> Result { - let result = ntfy_helm_chart_score(self.config.clone()) + ntfy_helm_chart_score(self.score.namespace.clone()) .create_interpret() .execute(inventory, topology) - .await; + .await?; - let client = topology.k8s_client().await.expect("couldn't get k8s client"); + debug!("installed ntfy helm chart"); + let client = topology + .k8s_client() + .await + .expect("couldn't get k8s client"); - client.wait_until_deployment_ready("ntfy", self.config.get_mut().expect("couldn't get config").namespace); - client. + client + .wait_until_deployment_ready( + "ntfy".to_string(), + Some(&self.score.namespace.as_str()), + None, + ) + .await?; + debug!("created k8s client"); - result + client + .exec_pod( + "ntfy".to_string(), + Some(&self.score.namespace), + vec![ + "sh", + "-c", + "NTFY_PASSWORD=harmony ntfy user add --role=admin harmony", + ], + ) + .await?; + debug!("exec into pod done"); + + Ok(Outcome::success("installed ntfy".to_string())) + } + + fn get_name(&self) -> InterpretName { + todo!() + } + fn get_version(&self) -> Version { + todo!() + } + fn get_status(&self) -> InterpretStatus { + todo!() + } + fn get_children(&self) -> Vec { + todo!() } } -- 2.39.5 From 9172da1d161d1c4989504493c7f9804fcc718b6c Mon Sep 17 00:00:00 2001 From: tahahawa Date: Mon, 30 Jun 2025 15:48:52 -0400 Subject: [PATCH 4/6] add some helper functions --- Cargo.lock | 27 +++++- harmony/Cargo.toml | 1 + harmony/src/modules/monitoring/ntfy/ntfy.rs | 99 ++++++++++++++++++--- 3 files changed, 114 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4671df..1e3f7f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,6 +1736,7 @@ dependencies = [ "serde_json", "serde_yaml", "similar", + "strum 0.27.1", "temp-dir", "temp-file", "tokio", @@ -3552,7 +3553,7 @@ dependencies = [ "itertools", "lru", "paste", - "strum", + "strum 0.26.3", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", @@ -4511,7 +4512,16 @@ version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros", + "strum_macros 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +dependencies = [ + "strum_macros 0.27.1", ] [[package]] @@ -4527,6 +4537,19 @@ dependencies = [ "syn", ] +[[package]] +name = "strum_macros" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/harmony/Cargo.toml b/harmony/Cargo.toml index 00582ef..97ed693 100644 --- a/harmony/Cargo.toml +++ b/harmony/Cargo.toml @@ -56,3 +56,4 @@ dyn-clone = "1.0.19" similar.workspace = true futures-util = "0.3.31" tokio-util = "0.7.15" +strum = { version = "0.27.1", features = ["derive"] } diff --git a/harmony/src/modules/monitoring/ntfy/ntfy.rs b/harmony/src/modules/monitoring/ntfy/ntfy.rs index 33e7b73..0a4d90e 100644 --- a/harmony/src/modules/monitoring/ntfy/ntfy.rs +++ b/harmony/src/modules/monitoring/ntfy/ntfy.rs @@ -1,6 +1,9 @@ +use std::sync::Arc; + use async_trait::async_trait; use log::debug; use serde::Serialize; +use strum::{Display, EnumString}; use crate::{ data::{Id, Version}, @@ -8,7 +11,7 @@ use crate::{ inventory::Inventory, modules::monitoring::ntfy::helm::ntfy_helm_chart::ntfy_helm_chart_score, score::Score, - topology::{HelmCommand, K8sclient, Topology}, + topology::{HelmCommand, K8sclient, Topology, k8s::K8sClient}, }; #[derive(Debug, Clone, Serialize)] @@ -33,6 +36,88 @@ pub struct NtfyInterpret { pub score: NtfyScore, } +#[derive(Debug, EnumString, Display)] +enum NtfyAccessMode { + #[strum(serialize = "read-write", serialize = "rw", to_string = "read-write")] + ReadWrite, + #[strum( + serialize = "read-only", + serialize = "ro", + serialize = "read", + to_string = "read-only" + )] + ReadOnly, + #[strum( + serialize = "write-only", + serialize = "wo", + serialize = "write", + to_string = "write-only" + )] + WriteOnly, + #[strum(serialize = "none", to_string = "deny")] + Deny, +} + +#[derive(Debug, EnumString, Display)] +enum NtfyRole { + #[strum(serialize = "user", to_string = "user")] + User, + #[strum(serialize = "admin", to_string = "admin")] + Admin, +} + +impl NtfyInterpret { + async fn add_user( + &self, + k8s_client: Arc, + username: &str, + password: &str, + role: Option, + ) -> Result<(), String> { + let role = match role { + Some(r) => r, + None => NtfyRole::User, + }; + + k8s_client + .exec_pod( + "ntfy".to_string(), + Some(&self.score.namespace), + vec![ + "sh", + "-c", + format!("NTFY_PASSWORD={password} ntfy user add --role={role} {username}") + .as_str(), + ], + ) + .await?; + + Ok(()) + } + + async fn set_access( + &self, + k8s_client: Arc, + username: &str, + topic: &str, + mode: NtfyAccessMode, + ) -> Result<(), String> { + k8s_client + .exec_pod( + "ntfy".to_string(), + Some(&self.score.namespace), + vec![ + "sh", + "-c", + format!("ntfy access {username} {topic} {mode}").as_str(), + ], + ) + .await?; + + Ok(()) + } +} + #[async_trait] impl Interpret for NtfyInterpret { async fn execute( @@ -60,17 +145,9 @@ impl Interpret for NtfyInterpret { .await?; debug!("created k8s client"); - client - .exec_pod( - "ntfy".to_string(), - Some(&self.score.namespace), - vec![ - "sh", - "-c", - "NTFY_PASSWORD=harmony ntfy user add --role=admin harmony", - ], - ) + self.add_user(client, "harmony", "harmony", Some(NtfyRole::Admin)) .await?; + debug!("exec into pod done"); Ok(Outcome::success("installed ntfy".to_string())) -- 2.39.5 From bed814837c0703b4087615c800f2b6c74d70675b Mon Sep 17 00:00:00 2001 From: tahahawa Date: Wed, 2 Jul 2025 12:05:42 -0400 Subject: [PATCH 5/6] fix comments --- harmony/src/domain/topology/k8s.rs | 3 ++- harmony/src/modules/monitoring/ntfy/ntfy.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/harmony/src/domain/topology/k8s.rs b/harmony/src/domain/topology/k8s.rs index 32da4ac..c01e6d4 100644 --- a/harmony/src/domain/topology/k8s.rs +++ b/harmony/src/domain/topology/k8s.rs @@ -54,7 +54,8 @@ impl K8sClient { } } - pub async fn exec_pod( + /// Will execute a command in the first pod found that matches the label `app.kubernetes.io/name={name}` + pub async fn exec_app( &self, name: String, namespace: Option<&str>, diff --git a/harmony/src/modules/monitoring/ntfy/ntfy.rs b/harmony/src/modules/monitoring/ntfy/ntfy.rs index 0a4d90e..b8b5cbe 100644 --- a/harmony/src/modules/monitoring/ntfy/ntfy.rs +++ b/harmony/src/modules/monitoring/ntfy/ntfy.rs @@ -118,6 +118,7 @@ impl NtfyInterpret { } } +/// We need a ntfy interpret to wrap the HelmChartScore in order to run the score, and then bootstrap the config inside ntfy #[async_trait] impl Interpret for NtfyInterpret { async fn execute( -- 2.39.5 From 8b0fc881e1ed2e84c9dbd1b34cae59351e0157c2 Mon Sep 17 00:00:00 2001 From: tahahawa Date: Wed, 2 Jul 2025 12:09:08 -0400 Subject: [PATCH 6/6] fix func call name --- harmony/src/modules/monitoring/ntfy/ntfy.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/harmony/src/modules/monitoring/ntfy/ntfy.rs b/harmony/src/modules/monitoring/ntfy/ntfy.rs index b8b5cbe..98d4fff 100644 --- a/harmony/src/modules/monitoring/ntfy/ntfy.rs +++ b/harmony/src/modules/monitoring/ntfy/ntfy.rs @@ -80,7 +80,7 @@ impl NtfyInterpret { }; k8s_client - .exec_pod( + .exec_app( "ntfy".to_string(), Some(&self.score.namespace), vec![ @@ -103,7 +103,7 @@ impl NtfyInterpret { mode: NtfyAccessMode, ) -> Result<(), String> { k8s_client - .exec_pod( + .exec_app( "ntfy".to_string(), Some(&self.score.namespace), vec![ -- 2.39.5