Compare commits
19 Commits
monitoring
...
feat/teams
| Author | SHA1 | Date | |
|---|---|---|---|
| f96572848a | |||
| 78aadadd22 | |||
| b5c6e1c99d | |||
| f94c899bf7 | |||
| 77eb1228be | |||
| e1a8ee1c15 | |||
| 44b2b092a8 | |||
| 2b6d2e8606 | |||
| 7fc2b1ebfe | |||
| e80752ea3f | |||
| bae7222d64 | |||
| f7d3da3ac9 | |||
| eb8a8a2e04 | |||
| b4c6848433 | |||
| 0d94c537a0 | |||
| 861f266c4e | |||
| 51724d0e55 | |||
| c2d1cb9b76 | |||
|
|
c84a02c8ec |
5
.cargo/config.toml
Normal file
5
.cargo/config.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[target.x86_64-pc-windows-msvc]
|
||||||
|
rustflags = ["-C", "link-arg=/STACK:8000000"]
|
||||||
|
|
||||||
|
[target.x86_64-pc-windows-gnu]
|
||||||
|
rustflags = ["-C", "link-arg=-Wl,--stack,8000000"]
|
||||||
390
Cargo.lock
generated
390
Cargo.lock
generated
@@ -4,19 +4,13 @@ version = 4
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "addr2line"
|
name = "addr2line"
|
||||||
version = "0.21.0"
|
version = "0.24.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gimli",
|
"gimli",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler2"
|
name = "adler2"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -60,15 +54,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.11"
|
version = "0.8.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"const-random",
|
"const-random",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy 0.7.35",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -198,17 +192,17 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.71"
|
version = "0.3.75"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
|
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"miniz_oxide 0.7.4",
|
"miniz_oxide",
|
||||||
"object",
|
"object",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -254,9 +248,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.0"
|
version = "2.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -356,9 +350,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.20"
|
version = "1.2.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a"
|
checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -413,9 +407,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.37"
|
version = "4.5.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -423,9 +417,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.37"
|
version = "4.5.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -453,9 +447,9 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color-eyre"
|
name = "color-eyre"
|
||||||
version = "0.6.3"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
|
checksum = "e6e1761c0e16f8883bbbb8ce5990867f4f06bf11a0253da6495a04ce4b6ef0ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"color-spantrace",
|
"color-spantrace",
|
||||||
@@ -468,9 +462,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "color-spantrace"
|
name = "color-spantrace"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
|
checksum = "2ddd8d5bfda1e11a501d0a7303f3bfed9aa632ebdb859be40d0fd70478ed70d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
@@ -614,7 +608,7 @@ version = "0.28.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"mio 1.0.3",
|
"mio 1.0.3",
|
||||||
@@ -936,6 +930,15 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "email_address"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@@ -1121,7 +1124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide 0.8.8",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1172,6 +1175,16 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fqdn"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0f5d7f7b3eed2f771fc7f6fcb651f9560d7b0c483d75876082acb4649d266b3"
|
||||||
|
dependencies = [
|
||||||
|
"punycode",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "funty"
|
name = "funty"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -1311,9 +1324,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.3.2"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1333,9 +1346,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.28.1"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "group"
|
name = "group"
|
||||||
@@ -1369,9 +1382,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.9"
|
version = "0.4.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
|
checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1396,7 +1409,9 @@ dependencies = [
|
|||||||
"derive-new",
|
"derive-new",
|
||||||
"directories",
|
"directories",
|
||||||
"dockerfile_builder",
|
"dockerfile_builder",
|
||||||
|
"email_address",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"fqdn",
|
||||||
"harmony_macros",
|
"harmony_macros",
|
||||||
"harmony_types",
|
"harmony_types",
|
||||||
"helm-wrapper-rs",
|
"helm-wrapper-rs",
|
||||||
@@ -1419,6 +1434,7 @@ dependencies = [
|
|||||||
"serde-value",
|
"serde-value",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
"temp-dir",
|
||||||
"temp-file",
|
"temp-file",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
@@ -1476,9 +1492,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.2"
|
version = "0.15.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"equivalent",
|
"equivalent",
|
||||||
@@ -1692,7 +1708,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.9",
|
"h2 0.4.10",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"httparse",
|
"httparse",
|
||||||
@@ -1831,21 +1847,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "1.5.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
|
"potential_utf",
|
||||||
"yoke",
|
"yoke",
|
||||||
"zerofrom",
|
"zerofrom",
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_locid"
|
name = "icu_locale_core"
|
||||||
version = "1.5.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"litemap",
|
"litemap",
|
||||||
@@ -1854,31 +1871,11 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_locid_transform"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
|
||||||
dependencies = [
|
|
||||||
"displaydoc",
|
|
||||||
"icu_locid",
|
|
||||||
"icu_locid_transform_data",
|
|
||||||
"icu_provider",
|
|
||||||
"tinystr",
|
|
||||||
"zerovec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_locid_transform_data"
|
|
||||||
version = "1.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_normalizer"
|
name = "icu_normalizer"
|
||||||
version = "1.5.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"icu_collections",
|
"icu_collections",
|
||||||
@@ -1886,67 +1883,54 @@ dependencies = [
|
|||||||
"icu_properties",
|
"icu_properties",
|
||||||
"icu_provider",
|
"icu_provider",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"utf16_iter",
|
|
||||||
"utf8_iter",
|
|
||||||
"write16",
|
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_normalizer_data"
|
name = "icu_normalizer_data"
|
||||||
version = "1.5.1"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
|
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties"
|
name = "icu_properties"
|
||||||
version = "1.5.1"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
checksum = "2549ca8c7241c82f59c80ba2a6f415d931c5b58d24fb8412caa1a1f02c49139a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"icu_collections",
|
"icu_collections",
|
||||||
"icu_locid_transform",
|
"icu_locale_core",
|
||||||
"icu_properties_data",
|
"icu_properties_data",
|
||||||
"icu_provider",
|
"icu_provider",
|
||||||
"tinystr",
|
"potential_utf",
|
||||||
|
"zerotrie",
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_properties_data"
|
name = "icu_properties_data"
|
||||||
version = "1.5.1"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
|
checksum = "8197e866e47b68f8f7d95249e172903bec06004b18b2937f1095d40a0c57de04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_provider"
|
name = "icu_provider"
|
||||||
version = "1.5.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"icu_locid",
|
"icu_locale_core",
|
||||||
"icu_provider_macros",
|
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
"tinystr",
|
"tinystr",
|
||||||
"writeable",
|
"writeable",
|
||||||
"yoke",
|
"yoke",
|
||||||
"zerofrom",
|
"zerofrom",
|
||||||
|
"zerotrie",
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "icu_provider_macros"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -1966,9 +1950,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna_adapter"
|
name = "idna_adapter"
|
||||||
version = "1.2.0"
|
version = "1.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
|
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"icu_normalizer",
|
"icu_normalizer",
|
||||||
"icu_properties",
|
"icu_properties",
|
||||||
@@ -2012,7 +1996,7 @@ version = "0.7.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a"
|
checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"crossterm 0.25.0",
|
"crossterm 0.25.0",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
@@ -2075,9 +2059,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff"
|
name = "jiff"
|
||||||
version = "0.2.10"
|
version = "0.2.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
|
checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jiff-static",
|
"jiff-static",
|
||||||
"log",
|
"log",
|
||||||
@@ -2088,9 +2072,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff-static"
|
name = "jiff-static"
|
||||||
version = "0.2.10"
|
version = "0.2.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
|
checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2249,9 +2233,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.2.13"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72"
|
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredfish"
|
name = "libredfish"
|
||||||
@@ -2272,7 +2256,7 @@ version = "0.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2290,9 +2274,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.7.5"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
|
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
@@ -2346,15 +2330,6 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.7.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
|
||||||
dependencies = [
|
|
||||||
"adler",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.8"
|
version = "0.8.8"
|
||||||
@@ -2499,18 +2474,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.32.2"
|
version = "0.36.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "octocrab"
|
name = "octocrab"
|
||||||
version = "0.44.0"
|
version = "0.44.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aaf799a9982a4d0b4b3fa15b4c1ff7daf5bd0597f46456744dcbb6ddc2e4c827"
|
checksum = "86996964f8b721067b6ed238aa0ccee56ecad6ee5e714468aa567992d05d2b91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2564,7 +2539,7 @@ version = "0.10.72"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
|
checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2592,9 +2567,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-sys"
|
name = "openssl-sys"
|
||||||
version = "0.9.107"
|
version = "0.9.108"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07"
|
checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2658,9 +2633,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owo-colors"
|
name = "owo-colors"
|
||||||
version = "3.5.0"
|
version = "4.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
checksum = "1036865bb9422d3300cf723f657c2851d0e9ab12567854b1f4eba3d77decf564"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "p256"
|
name = "p256"
|
||||||
@@ -2946,6 +2921,15 @@ dependencies = [
|
|||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "potential_utf"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
|
||||||
|
dependencies = [
|
||||||
|
"zerovec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powerfmt"
|
name = "powerfmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -2958,7 +2942,7 @@ version = "0.2.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy 0.8.25",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3016,6 +3000,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "punycode"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9e1dcb320d6839f6edb64f7a4a59d39b30480d4d1765b56873f7c858538a5fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
@@ -3093,7 +3083,7 @@ version = "0.9.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.2",
|
"getrandom 0.3.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3102,7 +3092,7 @@ version = "0.29.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"cassowary",
|
"cassowary",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"crossterm 0.28.1",
|
"crossterm 0.28.1",
|
||||||
@@ -3119,11 +3109,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.11"
|
version = "0.5.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
|
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3217,7 +3207,7 @@ dependencies = [
|
|||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.9",
|
"h2 0.4.10",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
@@ -3306,7 +3296,7 @@ dependencies = [
|
|||||||
"aes",
|
"aes",
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cbc",
|
"cbc",
|
||||||
"chacha20",
|
"chacha20",
|
||||||
@@ -3407,7 +3397,7 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3bb94393cafad0530145b8f626d8687f1ee1dedb93d7ba7740d6ae81868b13b5"
|
checksum = "3bb94393cafad0530145b8f626d8687f1ee1dedb93d7ba7740d6ae81868b13b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"flurry",
|
"flurry",
|
||||||
@@ -3454,7 +3444,7 @@ version = "0.38.44"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.15",
|
"linux-raw-sys 0.4.15",
|
||||||
@@ -3463,11 +3453,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.0.5"
|
version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
|
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.9.4",
|
"linux-raw-sys 0.9.4",
|
||||||
@@ -3476,9 +3466,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.26"
|
version = "0.23.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
|
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -3534,15 +3524,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
|
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.103.1"
|
version = "0.103.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
|
checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
@@ -3625,7 +3618,7 @@ version = "2.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"core-foundation 0.9.4",
|
"core-foundation 0.9.4",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -3638,7 +3631,7 @@ version = "3.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
|
checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"core-foundation 0.10.0",
|
"core-foundation 0.10.0",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -3769,9 +3762,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.8"
|
version = "0.10.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
@@ -3795,9 +3788,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook"
|
name = "signal-hook"
|
||||||
version = "0.3.17"
|
version = "0.3.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
@@ -4033,9 +4026,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
version = "0.13.1"
|
version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4059,7 +4052,7 @@ version = "0.6.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"core-foundation 0.9.4",
|
"core-foundation 0.9.4",
|
||||||
"system-configuration-sys 0.6.0",
|
"system-configuration-sys 0.6.0",
|
||||||
]
|
]
|
||||||
@@ -4090,6 +4083,12 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "temp-dir"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "temp-file"
|
name = "temp-file"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@@ -4098,14 +4097,14 @@ checksum = "b5ff282c3f91797f0acb021f3af7fffa8a78601f0f2fd0a9f79ee7dcf9a9af9e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.19.1"
|
version = "3.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom 0.3.2",
|
"getrandom 0.3.3",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 1.0.5",
|
"rustix 1.0.7",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4207,9 +4206,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.7.6"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"displaydoc",
|
"displaydoc",
|
||||||
"zerovec",
|
"zerovec",
|
||||||
@@ -4217,9 +4216,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.44.2"
|
version = "1.45.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
|
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4306,12 +4305,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.6.2"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
|
checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.3.1",
|
"http 1.3.1",
|
||||||
@@ -4493,12 +4492,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf16_iter"
|
|
||||||
version = "1.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8_iter"
|
name = "utf8_iter"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -4517,7 +4510,7 @@ version = "1.16.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.2",
|
"getrandom 0.3.3",
|
||||||
"rand 0.9.1",
|
"rand 0.9.1",
|
||||||
"uuid-macro-internal",
|
"uuid-macro-internal",
|
||||||
]
|
]
|
||||||
@@ -5018,20 +5011,14 @@ version = "0.39.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.0",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "write16"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "writeable"
|
name = "writeable"
|
||||||
version = "0.5.5"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
@@ -5080,9 +5067,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.7.5"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
@@ -5092,9 +5079,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke-derive"
|
name = "yoke-derive"
|
||||||
version = "0.7.5"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -5102,33 +5089,13 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.7.35"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive 0.7.35",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.25"
|
version = "0.8.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive 0.8.25",
|
"zerocopy-derive",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.7.35"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5170,10 +5137,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec"
|
name = "zerotrie"
|
||||||
version = "0.10.4"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
|
||||||
|
dependencies = [
|
||||||
|
"displaydoc",
|
||||||
|
"yoke",
|
||||||
|
"zerofrom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerovec"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"yoke",
|
"yoke",
|
||||||
"zerofrom",
|
"zerofrom",
|
||||||
@@ -5182,9 +5160,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec-derive"
|
name = "zerovec-derive"
|
||||||
version = "0.10.3"
|
version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -16,3 +16,5 @@ harmony_macros = { path = "../../harmony_macros" }
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
env_logger = { workspace = true }
|
env_logger = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
typetag = "0.2.20"
|
||||||
|
serde = "1.0.219"
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ use harmony::{
|
|||||||
data::Version,
|
data::Version,
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
maestro::Maestro,
|
maestro::Maestro,
|
||||||
modules::lamp::{LAMPConfig, LAMPScore},
|
modules::{
|
||||||
|
lamp::{LAMPConfig, LAMPScore},
|
||||||
|
monitoring::{kube_prometheus::prometheus_alert_channel::{DiscordChannel, SlackChannel}, monitoring_alerting::MonitoringAlertingScore},
|
||||||
|
},
|
||||||
topology::{K8sAnywhereTopology, Url},
|
topology::{K8sAnywhereTopology, Url},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,22 +27,47 @@ async fn main() {
|
|||||||
// This config can be extended as needed for more complicated configurations
|
// This config can be extended as needed for more complicated configurations
|
||||||
config: LAMPConfig {
|
config: LAMPConfig {
|
||||||
project_root: "./php".into(),
|
project_root: "./php".into(),
|
||||||
database_size: format!("2Gi").into(),
|
database_size: format!("4Gi").into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// You can choose the type of Topology you want, we suggest starting with the
|
// You can choose the type of Topology you want, we suggest starting with the
|
||||||
// K8sAnywhereTopology as it is the most automatic one that enables you to easily deploy
|
// K8sAnywhereTopology as it is the most automatic one that enables you to easily deploy
|
||||||
// locally, to development environment from a CI, to staging, and to production with settings
|
// locally, to development environment from a CI, to staging, and to production with settings
|
||||||
// that automatically adapt to each environment grade.
|
// that automatically adapt to each environment grade.
|
||||||
let mut maestro = Maestro::<K8sAnywhereTopology>::initialize(
|
let mut maestro = Maestro::<K8sAnywhereTopology>::initialize (
|
||||||
Inventory::autoload(),
|
Inventory::autoload(),
|
||||||
K8sAnywhereTopology::new(),
|
K8sAnywhereTopology::new(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
maestro.register_all(vec![Box::new(lamp_stack)]);
|
|
||||||
|
let url = url::Url::parse(
|
||||||
|
"https://hooks.slack.com/services/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",
|
||||||
|
)
|
||||||
|
.expect("invalid URL");
|
||||||
|
|
||||||
|
let mut monitoring_stack_score = MonitoringAlertingScore::new();
|
||||||
|
monitoring_stack_score.namespace = Some(lamp_stack.config.namespace.clone());
|
||||||
|
monitoring_stack_score.alert_channels = vec![(Box::new(SlackChannel {
|
||||||
|
name: "alert-test".to_string(),
|
||||||
|
webhook_url: url,})),
|
||||||
|
(Box::new(DiscordChannel {
|
||||||
|
name: "discord".to_string(),
|
||||||
|
webhook_url: url::Url::parse("https://discord.com/api/webhooks/1372994201746276462/YRn4TA9pj8ve3lfmyj1j0Yx97i92gv4U_uavt4CV4_SSIVArYUqfDzMOmzSTic2d8XSL").expect("invalid URL"),}))];
|
||||||
|
|
||||||
|
|
||||||
|
//TODO in process of testing
|
||||||
|
//webhook depricated in MSTeams August 2025
|
||||||
|
//(AlertChannel::MSTeams {
|
||||||
|
// connector: "alert-test".to_string(),
|
||||||
|
// webhook_url: url::Url::parse("").expect("invalid URL"),
|
||||||
|
//}),
|
||||||
|
|
||||||
|
|
||||||
|
maestro.register_all(vec![Box::new(monitoring_stack_score)]);
|
||||||
// Here we bootstrap the CLI, this gives some nice features if you need them
|
// Here we bootstrap the CLI, this gives some nice features if you need them
|
||||||
harmony_cli::init(maestro, None).await.unwrap();
|
harmony_cli::init(maestro, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
14
examples/ms_teams_alert_channel/Cargo.toml
Normal file
14
examples/ms_teams_alert_channel/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "ms_teams_alert_channel"
|
||||||
|
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" }
|
||||||
|
serde = "1.0.219"
|
||||||
|
tokio.workspace = true
|
||||||
|
typetag = "0.2.20"
|
||||||
|
url.workspace = true
|
||||||
65
examples/ms_teams_alert_channel/src/main.rs
Normal file
65
examples/ms_teams_alert_channel/src/main.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
mod prometheus_msteams;
|
||||||
|
use harmony::{
|
||||||
|
interpret::InterpretError, inventory::Inventory, maestro::Maestro, modules::{helm::chart::HelmChartScore, monitoring::{kube_prometheus::{prometheus_alert_channel::PrometheusAlertChannel, types::{AlertChannelConfig, AlertChannelReceiver, AlertChannelRoute, WebhookConfig}}, monitoring_alerting::MonitoringAlertingScore}}, topology::K8sAnywhereTopology
|
||||||
|
};
|
||||||
|
use prometheus_msteams::prometheus_msteams_score;
|
||||||
|
use url::Url;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let alert_channels: Vec<Box<dyn PrometheusAlertChannel>> = vec![Box::new(MSTeamsChannel {
|
||||||
|
connector: "teams-test".to_string(),
|
||||||
|
webhook_url: url::Url::parse(
|
||||||
|
"https://msteams.com/services/dummy/dummy/dummy",
|
||||||
|
)
|
||||||
|
.expect("invalid URL"),
|
||||||
|
})];
|
||||||
|
|
||||||
|
let monitoring_score = MonitoringAlertingScore {
|
||||||
|
alert_channels,
|
||||||
|
namespace: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut maestro = Maestro::<K8sAnywhereTopology>::initialize(
|
||||||
|
Inventory::autoload(),
|
||||||
|
K8sAnywhereTopology::new(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
maestro.register_all(vec![Box::new(monitoring_score)]);
|
||||||
|
harmony_cli::init(maestro, None).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct MSTeamsChannel {
|
||||||
|
connector: String,
|
||||||
|
webhook_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl PrometheusAlertChannel for MSTeamsChannel {
|
||||||
|
fn get_alert_manager_config_contribution(&self) -> Result<AlertChannelConfig, InterpretError> {
|
||||||
|
Ok(AlertChannelConfig{
|
||||||
|
receiver: AlertChannelReceiver{
|
||||||
|
name: format!("MSTeams-{}",self.connector),
|
||||||
|
slack_configs: None,
|
||||||
|
webhook_configs: Some(vec![WebhookConfig{
|
||||||
|
url: url::Url::parse("http://prometheus-msteams-prometheus-msteams.monitoring.svc.cluster.local:2000/alertmanager").expect("invalid url"),
|
||||||
|
send_resolved: true,}])
|
||||||
|
},
|
||||||
|
route: AlertChannelRoute{
|
||||||
|
receiver: format!("MSTeams-{}", self.connector),
|
||||||
|
matchers: vec!["alertname!=Watchdog".to_string()],
|
||||||
|
r#continue: true,
|
||||||
|
},
|
||||||
|
global_config: None, })
|
||||||
|
}
|
||||||
|
fn get_dependency_score(&self, ns: String) -> Option<HelmChartScore> {
|
||||||
|
Some(prometheus_msteams_score(self.connector.clone(), self.webhook_url.clone(), ns.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
30
examples/ms_teams_alert_channel/src/prometheus_msteams.rs
Normal file
30
examples/ms_teams_alert_channel/src/prometheus_msteams.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use harmony::modules::helm::chart::{HelmChartScore, NonBlankString};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
pub fn prometheus_msteams_score(
|
||||||
|
name: String,
|
||||||
|
webhook_url: Url,
|
||||||
|
namespace: String,
|
||||||
|
) -> HelmChartScore {
|
||||||
|
let values = format!(
|
||||||
|
r#"
|
||||||
|
connectors:
|
||||||
|
- default: "{webhook_url}"
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
HelmChartScore {
|
||||||
|
namespace: Some(NonBlankString::from_str(&namespace).unwrap()),
|
||||||
|
release_name: NonBlankString::from_str(&name).unwrap(),
|
||||||
|
chart_name: NonBlankString::from_str("oci://hub.nationtech.io/library/prometheus-msteams")
|
||||||
|
.unwrap(),
|
||||||
|
chart_version: None,
|
||||||
|
values_overrides: None,
|
||||||
|
values_yaml: Some(values.to_string()),
|
||||||
|
create_namespace: true,
|
||||||
|
install_only: true,
|
||||||
|
repository: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,3 +39,15 @@ lazy_static = "1.5.0"
|
|||||||
dockerfile_builder = "0.1.5"
|
dockerfile_builder = "0.1.5"
|
||||||
temp-file = "0.1.9"
|
temp-file = "0.1.9"
|
||||||
convert_case.workspace = true
|
convert_case.workspace = true
|
||||||
|
email_address = "0.2.9"
|
||||||
|
fqdn = { version = "0.4.6", features = [
|
||||||
|
"domain-label-cannot-start-or-end-with-hyphen",
|
||||||
|
"domain-label-length-limited-to-63",
|
||||||
|
"domain-name-without-special-chars",
|
||||||
|
"domain-name-length-limited-to-255",
|
||||||
|
"punycode",
|
||||||
|
"serde",
|
||||||
|
] }
|
||||||
|
temp-dir = "0.1.14"
|
||||||
|
typetag = "0.2.20"
|
||||||
|
dyn-clone = "1.0.19"
|
||||||
|
|||||||
@@ -92,9 +92,7 @@ impl K8sAnywhereTopology {
|
|||||||
|
|
||||||
async fn try_get_or_install_k8s_client(&self) -> Result<Option<K8sState>, InterpretError> {
|
async fn try_get_or_install_k8s_client(&self) -> Result<Option<K8sState>, InterpretError> {
|
||||||
let k8s_anywhere_config = K8sAnywhereConfig {
|
let k8s_anywhere_config = K8sAnywhereConfig {
|
||||||
kubeconfig: std::env::var("HARMONY_KUBECONFIG")
|
kubeconfig: std::env::var("KUBECONFIG").ok().map(|v| v.to_string()),
|
||||||
.ok()
|
|
||||||
.map(|v| v.to_string()),
|
|
||||||
use_system_kubeconfig: std::env::var("HARMONY_USE_SYSTEM_KUBECONFIG")
|
use_system_kubeconfig: std::env::var("HARMONY_USE_SYSTEM_KUBECONFIG")
|
||||||
.map_or_else(|_| false, |v| v.parse().ok().unwrap_or(false)),
|
.map_or_else(|_| false, |v| v.parse().ok().unwrap_or(false)),
|
||||||
autoinstall: std::env::var("HARMONY_AUTOINSTALL")
|
autoinstall: std::env::var("HARMONY_AUTOINSTALL")
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
pub mod monitoring_alerting;
|
|
||||||
mod ha_cluster;
|
mod ha_cluster;
|
||||||
mod host_binding;
|
mod host_binding;
|
||||||
mod http;
|
mod http;
|
||||||
|
|||||||
@@ -1,108 +0,0 @@
|
|||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use log::warn;
|
|
||||||
use tokio::sync::OnceCell;
|
|
||||||
|
|
||||||
use k8s_openapi::api::core::v1::Pod;
|
|
||||||
use kube::{
|
|
||||||
Client,
|
|
||||||
api::{Api, ListParams},
|
|
||||||
};
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
interpret::{InterpretError, Outcome},
|
|
||||||
inventory::Inventory,
|
|
||||||
maestro::Maestro,
|
|
||||||
modules::monitoring::monitoring_alerting::MonitoringAlertingStackScore,
|
|
||||||
score::Score,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{HelmCommand, K8sAnywhereTopology, Topology, k8s::K8sClient};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct MonitoringState {
|
|
||||||
message: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MonitoringAlertingTopology {
|
|
||||||
monitoring_state: OnceCell<Option<MonitoringState>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MonitoringAlertingTopology {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
monitoring_state: OnceCell::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_monitoring_state(&self) -> Result<Option<MonitoringState>, InterpretError> {
|
|
||||||
let client = Client::try_default()
|
|
||||||
.await
|
|
||||||
.map_err(|e| InterpretError::new(format!("Kubernetes client error: {}", e)))?;
|
|
||||||
|
|
||||||
for ns in &["monitoring", "openshift-monitoring"] {
|
|
||||||
let pods: Api<Pod> = Api::namespaced(client.clone(), ns);
|
|
||||||
//TODO hardcoding the label is a problem
|
|
||||||
//check all pods are ready
|
|
||||||
let lp = ListParams::default().labels("app.kubernetes.io/name=prometheus");
|
|
||||||
|
|
||||||
match pods.list(&lp).await {
|
|
||||||
Ok(pod_list) => {
|
|
||||||
for p in pod_list.items {
|
|
||||||
if let Some(status) = p.status {
|
|
||||||
if let Some(conditions) = status.conditions {
|
|
||||||
if conditions
|
|
||||||
.iter()
|
|
||||||
.any(|c| c.type_ == "Ready" && c.status == "True")
|
|
||||||
{
|
|
||||||
return Ok(Some(MonitoringState {
|
|
||||||
message: format!(
|
|
||||||
"Prometheus is ready in namespace: {}",
|
|
||||||
ns
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Failed to query pods in ns {}: {}", ns, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Topology> Clone for Box<dyn Score<T>> {
|
|
||||||
fn clone(&self) -> Box<dyn Score<T>> {
|
|
||||||
self.clone_box()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl Topology for MonitoringAlertingTopology {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"MonitoringAlertingTopology"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn ensure_ready(&self) -> Result<Outcome, InterpretError> {
|
|
||||||
if let Some(state) = self.get_monitoring_state().await? {
|
|
||||||
// Monitoring stack is already ready — stop app.
|
|
||||||
println!("{}", state.message);
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Monitoring not found — proceed with installation.
|
|
||||||
Ok(Outcome::success(
|
|
||||||
"Monitoring stack installation started.".to_string(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HelmCommand for MonitoringAlertingTopology {}
|
|
||||||
@@ -370,10 +370,13 @@ mod tests {
|
|||||||
let result = get_servers_for_backend(&backend, &haproxy);
|
let result = get_servers_for_backend(&backend, &haproxy);
|
||||||
|
|
||||||
// Check the result
|
// Check the result
|
||||||
assert_eq!(result, vec![BackendServer {
|
assert_eq!(
|
||||||
address: "192.168.1.1".to_string(),
|
result,
|
||||||
port: 80,
|
vec![BackendServer {
|
||||||
},]);
|
address: "192.168.1.1".to_string(),
|
||||||
|
port: 80,
|
||||||
|
},]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_servers_for_backend_no_linked_servers() {
|
fn test_get_servers_for_backend_no_linked_servers() {
|
||||||
@@ -430,15 +433,18 @@ mod tests {
|
|||||||
// Call the function
|
// Call the function
|
||||||
let result = get_servers_for_backend(&backend, &haproxy);
|
let result = get_servers_for_backend(&backend, &haproxy);
|
||||||
// Check the result
|
// Check the result
|
||||||
assert_eq!(result, vec![
|
assert_eq!(
|
||||||
BackendServer {
|
result,
|
||||||
address: "some-hostname.test.mcd".to_string(),
|
vec![
|
||||||
port: 80,
|
BackendServer {
|
||||||
},
|
address: "some-hostname.test.mcd".to_string(),
|
||||||
BackendServer {
|
port: 80,
|
||||||
address: "192.168.1.2".to_string(),
|
},
|
||||||
port: 8080,
|
BackendServer {
|
||||||
},
|
address: "192.168.1.2".to_string(),
|
||||||
]);
|
port: 8080,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::topology::{HelmCommand, Topology};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use helm_wrapper_rs;
|
use helm_wrapper_rs;
|
||||||
use helm_wrapper_rs::blocking::{DefaultHelmExecutor, HelmExecutor};
|
use helm_wrapper_rs::blocking::{DefaultHelmExecutor, HelmExecutor};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, info, warn};
|
||||||
pub use non_blank_string_rs::NonBlankString;
|
pub use non_blank_string_rs::NonBlankString;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -104,6 +104,7 @@ impl HelmChartInterpret {
|
|||||||
|
|
||||||
fn run_helm_command(args: &[&str]) -> Result<Output, InterpretError> {
|
fn run_helm_command(args: &[&str]) -> Result<Output, InterpretError> {
|
||||||
let command_str = format!("helm {}", args.join(" "));
|
let command_str = format!("helm {}", args.join(" "));
|
||||||
|
debug!("Got KUBECONFIG: `{}`", std::env::var("KUBECONFIG").unwrap());
|
||||||
debug!("Running Helm command: `{}`", command_str);
|
debug!("Running Helm command: `{}`", command_str);
|
||||||
|
|
||||||
let output = Command::new("helm")
|
let output = Command::new("helm")
|
||||||
@@ -159,7 +160,13 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
|
|||||||
|
|
||||||
self.add_repo()?;
|
self.add_repo()?;
|
||||||
|
|
||||||
let helm_executor = DefaultHelmExecutor::new();
|
let helm_executor = DefaultHelmExecutor::new_with_opts(
|
||||||
|
&NonBlankString::from_str("helm").unwrap(),
|
||||||
|
None,
|
||||||
|
900,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
let mut helm_options = Vec::new();
|
let mut helm_options = Vec::new();
|
||||||
if self.score.create_namespace {
|
if self.score.create_namespace {
|
||||||
|
|||||||
379
harmony/src/modules/helm/command.rs
Normal file
379
harmony/src/modules/helm/command.rs
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use log::debug;
|
||||||
|
use non_blank_string_rs::NonBlankString;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::env::temp_dir;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
use temp_dir::{self, TempDir};
|
||||||
|
use temp_file::TempFile;
|
||||||
|
|
||||||
|
use crate::data::{Id, Version};
|
||||||
|
use crate::interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome};
|
||||||
|
use crate::inventory::Inventory;
|
||||||
|
use crate::score::Score;
|
||||||
|
use crate::topology::{HelmCommand, K8sclient, Topology};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct HelmCommandExecutor {
|
||||||
|
pub env: HashMap<String, String>,
|
||||||
|
pub path: Option<PathBuf>,
|
||||||
|
pub args: Vec<String>,
|
||||||
|
pub api_versions: Option<Vec<String>>,
|
||||||
|
pub kube_version: String,
|
||||||
|
pub debug: Option<bool>,
|
||||||
|
pub globals: HelmGlobals,
|
||||||
|
pub chart: HelmChart,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct HelmGlobals {
|
||||||
|
pub chart_home: Option<PathBuf>,
|
||||||
|
pub config_home: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct HelmChart {
|
||||||
|
pub name: String,
|
||||||
|
pub version: Option<String>,
|
||||||
|
pub repo: Option<String>,
|
||||||
|
pub release_name: Option<String>,
|
||||||
|
pub namespace: Option<String>,
|
||||||
|
pub additional_values_files: Vec<PathBuf>,
|
||||||
|
pub values_file: Option<PathBuf>,
|
||||||
|
pub values_inline: Option<String>,
|
||||||
|
pub include_crds: Option<bool>,
|
||||||
|
pub skip_hooks: Option<bool>,
|
||||||
|
pub api_versions: Option<Vec<String>>,
|
||||||
|
pub kube_version: Option<String>,
|
||||||
|
pub name_template: String,
|
||||||
|
pub skip_tests: Option<bool>,
|
||||||
|
pub debug: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HelmCommandExecutor {
|
||||||
|
pub fn generate(mut self) -> Result<String, std::io::Error> {
|
||||||
|
if self.globals.chart_home.is_none() {
|
||||||
|
self.globals.chart_home = Some(PathBuf::from("charts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.clone()
|
||||||
|
.chart
|
||||||
|
.clone()
|
||||||
|
.chart_exists_locally(self.clone().globals.chart_home.unwrap())
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
if self.chart.repo.is_none() {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
ErrorKind::Other,
|
||||||
|
"Chart doesn't exist locally and no repo specified",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
self.clone().run_command(
|
||||||
|
self.chart
|
||||||
|
.clone()
|
||||||
|
.pull_command(self.globals.chart_home.clone().unwrap()),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let out = match self.clone().run_command(
|
||||||
|
self.chart
|
||||||
|
.clone()
|
||||||
|
.helm_args(self.globals.chart_home.clone().unwrap()),
|
||||||
|
) {
|
||||||
|
Ok(out) => out,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: don't use unwrap here
|
||||||
|
let s = String::from_utf8(out.stdout).unwrap();
|
||||||
|
debug!("helm stderr: {}", String::from_utf8(out.stderr).unwrap());
|
||||||
|
debug!("helm status: {}", out.status);
|
||||||
|
debug!("helm output: {s}");
|
||||||
|
|
||||||
|
let clean = s.split_once("---").unwrap().1;
|
||||||
|
|
||||||
|
Ok(clean.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn version(self) -> Result<String, std::io::Error> {
|
||||||
|
let out = match self.run_command(vec![
|
||||||
|
"version".to_string(),
|
||||||
|
"-c".to_string(),
|
||||||
|
"--short".to_string(),
|
||||||
|
]) {
|
||||||
|
Ok(out) => out,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: don't use unwrap
|
||||||
|
Ok(String::from_utf8(out.stdout).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_command(mut self, mut args: Vec<String>) -> Result<Output, std::io::Error> {
|
||||||
|
if let Some(d) = self.debug {
|
||||||
|
if d {
|
||||||
|
args.push("--debug".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = if let Some(p) = self.path {
|
||||||
|
p
|
||||||
|
} else {
|
||||||
|
PathBuf::from("helm")
|
||||||
|
};
|
||||||
|
|
||||||
|
let config_home = match self.globals.config_home {
|
||||||
|
Some(p) => p,
|
||||||
|
None => PathBuf::from(TempDir::new()?.path()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.chart.values_inline {
|
||||||
|
Some(yaml_str) => {
|
||||||
|
let tf: TempFile;
|
||||||
|
tf = temp_file::with_contents(yaml_str.as_bytes());
|
||||||
|
self.chart
|
||||||
|
.additional_values_files
|
||||||
|
.push(PathBuf::from(tf.path()));
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.env.insert(
|
||||||
|
"HELM_CONFIG_HOME".to_string(),
|
||||||
|
config_home.to_str().unwrap().to_string(),
|
||||||
|
);
|
||||||
|
self.env.insert(
|
||||||
|
"HELM_CACHE_HOME".to_string(),
|
||||||
|
config_home.to_str().unwrap().to_string(),
|
||||||
|
);
|
||||||
|
self.env.insert(
|
||||||
|
"HELM_DATA_HOME".to_string(),
|
||||||
|
config_home.to_str().unwrap().to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Command::new(path).envs(self.env).args(args).output()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HelmChart {
|
||||||
|
pub fn chart_exists_locally(self, chart_home: PathBuf) -> Option<PathBuf> {
|
||||||
|
let chart_path =
|
||||||
|
PathBuf::from(chart_home.to_str().unwrap().to_string() + "/" + &self.name.to_string());
|
||||||
|
|
||||||
|
if chart_path.exists() {
|
||||||
|
Some(chart_path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pull_command(self, chart_home: PathBuf) -> Vec<String> {
|
||||||
|
let mut args = vec![
|
||||||
|
"pull".to_string(),
|
||||||
|
"--untar".to_string(),
|
||||||
|
"--untardir".to_string(),
|
||||||
|
chart_home.to_str().unwrap().to_string(),
|
||||||
|
];
|
||||||
|
|
||||||
|
match self.repo {
|
||||||
|
Some(r) => {
|
||||||
|
if r.starts_with("oci://") {
|
||||||
|
args.push(String::from(
|
||||||
|
r.trim_end_matches("/").to_string() + "/" + self.name.clone().as_str(),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
args.push("--repo".to_string());
|
||||||
|
args.push(r.to_string());
|
||||||
|
|
||||||
|
args.push(self.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => args.push(self.name),
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.version {
|
||||||
|
Some(v) => {
|
||||||
|
args.push("--version".to_string());
|
||||||
|
args.push(v.to_string());
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
args
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn helm_args(self, chart_home: PathBuf) -> Vec<String> {
|
||||||
|
let mut args: Vec<String> = vec!["template".to_string()];
|
||||||
|
|
||||||
|
match self.release_name {
|
||||||
|
Some(rn) => args.push(rn.to_string()),
|
||||||
|
None => args.push("--generate-name".to_string()),
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push(
|
||||||
|
PathBuf::from(chart_home.to_str().unwrap().to_string() + "/" + self.name.as_str())
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(n) = self.namespace {
|
||||||
|
args.push("--namespace".to_string());
|
||||||
|
args.push(n.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(f) = self.values_file {
|
||||||
|
args.push("-f".to_string());
|
||||||
|
args.push(f.to_str().unwrap().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
for f in self.additional_values_files {
|
||||||
|
args.push("-f".to_string());
|
||||||
|
args.push(f.to_str().unwrap().to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(vv) = self.api_versions {
|
||||||
|
for v in vv {
|
||||||
|
args.push("--api-versions".to_string());
|
||||||
|
args.push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(kv) = self.kube_version {
|
||||||
|
args.push("--kube-version".to_string());
|
||||||
|
args.push(kv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(crd) = self.include_crds {
|
||||||
|
if crd {
|
||||||
|
args.push("--include-crds".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(st) = self.skip_tests {
|
||||||
|
if st {
|
||||||
|
args.push("--skip-tests".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(sh) = self.skip_hooks {
|
||||||
|
if sh {
|
||||||
|
args.push("--no-hooks".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(d) = self.debug {
|
||||||
|
if d {
|
||||||
|
args.push("--debug".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct HelmChartScoreV2 {
|
||||||
|
pub chart: HelmChart,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Topology + K8sclient + HelmCommand> Score<T> for HelmChartScoreV2 {
|
||||||
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
|
Box::new(HelmChartInterpretV2 {
|
||||||
|
score: self.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"{} {} HelmChartScoreV2",
|
||||||
|
self.chart
|
||||||
|
.release_name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or("Unknown".to_string()),
|
||||||
|
self.chart.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct HelmChartInterpretV2 {
|
||||||
|
pub score: HelmChartScoreV2,
|
||||||
|
}
|
||||||
|
impl HelmChartInterpretV2 {}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: Topology + K8sclient + HelmCommand> Interpret<T> for HelmChartInterpretV2 {
|
||||||
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
_inventory: &Inventory,
|
||||||
|
_topology: &T,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let ns = self
|
||||||
|
.score
|
||||||
|
.chart
|
||||||
|
.namespace
|
||||||
|
.as_ref()
|
||||||
|
.unwrap_or_else(|| todo!("Get namespace from active kubernetes cluster"));
|
||||||
|
|
||||||
|
let helm_executor = HelmCommandExecutor {
|
||||||
|
env: HashMap::new(),
|
||||||
|
path: None,
|
||||||
|
args: vec![],
|
||||||
|
api_versions: None,
|
||||||
|
kube_version: "v1.33.0".to_string(),
|
||||||
|
debug: Some(false),
|
||||||
|
globals: HelmGlobals {
|
||||||
|
chart_home: None,
|
||||||
|
config_home: None,
|
||||||
|
},
|
||||||
|
chart: self.score.chart.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// let mut helm_options = Vec::new();
|
||||||
|
// if self.score.create_namespace {
|
||||||
|
// helm_options.push(NonBlankString::from_str("--create-namespace").unwrap());
|
||||||
|
// }
|
||||||
|
|
||||||
|
let res = helm_executor.generate();
|
||||||
|
|
||||||
|
let output = match res {
|
||||||
|
Ok(output) => output,
|
||||||
|
Err(err) => return Err(InterpretError::new(err.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: implement actually applying the YAML from the templating in the generate function to a k8s cluster, having trouble passing in straight YAML into the k8s client
|
||||||
|
|
||||||
|
// let k8s_resource = k8s_openapi::serde_json::from_str(output.as_str()).unwrap();
|
||||||
|
|
||||||
|
// let client = topology
|
||||||
|
// .k8s_client()
|
||||||
|
// .await
|
||||||
|
// .expect("Environment should provide enough information to instanciate a client")
|
||||||
|
// .apply_namespaced(&vec![output], Some(ns.to_string().as_str()));
|
||||||
|
// match client.apply_yaml(output) {
|
||||||
|
// Ok(_) => return Ok(Outcome::success("Helm chart deployed".to_string())),
|
||||||
|
// Err(e) => return Err(InterpretError::new(e)),
|
||||||
|
// }
|
||||||
|
|
||||||
|
Ok(Outcome::success("Helm chart deployed".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<Id> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
pub mod chart;
|
pub mod chart;
|
||||||
|
pub mod command;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl<T: Topology + K8sclient> Score<T> for K8sDeploymentScore {
|
|||||||
{
|
{
|
||||||
"image": self.image,
|
"image": self.image,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"imagePullPolicy": "IfNotPresent",
|
"imagePullPolicy": "Always",
|
||||||
"env": self.env_vars,
|
"env": self.env_vars,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
98
harmony/src/modules/k8s/ingress.rs
Normal file
98
harmony/src/modules/k8s/ingress.rs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
use harmony_macros::ingress_path;
|
||||||
|
use k8s_openapi::api::networking::v1::Ingress;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
interpret::Interpret,
|
||||||
|
score::Score,
|
||||||
|
topology::{K8sclient, Topology},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::resource::{K8sResourceInterpret, K8sResourceScore};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub enum PathType {
|
||||||
|
ImplementationSpecific,
|
||||||
|
Exact,
|
||||||
|
Prefix,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathType {
|
||||||
|
fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
PathType::ImplementationSpecific => "ImplementationSpecific",
|
||||||
|
PathType::Exact => "Exact",
|
||||||
|
PathType::Prefix => "Prefix",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IngressPath = String;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct K8sIngressScore {
|
||||||
|
pub name: fqdn::FQDN,
|
||||||
|
pub host: fqdn::FQDN,
|
||||||
|
pub backend_service: fqdn::FQDN,
|
||||||
|
pub port: u16,
|
||||||
|
pub path: Option<IngressPath>,
|
||||||
|
pub path_type: Option<PathType>,
|
||||||
|
pub namespace: Option<fqdn::FQDN>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Topology + K8sclient> Score<T> for K8sIngressScore {
|
||||||
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
|
let path = match self.path.clone() {
|
||||||
|
Some(p) => p,
|
||||||
|
None => ingress_path!("/"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let path_type = match self.path_type.clone() {
|
||||||
|
Some(p) => p,
|
||||||
|
None => PathType::Prefix,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ingress = json!(
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": self.name
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"rules": [
|
||||||
|
{ "host": self.host,
|
||||||
|
"http": {
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"path": path,
|
||||||
|
"pathType": path_type.as_str(),
|
||||||
|
"backend": [
|
||||||
|
{
|
||||||
|
"service": self.backend_service,
|
||||||
|
"port": self.port
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let ingress: Ingress = serde_json::from_value(ingress).unwrap();
|
||||||
|
Box::new(K8sResourceInterpret {
|
||||||
|
score: K8sResourceScore::single(
|
||||||
|
ingress.clone(),
|
||||||
|
self.namespace
|
||||||
|
.clone()
|
||||||
|
.map(|f| f.as_c_str().to_str().unwrap().to_string()),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
format!("{} K8sIngressScore", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod deployment;
|
pub mod deployment;
|
||||||
|
pub mod ingress;
|
||||||
pub mod namespace;
|
pub mod namespace;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use dockerfile_builder::instruction::{CMD, COPY, ENV, EXPOSE, FROM, RUN, WORKDIR};
|
use dockerfile_builder::instruction::{CMD, COPY, ENV, EXPOSE, FROM, RUN, WORKDIR};
|
||||||
use dockerfile_builder::{Dockerfile, instruction_builder::EnvBuilder};
|
use dockerfile_builder::{Dockerfile, instruction_builder::EnvBuilder};
|
||||||
|
use fqdn::fqdn;
|
||||||
|
use harmony_macros::ingress_path;
|
||||||
use non_blank_string_rs::NonBlankString;
|
use non_blank_string_rs::NonBlankString;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -13,6 +15,7 @@ use log::{debug, info};
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::config::{REGISTRY_PROJECT, REGISTRY_URL};
|
use crate::config::{REGISTRY_PROJECT, REGISTRY_URL};
|
||||||
|
use crate::modules::k8s::ingress::K8sIngressScore;
|
||||||
use crate::topology::HelmCommand;
|
use crate::topology::HelmCommand;
|
||||||
use crate::{
|
use crate::{
|
||||||
data::{Id, Version},
|
data::{Id, Version},
|
||||||
@@ -38,6 +41,7 @@ pub struct LAMPConfig {
|
|||||||
pub project_root: PathBuf,
|
pub project_root: PathBuf,
|
||||||
pub ssl_enabled: bool,
|
pub ssl_enabled: bool,
|
||||||
pub database_size: Option<String>,
|
pub database_size: Option<String>,
|
||||||
|
pub namespace: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LAMPConfig {
|
impl Default for LAMPConfig {
|
||||||
@@ -46,6 +50,7 @@ impl Default for LAMPConfig {
|
|||||||
project_root: Path::new("./src").to_path_buf(),
|
project_root: Path::new("./src").to_path_buf(),
|
||||||
ssl_enabled: true,
|
ssl_enabled: true,
|
||||||
database_size: None,
|
database_size: None,
|
||||||
|
namespace: "harmony-lamp".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,7 +59,6 @@ impl<T: Topology + K8sclient + HelmCommand> Score<T> for LAMPScore {
|
|||||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
Box::new(LAMPInterpret {
|
Box::new(LAMPInterpret {
|
||||||
score: self.clone(),
|
score: self.clone(),
|
||||||
namespace: "harmony-lamp".to_string(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +70,6 @@ impl<T: Topology + K8sclient + HelmCommand> Score<T> for LAMPScore {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LAMPInterpret {
|
pub struct LAMPInterpret {
|
||||||
score: LAMPScore,
|
score: LAMPScore,
|
||||||
namespace: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -132,7 +135,32 @@ impl<T: Topology + K8sclient + HelmCommand> Interpret<T> for LAMPInterpret {
|
|||||||
|
|
||||||
info!("LAMP deployment_score {deployment_score:?}");
|
info!("LAMP deployment_score {deployment_score:?}");
|
||||||
|
|
||||||
Ok(Outcome::success("Successfully deployed LAMP Stack!".to_string()))
|
let lamp_ingress = K8sIngressScore {
|
||||||
|
name: fqdn!("lamp-ingress"),
|
||||||
|
host: fqdn!("test"),
|
||||||
|
backend_service: fqdn!(
|
||||||
|
<LAMPScore as Score<T>>::name(&self.score)
|
||||||
|
.to_case(Case::Kebab)
|
||||||
|
.as_str()
|
||||||
|
),
|
||||||
|
port: 8080,
|
||||||
|
path: Some(ingress_path!("/")),
|
||||||
|
path_type: None,
|
||||||
|
namespace: self
|
||||||
|
.get_namespace()
|
||||||
|
.map(|nbs| fqdn!(nbs.to_string().as_str())),
|
||||||
|
};
|
||||||
|
|
||||||
|
lamp_ingress
|
||||||
|
.create_interpret()
|
||||||
|
.execute(inventory, topology)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
info!("LAMP lamp_ingress {lamp_ingress:?}");
|
||||||
|
|
||||||
|
Ok(Outcome::success(
|
||||||
|
"Successfully deployed LAMP Stack!".to_string(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_name(&self) -> InterpretName {
|
fn get_name(&self) -> InterpretName {
|
||||||
@@ -164,6 +192,10 @@ impl LAMPInterpret {
|
|||||||
NonBlankString::from_str("primary.persistence.size").unwrap(),
|
NonBlankString::from_str("primary.persistence.size").unwrap(),
|
||||||
database_size,
|
database_size,
|
||||||
);
|
);
|
||||||
|
values_overrides.insert(
|
||||||
|
NonBlankString::from_str("auth.rootPassword").unwrap(),
|
||||||
|
"mariadb-changethis".to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let score = HelmChartScore {
|
let score = HelmChartScore {
|
||||||
namespace: self.get_namespace(),
|
namespace: self.get_namespace(),
|
||||||
@@ -176,7 +208,7 @@ impl LAMPInterpret {
|
|||||||
chart_version: None,
|
chart_version: None,
|
||||||
values_overrides: Some(values_overrides),
|
values_overrides: Some(values_overrides),
|
||||||
create_namespace: true,
|
create_namespace: true,
|
||||||
install_only: true,
|
install_only: false,
|
||||||
values_yaml: None,
|
values_yaml: None,
|
||||||
repository: None,
|
repository: None,
|
||||||
};
|
};
|
||||||
@@ -231,6 +263,9 @@ impl LAMPInterpret {
|
|||||||
opcache",
|
opcache",
|
||||||
));
|
));
|
||||||
|
|
||||||
|
dockerfile.push(RUN::from(r#"sed -i 's/VirtualHost \*:80/VirtualHost *:8080/' /etc/apache2/sites-available/000-default.conf && \
|
||||||
|
sed -i 's/^Listen 80$/Listen 8080/' /etc/apache2/ports.conf"#));
|
||||||
|
|
||||||
// Copy PHP configuration
|
// Copy PHP configuration
|
||||||
dockerfile.push(RUN::from("mkdir -p /usr/local/etc/php/conf.d/"));
|
dockerfile.push(RUN::from("mkdir -p /usr/local/etc/php/conf.d/"));
|
||||||
|
|
||||||
@@ -296,7 +331,7 @@ opcache.fast_shutdown=1
|
|||||||
dockerfile.push(RUN::from("chown -R appuser:appuser /var/www/html"));
|
dockerfile.push(RUN::from("chown -R appuser:appuser /var/www/html"));
|
||||||
|
|
||||||
// Expose Apache port
|
// Expose Apache port
|
||||||
dockerfile.push(EXPOSE::from("80/tcp"));
|
dockerfile.push(EXPOSE::from("8080/tcp"));
|
||||||
|
|
||||||
// Set the default command
|
// Set the default command
|
||||||
dockerfile.push(CMD::from("apache2-foreground"));
|
dockerfile.push(CMD::from("apache2-foreground"));
|
||||||
@@ -380,6 +415,6 @@ opcache.fast_shutdown=1
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_namespace(&self) -> Option<NonBlankString> {
|
fn get_namespace(&self) -> Option<NonBlankString> {
|
||||||
Some(NonBlankString::from_str(&self.namespace).unwrap())
|
Some(NonBlankString::from_str(&self.score.config.namespace).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub mod k3d;
|
|||||||
pub mod k8s;
|
pub mod k8s;
|
||||||
pub mod lamp;
|
pub mod lamp;
|
||||||
pub mod load_balancer;
|
pub mod load_balancer;
|
||||||
|
pub mod monitoring;
|
||||||
pub mod okd;
|
pub mod okd;
|
||||||
pub mod opnsense;
|
pub mod opnsense;
|
||||||
pub mod tftp;
|
pub mod tftp;
|
||||||
pub mod monitoring;
|
|
||||||
|
|||||||
53
harmony/src/modules/monitoring/config.rs
Normal file
53
harmony/src/modules/monitoring/config.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use super::kube_prometheus::{prometheus_alert_channel::PrometheusAlertChannel, types::AlertManagerValues};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
pub struct KubePrometheusChartConfig {
|
||||||
|
pub namespace: String,
|
||||||
|
pub default_rules: bool,
|
||||||
|
pub windows_monitoring: bool,
|
||||||
|
pub alert_manager: bool,
|
||||||
|
pub alert_manager_values: AlertManagerValues,
|
||||||
|
pub node_exporter: bool,
|
||||||
|
pub prometheus: bool,
|
||||||
|
pub grafana: bool,
|
||||||
|
pub kubernetes_service_monitors: bool,
|
||||||
|
pub kubernetes_api_server: bool,
|
||||||
|
pub kubelet: bool,
|
||||||
|
pub kube_controller_manager: bool,
|
||||||
|
pub core_dns: bool,
|
||||||
|
pub kube_etcd: bool,
|
||||||
|
pub kube_scheduler: bool,
|
||||||
|
pub kube_proxy: bool,
|
||||||
|
pub kube_state_metrics: bool,
|
||||||
|
pub prometheus_operator: bool,
|
||||||
|
pub alert_channels: Vec<Box<dyn PrometheusAlertChannel>>,
|
||||||
|
}
|
||||||
|
impl KubePrometheusChartConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
namespace: "monitoring".into(),
|
||||||
|
default_rules: true,
|
||||||
|
windows_monitoring: false,
|
||||||
|
alert_manager: true,
|
||||||
|
alert_manager_values: AlertManagerValues::default(),
|
||||||
|
alert_channels: Vec::new(),
|
||||||
|
grafana: true,
|
||||||
|
node_exporter: false,
|
||||||
|
prometheus: true,
|
||||||
|
kubernetes_service_monitors: true,
|
||||||
|
kubernetes_api_server: false,
|
||||||
|
kubelet: false,
|
||||||
|
kube_controller_manager: false,
|
||||||
|
kube_etcd: false,
|
||||||
|
kube_proxy: false,
|
||||||
|
kube_state_metrics: true,
|
||||||
|
prometheus_operator: true,
|
||||||
|
core_dns: false,
|
||||||
|
kube_scheduler: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
harmony/src/modules/monitoring/discord_alert_manager.rs
Normal file
32
harmony/src/modules/monitoring/discord_alert_manager.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use non_blank_string_rs::NonBlankString;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::modules::helm::chart::HelmChartScore;
|
||||||
|
|
||||||
|
pub fn discord_alert_manager_score(name: String, webhook: Url, namespace: String) -> HelmChartScore {
|
||||||
|
let url = webhook;
|
||||||
|
let values = format!(
|
||||||
|
r#"
|
||||||
|
environment:
|
||||||
|
- name: "DISCORD_WEBHOOK"
|
||||||
|
value: "{url}"
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
HelmChartScore {
|
||||||
|
namespace: Some(NonBlankString::from_str(&namespace).unwrap()),
|
||||||
|
release_name: NonBlankString::from_str(&name).unwrap(),
|
||||||
|
chart_name: NonBlankString::from_str(
|
||||||
|
"oci://hub.nationtech.io/library/alertmanager-discord",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
chart_version: None,
|
||||||
|
values_overrides: None,
|
||||||
|
values_yaml: Some(values.to_string()),
|
||||||
|
create_namespace: true,
|
||||||
|
install_only: true,
|
||||||
|
repository: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use non_blank_string_rs::NonBlankString;
|
|
||||||
|
|
||||||
use crate::modules::helm::chart::HelmChartScore;
|
|
||||||
|
|
||||||
pub fn kube_prometheus_score(ns: &str) -> HelmChartScore {
|
|
||||||
//TODO this should be make into a rule with default formatting that can be easily passed as a vec
|
|
||||||
//to the overrides or something leaving the user to deal with formatting here seems bad
|
|
||||||
let values = r#"
|
|
||||||
additionalPrometheusRulesMap:
|
|
||||||
pvc-alerts:
|
|
||||||
groups:
|
|
||||||
- name: pvc-alerts
|
|
||||||
rules:
|
|
||||||
- alert: 'PVC Fill Over 95 Percent In 2 Days'
|
|
||||||
expr: |
|
|
||||||
(
|
|
||||||
kubelet_volume_stats_used_bytes
|
|
||||||
/
|
|
||||||
kubelet_volume_stats_capacity_bytes
|
|
||||||
) > 0.95
|
|
||||||
AND
|
|
||||||
predict_linear(kubelet_volume_stats_used_bytes[2d], 2 * 24 * 60 * 60)
|
|
||||||
/
|
|
||||||
kubelet_volume_stats_capacity_bytes
|
|
||||||
> 0.95
|
|
||||||
for: 1m
|
|
||||||
labels:
|
|
||||||
severity: warning
|
|
||||||
annotations:
|
|
||||||
description: The PVC {{ $labels.persistentvolumeclaim }} in namespace {{ $labels.namespace }} is predicted to fill over 95% in less than 2 days.
|
|
||||||
title: PVC {{ $labels.persistentvolumeclaim }} in namespace {{ $labels.namespace }} will fill over 95% in less than 2 days
|
|
||||||
"#;
|
|
||||||
HelmChartScore {
|
|
||||||
namespace: Some(NonBlankString::from_str(ns).unwrap()),
|
|
||||||
release_name: NonBlankString::from_str("kube-prometheus").unwrap(),
|
|
||||||
chart_name: NonBlankString::from_str(
|
|
||||||
"oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack", //use kube prometheus chart which includes grafana, prometheus, alert
|
|
||||||
//manager, etc
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
chart_version: None,
|
|
||||||
values_overrides: None,
|
|
||||||
values_yaml: Some(values.to_string()),
|
|
||||||
create_namespace: true,
|
|
||||||
install_only: true,
|
|
||||||
repository: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
use crate::modules::{helm::chart::HelmChartScore, monitoring::config::KubePrometheusChartConfig};
|
||||||
|
use log::info;
|
||||||
|
use non_blank_string_rs::NonBlankString;
|
||||||
|
use serde_yaml::{self};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
pub fn kube_prometheus_helm_chart_score(config: &KubePrometheusChartConfig) -> HelmChartScore {
|
||||||
|
//TODO this should be make into a rule with default formatting that can be easily passed as a vec
|
||||||
|
//to the overrides or something leaving the user to deal with formatting here seems bad
|
||||||
|
let default_rules = config.default_rules.to_string();
|
||||||
|
let windows_monitoring = config.windows_monitoring.to_string();
|
||||||
|
let grafana = config.grafana.to_string();
|
||||||
|
let kubernetes_service_monitors = config.kubernetes_service_monitors.to_string();
|
||||||
|
let kubernetes_api_server = config.kubernetes_api_server.to_string();
|
||||||
|
let kubelet = config.kubelet.to_string();
|
||||||
|
let kube_controller_manager = config.kube_controller_manager.to_string();
|
||||||
|
let core_dns = config.core_dns.to_string();
|
||||||
|
let kube_etcd = config.kube_etcd.to_string();
|
||||||
|
let kube_scheduler = config.kube_scheduler.to_string();
|
||||||
|
let kube_proxy = config.kube_proxy.to_string();
|
||||||
|
let kube_state_metrics = config.kube_state_metrics.to_string();
|
||||||
|
let node_exporter = config.node_exporter.to_string();
|
||||||
|
let prometheus_operator = config.prometheus_operator.to_string();
|
||||||
|
let prometheus = config.prometheus.to_string();
|
||||||
|
let alert_manager_values = config.alert_manager_values.clone();
|
||||||
|
let mut values = format!(
|
||||||
|
r#"
|
||||||
|
additionalPrometheusRulesMap:
|
||||||
|
pods-status-alerts:
|
||||||
|
groups:
|
||||||
|
- name: pods
|
||||||
|
rules:
|
||||||
|
- alert: "[CRIT] POD not healthy"
|
||||||
|
expr: min_over_time(sum by (namespace, pod) (kube_pod_status_phase{{phase=~"Pending|Unknown|Failed"}})[15m:1m]) > 0
|
||||||
|
for: 0m
|
||||||
|
labels:
|
||||||
|
severity: critical
|
||||||
|
annotations:
|
||||||
|
title: "[CRIT] POD not healthy : {{{{ $labels.pod }}}}"
|
||||||
|
description: |
|
||||||
|
A POD is in a non-ready state!
|
||||||
|
- **Pod**: {{{{ $labels.pod }}}}
|
||||||
|
- **Namespace**: {{{{ $labels.namespace }}}}
|
||||||
|
- alert: "[CRIT] POD crash looping"
|
||||||
|
expr: increase(kube_pod_container_status_restarts_total[5m]) > 3
|
||||||
|
for: 0m
|
||||||
|
labels:
|
||||||
|
severity: critical
|
||||||
|
annotations:
|
||||||
|
title: "[CRIT] POD crash looping : {{{{ $labels.pod }}}}"
|
||||||
|
description: |
|
||||||
|
A POD is drowning in a crash loop!
|
||||||
|
- **Pod**: {{{{ $labels.pod }}}}
|
||||||
|
- **Namespace**: {{{{ $labels.namespace }}}}
|
||||||
|
- **Instance**: {{{{ $labels.instance }}}}
|
||||||
|
pvc-alerts:
|
||||||
|
groups:
|
||||||
|
- name: pvc-alerts
|
||||||
|
rules:
|
||||||
|
- alert: 'PVC Fill Over 95 Percent In 2 Days'
|
||||||
|
expr: |
|
||||||
|
(
|
||||||
|
kubelet_volume_stats_used_bytes
|
||||||
|
/
|
||||||
|
kubelet_volume_stats_capacity_bytes
|
||||||
|
) > 0.95
|
||||||
|
AND
|
||||||
|
predict_linear(kubelet_volume_stats_used_bytes[2d], 2 * 24 * 60 * 60)
|
||||||
|
/
|
||||||
|
kubelet_volume_stats_capacity_bytes
|
||||||
|
> 0.95
|
||||||
|
for: 1m
|
||||||
|
labels:
|
||||||
|
severity: warning
|
||||||
|
annotations:
|
||||||
|
description: The PVC {{{{ $labels.persistentvolumeclaim }}}} in namespace {{{{ $labels.namespace }}}} is predicted to fill over 95% in less than 2 days.
|
||||||
|
title: PVC {{{{ $labels.persistentvolumeclaim }}}} in namespace {{{{ $labels.namespace }}}} will fill over 95% in less than 2 days
|
||||||
|
defaultRules:
|
||||||
|
create: {default_rules}
|
||||||
|
rules:
|
||||||
|
alertmanager: true
|
||||||
|
etcd: true
|
||||||
|
configReloaders: true
|
||||||
|
general: true
|
||||||
|
k8sContainerCpuUsageSecondsTotal: true
|
||||||
|
k8sContainerMemoryCache: true
|
||||||
|
k8sContainerMemoryRss: true
|
||||||
|
k8sContainerMemorySwap: true
|
||||||
|
k8sContainerResource: true
|
||||||
|
k8sContainerMemoryWorkingSetBytes: true
|
||||||
|
k8sPodOwner: true
|
||||||
|
kubeApiserverAvailability: true
|
||||||
|
kubeApiserverBurnrate: true
|
||||||
|
kubeApiserverHistogram: true
|
||||||
|
kubeApiserverSlos: true
|
||||||
|
kubeControllerManager: true
|
||||||
|
kubelet: true
|
||||||
|
kubeProxy: true
|
||||||
|
kubePrometheusGeneral: true
|
||||||
|
kubePrometheusNodeRecording: true
|
||||||
|
kubernetesApps: true
|
||||||
|
kubernetesResources: true
|
||||||
|
kubernetesStorage: true
|
||||||
|
kubernetesSystem: true
|
||||||
|
kubeSchedulerAlerting: true
|
||||||
|
kubeSchedulerRecording: true
|
||||||
|
kubeStateMetrics: true
|
||||||
|
network: true
|
||||||
|
node: true
|
||||||
|
nodeExporterAlerting: true
|
||||||
|
nodeExporterRecording: true
|
||||||
|
prometheus: true
|
||||||
|
prometheusOperator: true
|
||||||
|
windows: true
|
||||||
|
windowsMonitoring:
|
||||||
|
enabled: {windows_monitoring}
|
||||||
|
grafana:
|
||||||
|
enabled: {grafana}
|
||||||
|
kubernetesServiceMonitors:
|
||||||
|
enabled: {kubernetes_service_monitors}
|
||||||
|
kubeApiServer:
|
||||||
|
enabled: {kubernetes_api_server}
|
||||||
|
kubelet:
|
||||||
|
enabled: {kubelet}
|
||||||
|
kubeControllerManager:
|
||||||
|
enabled: {kube_controller_manager}
|
||||||
|
coreDns:
|
||||||
|
enabled: {core_dns}
|
||||||
|
kubeEtcd:
|
||||||
|
enabled: {kube_etcd}
|
||||||
|
kubeScheduler:
|
||||||
|
enabled: {kube_scheduler}
|
||||||
|
kubeProxy:
|
||||||
|
enabled: {kube_proxy}
|
||||||
|
kubeStateMetrics:
|
||||||
|
enabled: {kube_state_metrics}
|
||||||
|
nodeExporter:
|
||||||
|
enabled: {node_exporter}
|
||||||
|
prometheusOperator:
|
||||||
|
enabled: {prometheus_operator}
|
||||||
|
prometheus:
|
||||||
|
enabled: {prometheus}
|
||||||
|
prometheusSpec:
|
||||||
|
maximumStartupDurationSeconds: 240
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
let alert_manager_yaml = serde_yaml::to_string(&alert_manager_values).expect("Failed to serialize YAML");
|
||||||
|
values.push_str(&alert_manager_yaml);
|
||||||
|
|
||||||
|
|
||||||
|
info!("{}", values);
|
||||||
|
|
||||||
|
HelmChartScore {
|
||||||
|
namespace: Some(NonBlankString::from_str(&config.namespace).unwrap()),
|
||||||
|
release_name: NonBlankString::from_str("kube-prometheus").unwrap(),
|
||||||
|
chart_name: NonBlankString::from_str(
|
||||||
|
"oci://ghcr.io/prometheus-community/charts/kube-prometheus-stack",
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
chart_version: None,
|
||||||
|
values_overrides: None,
|
||||||
|
values_yaml: Some(values.to_string()),
|
||||||
|
create_namespace: true,
|
||||||
|
install_only: true,
|
||||||
|
repository: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
3
harmony/src/modules/monitoring/kube_prometheus/mod.rs
Normal file
3
harmony/src/modules/monitoring/kube_prometheus/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod kube_prometheus;
|
||||||
|
pub mod types;
|
||||||
|
pub mod prometheus_alert_channel;
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
use crate::{
|
||||||
|
interpret::InterpretError,
|
||||||
|
modules::{
|
||||||
|
helm::chart::HelmChartScore,
|
||||||
|
monitoring::{
|
||||||
|
discord_alert_manager::discord_alert_manager_score,
|
||||||
|
kube_prometheus::types::{
|
||||||
|
AlertChannelConfig, AlertChannelGlobalConfig, AlertChannelReceiver,
|
||||||
|
AlertChannelRoute, SlackConfig, WebhookConfig,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use dyn_clone::DynClone;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[typetag::serde(tag = "channel_type")]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait PrometheusAlertChannel: DynClone + Debug + Send + Sync {
|
||||||
|
fn get_alert_manager_config_contribution(&self) -> Result<AlertChannelConfig, InterpretError>;
|
||||||
|
|
||||||
|
fn get_dependency_score(&self, namespace:String) -> Option<HelmChartScore>;
|
||||||
|
}
|
||||||
|
|
||||||
|
dyn_clone::clone_trait_object!(PrometheusAlertChannel);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct DiscordChannel {
|
||||||
|
pub name: String,
|
||||||
|
pub webhook_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl PrometheusAlertChannel for DiscordChannel {
|
||||||
|
fn get_alert_manager_config_contribution(&self) -> Result<AlertChannelConfig, InterpretError> {
|
||||||
|
let service_url = format!("http://{}-alertmanager-discord:9094", &self.name);
|
||||||
|
Ok(AlertChannelConfig {
|
||||||
|
receiver: AlertChannelReceiver {
|
||||||
|
name: format!("Discord-{}", self.name),
|
||||||
|
slack_configs: None,
|
||||||
|
webhook_configs: Some(vec![WebhookConfig {
|
||||||
|
url: url::Url::parse(&service_url)
|
||||||
|
.expect("invalid url"),
|
||||||
|
send_resolved: true,
|
||||||
|
}]),
|
||||||
|
},
|
||||||
|
route: AlertChannelRoute {
|
||||||
|
receiver: format!("Discord-{}", self.name),
|
||||||
|
matchers: vec!["alertname!=Watchdog".to_string()],
|
||||||
|
r#continue: true,
|
||||||
|
},
|
||||||
|
global_config: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn get_dependency_score(&self, namespace: String) -> Option<HelmChartScore> {
|
||||||
|
Some(discord_alert_manager_score(self.name.clone(), self.webhook_url.clone(), namespace.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SlackChannel {
|
||||||
|
pub name: String,
|
||||||
|
pub webhook_url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl PrometheusAlertChannel for SlackChannel {
|
||||||
|
fn get_alert_manager_config_contribution(&self) -> Result<AlertChannelConfig, InterpretError> {
|
||||||
|
Ok(AlertChannelConfig {
|
||||||
|
receiver: AlertChannelReceiver {
|
||||||
|
name: format!("Slack-{}", self.name),
|
||||||
|
slack_configs: Some(vec![SlackConfig {
|
||||||
|
channel: self.name.clone(),
|
||||||
|
send_resolved: true,
|
||||||
|
title: "{{ .CommonAnnotations.title }}".to_string(),
|
||||||
|
text: ">-
|
||||||
|
*Alert:* {{ .CommonLabels.alertname }}
|
||||||
|
*Severity:* {{ .CommonLabels.severity }}
|
||||||
|
*Namespace:* {{ .CommonLabels.namespace }}
|
||||||
|
*Pod:* {{ .CommonLabels.pod }}
|
||||||
|
*ExternalURL:* {{ .ExternalURL }}
|
||||||
|
|
||||||
|
{{ range .Alerts }}
|
||||||
|
*Instance:* {{ .Labels.instance }}
|
||||||
|
*Summary:* {{ .Annotations.summary }}
|
||||||
|
*Description:* {{ .Annotations.description }}
|
||||||
|
*Starts At:* {{ .StartsAt }}
|
||||||
|
*Status:* {{ .Status }}
|
||||||
|
{{ end }}"
|
||||||
|
.to_string(),
|
||||||
|
}]),
|
||||||
|
webhook_configs: None,
|
||||||
|
},
|
||||||
|
route: AlertChannelRoute {
|
||||||
|
receiver: format!("Slack-{}", self.name),
|
||||||
|
matchers: vec!["alertname!=Watchdog".to_string()],
|
||||||
|
r#continue: true,
|
||||||
|
},
|
||||||
|
global_config: Some(AlertChannelGlobalConfig {
|
||||||
|
slack_api_url: Some(self.webhook_url.clone()),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn get_dependency_score(&self, _namespace: String) -> Option<HelmChartScore> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct NullReceiver {}
|
||||||
|
|
||||||
|
impl NullReceiver {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typetag::serde]
|
||||||
|
impl PrometheusAlertChannel for NullReceiver {
|
||||||
|
fn get_alert_manager_config_contribution(&self) -> Result<AlertChannelConfig, InterpretError> {
|
||||||
|
Ok(AlertChannelConfig {
|
||||||
|
receiver: AlertChannelReceiver {
|
||||||
|
name: "null".to_string(),
|
||||||
|
slack_configs: None,
|
||||||
|
webhook_configs: None,
|
||||||
|
},
|
||||||
|
route: AlertChannelRoute {
|
||||||
|
receiver: "null".to_string(),
|
||||||
|
matchers: vec!["alertname=Watchdog".to_string()],
|
||||||
|
r#continue: false,
|
||||||
|
},
|
||||||
|
global_config: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn get_dependency_score(&self, _namespace: String) -> Option<HelmChartScore> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
94
harmony/src/modules/monitoring/kube_prometheus/types.rs
Normal file
94
harmony/src/modules/monitoring/kube_prometheus/types.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AlertManagerValues {
|
||||||
|
pub alertmanager: AlertManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AlertManager {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub config: AlertManagerConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct AlertChannelConfig {
|
||||||
|
pub receiver: AlertChannelReceiver,
|
||||||
|
pub route: AlertChannelRoute,
|
||||||
|
pub global_config: Option<AlertChannelGlobalConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AlertChannelReceiver {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub slack_configs: Option<Vec<SlackConfig>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub webhook_configs: Option<Vec<WebhookConfig>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AlertManagerRoute {
|
||||||
|
pub group_by: Vec<String>,
|
||||||
|
pub group_wait: String,
|
||||||
|
pub group_interval: String,
|
||||||
|
pub repeat_interval: String,
|
||||||
|
pub routes: Vec<AlertChannelRoute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AlertChannelGlobalConfig {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub slack_api_url: Option<Url>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct SlackConfig {
|
||||||
|
pub channel: String,
|
||||||
|
pub send_resolved: bool,
|
||||||
|
pub title: String,
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct WebhookConfig {
|
||||||
|
pub url: Url,
|
||||||
|
pub send_resolved: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AlertChannelRoute {
|
||||||
|
pub receiver: String,
|
||||||
|
pub matchers: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub r#continue: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AlertManagerConfig {
|
||||||
|
pub global: Option<AlertChannelGlobalConfig>,
|
||||||
|
pub route: AlertManagerRoute,
|
||||||
|
pub receivers: Vec<AlertChannelReceiver>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlertManagerValues {
|
||||||
|
pub fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
alertmanager: AlertManager {
|
||||||
|
enabled: true,
|
||||||
|
config: AlertManagerConfig {
|
||||||
|
global: None,
|
||||||
|
route: AlertManagerRoute {
|
||||||
|
group_by: vec!["job".to_string()],
|
||||||
|
group_wait: "30s".to_string(),
|
||||||
|
group_interval: "5m".to_string(),
|
||||||
|
repeat_interval: "12h".to_string(),
|
||||||
|
routes: vec![AlertChannelRoute{ receiver: "null".to_string(), matchers: vec!["alertname=Watchdog".to_string()], r#continue: false }],
|
||||||
|
},
|
||||||
|
receivers: vec![AlertChannelReceiver{ name: "null".to_string(), slack_configs: None, webhook_configs: None }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5003
harmony/src/modules/monitoring/kube_prometheus/values.yaml
Normal file
5003
harmony/src/modules/monitoring/kube_prometheus/values.yaml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
|||||||
|
mod config;
|
||||||
|
mod discord_alert_manager;
|
||||||
|
pub mod kube_prometheus;
|
||||||
pub mod monitoring_alerting;
|
pub mod monitoring_alerting;
|
||||||
mod kube_prometheus;
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -6,123 +7,166 @@ use crate::{
|
|||||||
data::{Id, Version},
|
data::{Id, Version},
|
||||||
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
maestro::Maestro,
|
modules::monitoring::kube_prometheus::types::{
|
||||||
score::{CloneBoxScore, Score},
|
AlertManager, AlertManagerConfig, AlertManagerRoute,
|
||||||
topology::{HelmCommand, Topology, monitoring_alerting::MonitoringAlertingTopology},
|
},
|
||||||
|
score::Score,
|
||||||
|
topology::{HelmCommand, Topology},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::kube_prometheus::kube_prometheus_score;
|
use super::{
|
||||||
|
config::KubePrometheusChartConfig,
|
||||||
|
kube_prometheus::{
|
||||||
|
kube_prometheus::kube_prometheus_helm_chart_score,
|
||||||
|
prometheus_alert_channel::{NullReceiver, PrometheusAlertChannel},
|
||||||
|
types::AlertManagerValues,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct MonitoringAlertingStackScore {
|
pub struct MonitoringAlertingScore {
|
||||||
//TODO add documenation to explain why its here
|
pub alert_channels: Vec<Box<dyn PrometheusAlertChannel>>,
|
||||||
//keeps it open for the end user to specify which stack they want
|
pub namespace: Option<String>,
|
||||||
//if it isnt default kube-prometheus
|
|
||||||
pub monitoring_stack: Vec<Box<dyn Score<MonitoringAlertingTopology>>>,
|
|
||||||
pub namespace: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonitoringAlertingStackScore {
|
impl MonitoringAlertingScore {
|
||||||
pub fn new(
|
pub fn new() -> Self {
|
||||||
monitoring_stack: Vec<Box<dyn Score<MonitoringAlertingTopology>>>,
|
|
||||||
namespace: String,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
monitoring_stack,
|
alert_channels: Vec::new(),
|
||||||
namespace,
|
namespace: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MonitoringAlertingStackScore {
|
impl<T: Topology + HelmCommand> Score<T> for MonitoringAlertingScore {
|
||||||
fn default() -> Self {
|
|
||||||
let ns = "monitoring";
|
|
||||||
Self {
|
|
||||||
monitoring_stack: vec![Box::new(kube_prometheus_score(ns))],
|
|
||||||
namespace: ns.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Clone for MonitoringAlertingStackScore {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
monitoring_stack: self
|
|
||||||
.monitoring_stack
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.clone_box())
|
|
||||||
.collect(),
|
|
||||||
namespace: self.namespace.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for MonitoringAlertingStackScore {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
use serde::ser::SerializeStruct;
|
|
||||||
let mut s = serializer.serialize_struct("MonitoringAlertingStackScore", 1)?;
|
|
||||||
let monitoring_values: Vec<_> = self
|
|
||||||
.monitoring_stack
|
|
||||||
.iter()
|
|
||||||
.map(|m| m.serialize())
|
|
||||||
.collect();
|
|
||||||
s.serialize_field("monitoring", &monitoring_values)?;
|
|
||||||
s.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T:Topology> Score<T> for MonitoringAlertingStackScore {
|
|
||||||
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
fn create_interpret(&self) -> Box<dyn Interpret<T>> {
|
||||||
Box::new(MonitoringAlertingStackInterpret {
|
Box::new(MonitoringAlertingInterpret {
|
||||||
score: MonitoringAlertingStackScore {
|
score: self.clone(),
|
||||||
monitoring_stack: self
|
|
||||||
.monitoring_stack
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.clone_box())
|
|
||||||
.collect(),
|
|
||||||
namespace: self.namespace.clone(),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
format!("MonitoringAlertingStackScore")
|
format!("MonitoringAlertingStackScore")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
struct MonitoringAlertingStackInterpret {
|
struct MonitoringAlertingInterpret {
|
||||||
pub score: MonitoringAlertingStackScore,
|
score: MonitoringAlertingScore,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MonitoringAlertingInterpret {
|
||||||
|
async fn build_kube_prometheus_helm_chart_config(&self) -> KubePrometheusChartConfig {
|
||||||
|
let mut config = KubePrometheusChartConfig::new();
|
||||||
|
let mut receivers = Vec::new();
|
||||||
|
let mut routes = Vec::new();
|
||||||
|
let mut global_config = None;
|
||||||
|
|
||||||
|
if let Some(ns) = &self.score.namespace {
|
||||||
|
config.namespace = ns.clone();
|
||||||
|
};
|
||||||
|
|
||||||
|
let null_channel = NullReceiver::new();
|
||||||
|
let null_channel = null_channel
|
||||||
|
.get_alert_manager_config_contribution()
|
||||||
|
.unwrap();
|
||||||
|
receivers.push(null_channel.receiver);
|
||||||
|
routes.push(null_channel.route);
|
||||||
|
|
||||||
|
for channel in self.score.alert_channels.clone() {
|
||||||
|
let alert_manager_config_contribution =
|
||||||
|
channel.get_alert_manager_config_contribution().unwrap();
|
||||||
|
receivers.push(alert_manager_config_contribution.receiver);
|
||||||
|
routes.push(alert_manager_config_contribution.route);
|
||||||
|
if let Some(global) = alert_manager_config_contribution.global_config {
|
||||||
|
global_config = Some(global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("after alert receiver: {:#?}", receivers);
|
||||||
|
info!("after alert routes: {:#?}", routes);
|
||||||
|
|
||||||
|
let alert_manager_config = AlertManagerConfig {
|
||||||
|
global: global_config,
|
||||||
|
route: AlertManagerRoute {
|
||||||
|
group_by: vec!["job".to_string()],
|
||||||
|
group_wait: "30s".to_string(),
|
||||||
|
group_interval: "5m".to_string(),
|
||||||
|
repeat_interval: "12h".to_string(),
|
||||||
|
routes,
|
||||||
|
},
|
||||||
|
receivers,
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("alert manager config: {:?}", config);
|
||||||
|
|
||||||
|
config.alert_manager_values = AlertManagerValues {
|
||||||
|
alertmanager: AlertManager {
|
||||||
|
enabled: true,
|
||||||
|
config: alert_manager_config,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deploy_kube_prometheus_helm_chart_score<T: Topology + HelmCommand>(
|
||||||
|
&self,
|
||||||
|
inventory: &Inventory,
|
||||||
|
topology: &T,
|
||||||
|
config: &KubePrometheusChartConfig,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let helm_chart = kube_prometheus_helm_chart_score(config);
|
||||||
|
helm_chart
|
||||||
|
.create_interpret()
|
||||||
|
.execute(inventory, topology)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deploy_alert_channel_dependencies<T: Topology + HelmCommand>(
|
||||||
|
&self,
|
||||||
|
inventory: &Inventory,
|
||||||
|
topology: &T,
|
||||||
|
config: &KubePrometheusChartConfig,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
let mut outcomes = Vec::new();
|
||||||
|
|
||||||
|
for channel in &self.score.alert_channels {
|
||||||
|
let ns = config.namespace.clone();
|
||||||
|
if let Some(dependency_score) = channel.get_dependency_score(ns) {
|
||||||
|
match dependency_score
|
||||||
|
.create_interpret()
|
||||||
|
.execute(inventory, topology)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(outcome) => outcomes.push(outcome),
|
||||||
|
Err(e) => {
|
||||||
|
info!("failed to deploy dependency: {}", { &e });
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Outcome::success("All alert channels deployed".to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl <T: Topology> Interpret<T> for MonitoringAlertingStackInterpret {
|
impl<T: Topology + HelmCommand> Interpret<T> for MonitoringAlertingInterpret {
|
||||||
async fn execute(
|
async fn execute(
|
||||||
&self,
|
&self,
|
||||||
_inventory: &Inventory,
|
inventory: &Inventory,
|
||||||
_topology: &T,
|
topology: &T,
|
||||||
) -> Result<Outcome, InterpretError> {
|
) -> Result<Outcome, InterpretError> {
|
||||||
let inventory = Inventory::autoload();
|
let config = self.build_kube_prometheus_helm_chart_config().await;
|
||||||
let topology = MonitoringAlertingTopology::new();
|
info!("Built kube prometheus config{:?}", config);
|
||||||
let maestro = match Maestro::initialize(inventory, topology).await {
|
info!("Installing kube prometheus chart");
|
||||||
Ok(m) => m,
|
self.deploy_kube_prometheus_helm_chart_score(inventory, topology, &config)
|
||||||
Err(e) => {
|
.await?;
|
||||||
println!("failed to initialize Maestro: {}", e);
|
info!("Installing alert channel service");
|
||||||
std::process::exit(1);
|
self.deploy_alert_channel_dependencies(inventory, topology, &config)
|
||||||
}
|
.await?;
|
||||||
};
|
|
||||||
|
|
||||||
let scores_vec = self.score.monitoring_stack.clone();
|
|
||||||
for s in scores_vec{
|
|
||||||
info!("Running: {}", s.name());
|
|
||||||
maestro.interpret(s).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Outcome::success(format!(
|
Ok(Outcome::success(format!(
|
||||||
"monitoring stack installed in {} namespace",
|
"succesfully deployed monitoring and alerting score"
|
||||||
self.score.namespace
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -116,3 +116,19 @@ pub fn yaml(input: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify that a string is a valid(ish) ingress path
|
||||||
|
/// Panics if path does not start with `/`
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn ingress_path(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as LitStr);
|
||||||
|
let path_str = input.value();
|
||||||
|
|
||||||
|
match path_str.starts_with("/") {
|
||||||
|
true => {
|
||||||
|
let expanded = quote! {(#path_str.to_string()) };
|
||||||
|
return TokenStream::from(expanded);
|
||||||
|
}
|
||||||
|
false => panic!("Invalid ingress path"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user