Compare commits

...

20 Commits

Author SHA1 Message Date
mbecker20
4aac301852 0.2.14 only try to merge files, filter nested directories 2023-04-01 19:35:04 +00:00
mbecker20
b375708bbd 0.2.13 support config directories 2023-04-01 19:02:43 +00:00
mbecker20
10b6a9482b update aws sdk verison and implement merge_config_files 2023-04-01 07:06:17 +00:00
mbecker20
84d45c5df8 0.2.12 fix docker build command interp 2023-03-31 18:06:18 +00:00
mbecker20
c6559814b1 frontend for docker build extra args and use buildx 2023-03-31 17:31:39 +00:00
mbecker20
c8c080183f remove publish for cli 2023-03-31 17:04:15 +00:00
mbecker20
597b67f799 0.2.11 support buildx and arbitrary extra args 2023-03-31 17:03:38 +00:00
mbecker20
ec52d5f422 support docker buildx build and passing arbitrary extra args 2023-03-31 16:57:02 +00:00
mbecker20
34806304d6 add center menu title bottom border and adjust copy menu 2023-03-31 05:41:35 +00:00
beckerinj
87953d5495 menu padding 2rem 2023-03-31 01:27:17 -04:00
beckerinj
b6c7c80c95 full width input for copy menu 2023-03-31 01:26:19 -04:00
beckerinj
77e568d5c3 small 2023-03-27 12:41:59 -04:00
mbecker20
699fc51cf7 link to build if click on image deployment header 2023-03-27 15:30:11 +00:00
mbecker20
21029c90b7 info page on stats page 2023-03-27 05:13:12 +00:00
mbecker20
6b0530eb7f brush up server stats page 2023-03-26 23:15:58 +00:00
beckerinj
f7061c7225 toggle to show absolutes for mem and disk stat graphs 2023-03-26 18:47:21 -04:00
mbecker20
750f698369 updates page 2023-03-26 02:20:39 +00:00
mbecker20
ec5ef42298 add max height / scrolling to copy menu target selector 2023-03-24 00:45:47 +00:00
beckerinj
46820b0044 increase the tab title padding 2023-03-23 20:36:31 -04:00
beckerinj
425a6648f7 improve summary styling 2023-03-23 03:13:19 -04:00
49 changed files with 891 additions and 634 deletions

8
.vscode/tasks.json vendored
View File

@@ -100,14 +100,6 @@
"cwd": "${workspaceFolder}/lib/monitor_client"
}
},
{
"type": "cargo",
"command": "publish",
"label": "publish monitor cli",
"options": {
"cwd": "${workspaceFolder}/cli"
}
},
{
"type": "shell",
"command": "docker compose up -d",

327
Cargo.lock generated
View File

@@ -37,6 +37,46 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-wincon",
"concolor-override",
"concolor-query",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
[[package]]
name = "anstyle-parse"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-wincon"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
dependencies = [
"anstyle",
"windows-sys 0.45.0",
]
[[package]]
name = "anyhow"
version = "1.0.69"
@@ -51,7 +91,7 @@ checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -86,9 +126,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "aws-config"
version = "0.54.1"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3d1e2a1f1ab3ac6c4b884e37413eaa03eb9d901e4fc68ee8f5c1d49721680e"
checksum = "1854be4730cc87602316707045a5c0585287419d54f293bbb52a82c895d9086a"
dependencies = [
"aws-credential-types",
"aws-http",
@@ -102,6 +142,7 @@ dependencies = [
"aws-smithy-types",
"aws-types",
"bytes",
"fastrand",
"hex",
"http",
"hyper",
@@ -115,12 +156,13 @@ dependencies = [
[[package]]
name = "aws-credential-types"
version = "0.54.1"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0696a0523a39a19087747e4dafda0362dc867531e3d72a3f195564c84e5e08"
checksum = "77e37e62f59cf3284067337da7467d842df8cfe3f5e5c06487ac7521819cf16d"
dependencies = [
"aws-smithy-async",
"aws-smithy-types",
"fastrand",
"tokio",
"tracing",
"zeroize",
@@ -128,9 +170,9 @@ dependencies = [
[[package]]
name = "aws-endpoint"
version = "0.54.1"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80a4f935ab6a1919fbfd6102a80c4fccd9ff5f47f94ba154074afe1051903261"
checksum = "9f38276d5875b19a9bb2b4ae049fd776c932fcc62068f78b71ce475093ccb4c8"
dependencies = [
"aws-smithy-http",
"aws-smithy-types",
@@ -142,9 +184,9 @@ dependencies = [
[[package]]
name = "aws-http"
version = "0.54.1"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82976ca4e426ee9ca3ffcf919d9b2c8d14d0cd80d43cc02173737a8f07f28d4d"
checksum = "67224bfece71e21a63ae82b1ebbfda05be28678a0fab06def03229c7a445d3fb"
dependencies = [
"aws-credential-types",
"aws-smithy-http",
@@ -161,9 +203,9 @@ dependencies = [
[[package]]
name = "aws-sdk-ec2"
version = "0.24.0"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b40ee2d853d8300a49513778beb79b1574ff9e9c94b30b1531bc0171d730ad64"
checksum = "fd180c0cb50c9b4306288a9c67f51a0e252ce09f05462b34a437b8b37107982e"
dependencies = [
"aws-credential-types",
"aws-endpoint",
@@ -189,9 +231,9 @@ dependencies = [
[[package]]
name = "aws-sdk-sso"
version = "0.24.0"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca0119bacf0c42f587506769390983223ba834e605f049babe514b2bd646dbb2"
checksum = "f4c10657158e12163d6b3fb1e0c9154e43656843794a3071d9ee63ec82a4de2d"
dependencies = [
"aws-credential-types",
"aws-endpoint",
@@ -209,13 +251,14 @@ dependencies = [
"regex",
"tokio-stream",
"tower",
"tracing",
]
[[package]]
name = "aws-sdk-sts"
version = "0.24.0"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "270b6a33969ebfcb193512fbd5e8ee5306888ad6c6d5d775cdbfb2d50d94de26"
checksum = "706a308b7277ac9aac78ab37933509659c6f978a8473e95d8e7a8103093133c3"
dependencies = [
"aws-credential-types",
"aws-endpoint",
@@ -239,9 +282,9 @@ dependencies = [
[[package]]
name = "aws-sig-auth"
version = "0.54.1"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "660a02a98ab1af83bd8d714afbab2d502ba9b18c49e7e4cddd6bf8837ff778cb"
checksum = "cebfa0f118afd7197185d5e097bfcdfca9f8410dca50435d67784405f1fd5a05"
dependencies = [
"aws-credential-types",
"aws-sigv4",
@@ -253,9 +296,9 @@ dependencies = [
[[package]]
name = "aws-sigv4"
version = "0.54.1"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdaf11005b7444e6cd66f600d09861a3aeb6eb89a0f003c7c9820dbab2d15297"
checksum = "19a4f5c05c8646d12b7bb3f18c04edc5ac5e8928ab80e1649e568190f2bc7b79"
dependencies = [
"aws-smithy-http",
"form_urlencoded",
@@ -272,9 +315,9 @@ dependencies = [
[[package]]
name = "aws-smithy-async"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63c712a28a4f2f2139759235c08bf98aca99d4fdf1b13c78c5f95613df0a5db9"
checksum = "8cd4b9b7d99263f75304fc1fcd752361cbc4cbf068b832acd8daeaaff44267eb"
dependencies = [
"futures-util",
"pin-project-lite",
@@ -284,9 +327,9 @@ dependencies = [
[[package]]
name = "aws-smithy-client"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "104ca17f56cde00a10207169697dfe9c6810db339d52fb352707e64875b30a44"
checksum = "748298b60bbd0594223ea136ceed2ed4b6d50970bcefa69a5ff6d710ce593854"
dependencies = [
"aws-smithy-async",
"aws-smithy-http",
@@ -300,6 +343,7 @@ dependencies = [
"hyper-rustls",
"lazy_static",
"pin-project-lite",
"rustls",
"tokio",
"tower",
"tracing",
@@ -307,9 +351,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "873f316f1833add0d3aa54ed1b0cd252ddd88c792a0cf839886400099971e844"
checksum = "d78510732b81040689dc146e3693bfbcf388ab88cbda667d3ef67f8869b0744a"
dependencies = [
"aws-smithy-types",
"bytes",
@@ -329,9 +373,9 @@ dependencies = [
[[package]]
name = "aws-smithy-http-tower"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f38231d3f5dac9ac7976f44e12803add1385119ffca9e5f050d8e980733d164"
checksum = "dc33689c27bbd8184412b45c4d1ab795d9a35402562d9fde6c53695a90969740"
dependencies = [
"aws-smithy-http",
"aws-smithy-types",
@@ -345,18 +389,18 @@ dependencies = [
[[package]]
name = "aws-smithy-json"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd83ff2b79e9f729746fcc8ad798676b68fe6ea72986571569a5306a277a182"
checksum = "ada31cab1b1d1f0abc5c4d1183de5b278597704851aa703801b82feabf19aa74"
dependencies = [
"aws-smithy-types",
]
[[package]]
name = "aws-smithy-query"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f0445dafe9d2cd50b44339ae3c3ed46549aad8ac696c52ad660b3e7ae8682b"
checksum = "b55358401b657d192f70f093927f01d73cc4859e2907956b20c4043c76624006"
dependencies = [
"aws-smithy-types",
"urlencoding",
@@ -364,9 +408,9 @@ dependencies = [
[[package]]
name = "aws-smithy-types"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8161232eda10290f5136610a1eb9de56aceaccd70c963a26a260af20ac24794f"
checksum = "474d145c2e0f82892841d2502bd546ca0dbc1e4e242c3563d96e7061054c268f"
dependencies = [
"base64-simd",
"itoa",
@@ -377,18 +421,18 @@ dependencies = [
[[package]]
name = "aws-smithy-xml"
version = "0.54.4"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343ffe9a9bb3f542675f4df0e0d5933513d6ad038ca3907ad1767ba690a99684"
checksum = "bb159921734d090b01c586a4fef73964f42fcb7eb53a8184b2db374bd6a6fc99"
dependencies = [
"xmlparser",
]
[[package]]
name = "aws-types"
version = "0.54.1"
version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f15b34253b68cde08e39b0627cc6101bcca64351229484b4743392c035d057"
checksum = "81fb02591b5075d318e0083dcb76df0e151db4ce48f987ecd00e5b53c7a6ba59"
dependencies = [
"aws-credential-types",
"aws-smithy-async",
@@ -676,40 +720,45 @@ dependencies = [
[[package]]
name = "clap"
version = "4.1.6"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3"
checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
dependencies = [
"bitflags",
"clap_builder",
"clap_derive",
"clap_lex",
"is-terminal",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
dependencies = [
"anstream",
"anstyle",
"bitflags",
"clap_lex",
"strsim",
"termcolor",
]
[[package]]
name = "clap_derive"
version = "4.1.0"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 2.0.12",
]
[[package]]
name = "clap_lex"
version = "0.3.2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
dependencies = [
"os_str_bytes",
]
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
[[package]]
name = "codespan-reporting"
@@ -732,6 +781,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "concolor-override"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
[[package]]
name = "concolor-query"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -740,7 +804,7 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"anyhow",
"async_timing_util",
@@ -758,8 +822,9 @@ dependencies = [
"hex",
"hmac",
"jwt",
"merge_config_files",
"monitor_helpers",
"monitor_types 0.2.10",
"monitor_types 0.2.14",
"mungos",
"periphery_client",
"serde",
@@ -886,7 +951,7 @@ dependencies = [
"proc-macro2",
"quote",
"scratch",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -903,7 +968,7 @@ checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -946,7 +1011,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -960,7 +1025,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -971,7 +1036,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core 0.13.4",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -982,7 +1047,7 @@ checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"
dependencies = [
"darling_core 0.14.3",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -993,10 +1058,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
[[package]]
name = "db_client"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"anyhow",
"monitor_types 0.2.10",
"monitor_types 0.2.14",
"mungos",
]
@@ -1008,7 +1073,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1029,7 +1094,7 @@ dependencies = [
"darling 0.14.3",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1039,7 +1104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
dependencies = [
"derive_builder_core",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1052,7 +1117,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version 0.4.0",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1074,7 +1139,7 @@ checksum = "fe165e7ead196bbbf44c7ce11a7a21157b5c002ce46d7098ff9c556784a4912d"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1118,7 +1183,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1256,7 +1321,7 @@ checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -1443,9 +1508,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "hyper"
version = "0.14.24"
version = "0.14.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899"
dependencies = [
"bytes",
"futures-channel",
@@ -1768,6 +1833,18 @@ dependencies = [
"autocfg",
]
[[package]]
name = "merge_config_files"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48ed7c516ae04f732a7651e6bbbbdfd7762170fca6b6d263c14a105c41cab9a5"
dependencies = [
"serde",
"serde_json",
"thiserror",
"toml",
]
[[package]]
name = "mime"
version = "0.3.16"
@@ -1857,12 +1934,12 @@ dependencies = [
[[package]]
name = "monitor_cli"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"async_timing_util",
"clap",
"colored",
"monitor_types 0.2.10",
"monitor_types 0.2.14",
"rand",
"run_command",
"serde",
@@ -1874,12 +1951,12 @@ dependencies = [
[[package]]
name = "monitor_client"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"anyhow",
"envy",
"futures-util",
"monitor_types 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"monitor_types 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest",
"serde",
"serde_derive",
@@ -1891,11 +1968,11 @@ dependencies = [
[[package]]
name = "monitor_helpers"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"anyhow",
"axum",
"monitor_types 0.2.10",
"monitor_types 0.2.14",
"rand",
"serde",
"serde_json",
@@ -1904,7 +1981,7 @@ dependencies = [
[[package]]
name = "monitor_periphery"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"anyhow",
"async_timing_util",
@@ -1915,8 +1992,9 @@ dependencies = [
"dotenv",
"envy",
"futures",
"merge_config_files",
"monitor_helpers",
"monitor_types 0.2.10",
"monitor_types 0.2.14",
"run_command",
"serde",
"serde_derive",
@@ -1930,7 +2008,7 @@ dependencies = [
[[package]]
name = "monitor_types"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"anyhow",
"bollard",
@@ -1947,9 +2025,9 @@ dependencies = [
[[package]]
name = "monitor_types"
version = "0.2.10"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bf35db6341431dea9f062f5d676305a213834638410fb9cdc49ca2521635c43"
checksum = "19f0b594fa8c0af0c9d4ad7962a15782c7aa4afa575c60df69341f2de012e0c5"
dependencies = [
"anyhow",
"bollard",
@@ -2122,7 +2200,7 @@ checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2144,12 +2222,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "os_str_bytes"
version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
[[package]]
name = "outref"
version = "0.5.1"
@@ -2196,11 +2268,11 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "periphery_client"
version = "0.2.10"
version = "0.2.14"
dependencies = [
"anyhow",
"futures-util",
"monitor_types 0.2.10",
"monitor_types 0.2.14",
"reqwest",
"serde",
"serde_json",
@@ -2225,7 +2297,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2252,35 +2324,11 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.51"
version = "1.0.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
dependencies = [
"unicode-ident",
]
@@ -2293,9 +2341,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
@@ -2637,7 +2685,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2669,7 +2717,7 @@ checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2727,7 +2775,7 @@ dependencies = [
"darling 0.13.4",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2856,7 +2904,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -2885,6 +2933,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
@@ -2966,7 +3025,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3050,7 +3109,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3245,7 +3304,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3336,7 +3395,7 @@ checksum = "89851716b67b937e393b3daa8423e67ddfc4bbbf1654bcf05488e95e0828db0c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3364,7 +3423,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9"
dependencies = [
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
@@ -3432,6 +3491,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.3.0"
@@ -3503,7 +3568,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"wasm-bindgen-shared",
]
@@ -3537,7 +3602,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@@ -1,6 +1,6 @@
[package]
name = "monitor_cli"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
authors = ["MoghTech"]
description = "monitor cli | tools to setup monitor system"

View File

@@ -1,6 +1,6 @@
[package]
name = "core"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -35,5 +35,6 @@ futures-util = "0.3"
diff-struct = "0.5"
typeshare = "1.0.0"
hex = "0.4"
aws-config = "0.54"
aws-sdk-ec2 = "0.24"
aws-config = "0.55"
aws-sdk-ec2 = "0.25"
merge_config_files = "0.1.3"

View File

@@ -1,6 +1,7 @@
use std::time::Duration;
use anyhow::{anyhow, Context};
use aws_sdk_ec2::Client;
use diff::Diff;
use helpers::{all_logs_success, to_monitor_name};
use mungos::{doc, to_bson};
@@ -14,7 +15,7 @@ use types::{
use crate::{
auth::RequestUser,
cloud::aws::{
self, create_ec2_client, create_instance_with_ami, terminate_ec2_instance, Ec2Instance,
create_ec2_client, create_instance_with_ami, terminate_ec2_instance, Ec2Instance,
},
helpers::empty_or_only_spaces,
state::State,
@@ -426,7 +427,7 @@ impl State {
async fn create_ec2_instance_for_build(
&self,
build: &Build,
) -> anyhow::Result<(Ec2Instance, Option<aws::Client>, Vec<Log>)> {
) -> anyhow::Result<(Ec2Instance, Option<Client>, Vec<Log>)> {
if build.aws_config.is_none() {
return Err(anyhow!("build has no aws_config attached"));
}
@@ -527,7 +528,7 @@ impl State {
async fn terminate_ec2_instance(
&self,
aws_client: aws::Client,
aws_client: Client,
server: &Ec2Instance,
update: &mut Update,
) {

View File

@@ -12,7 +12,7 @@ use crate::{
state::{State, StateExtension},
};
const NUM_UPDATES_PER_PAGE: usize = 10;
const NUM_UPDATES_PER_PAGE: usize = 20;
pub fn router() -> Router {
Router::new().route(

View File

@@ -1,14 +1,14 @@
use std::time::Duration;
use anyhow::{anyhow, Context};
use aws_sdk_ec2::model::{
BlockDeviceMapping, EbsBlockDevice, InstanceNetworkInterfaceSpecification, InstanceStateChange,
InstanceStateName, InstanceStatus, ResourceType, Tag, TagSpecification,
};
pub use aws_sdk_ec2::{
model::InstanceType,
output::{DescribeInstanceStatusOutput, TerminateInstancesOutput},
Client, Region,
use aws_sdk_ec2::{
config::Region,
types::{
BlockDeviceMapping, EbsBlockDevice, InstanceNetworkInterfaceSpecification,
InstanceStateChange, InstanceStateName, InstanceStatus, InstanceType, ResourceType, Tag,
TagSpecification,
},
Client,
};
use types::Server;

View File

@@ -1,6 +1,6 @@
use axum_extra::routing::SpaRouter;
use dotenv::dotenv;
use helpers::parse_config_file;
use merge_config_files::parse_config_file;
use mungos::Deserialize;
use types::CoreConfig;
@@ -15,7 +15,7 @@ struct Env {
pub fn load() -> (CoreConfig, SpaRouter) {
dotenv().ok();
let env: Env = envy::from_env().expect("failed to parse environment variables");
let config = parse_config_file(&env.config_path).expect("failed to parse config");
let config = parse_config_file(env.config_path).expect("failed to parse config");
let spa_router = SpaRouter::new("/assets", env.frontend_path);
(config, spa_router)
}

View File

@@ -13,7 +13,7 @@ The easiest way to do this is to follow the [monitor guide](https://github.com/m
1. Download the periphery binary from the latest [release](https://github.com/mbecker20/monitor/releases).
2. Create and edit your config files, following the [config example](https://github.com/mbecker20/monitor/blob/main/config_example/periphery.config.example.toml). The monitor cli can be used to add the boilerplate: ```monitor periphery gen-config --path /path/to/config.toml```. The files can be anywhere, and can be passed to periphery via the ```--config-path``` flag.
2. Create and edit your config files, following the [config example](https://github.com/mbecker20/monitor/blob/main/config_example/periphery.config.example.toml). The monitor cli can be used to add the boilerplate: ```monitor periphery gen-config --path /path/to/config.toml```. The files can be anywhere, and can be passed to periphery via the ```--config-path``` argument.
3. Ensure that inbound connectivity is allowed on the port specified in periphery.config.toml (default 8000).
@@ -26,16 +26,22 @@ The easiest way to do this is to follow the [monitor guide](https://github.com/m
```
periphery \
--config-path /path/to/periphery.config.base.toml \
--config-path /other_path/to/periphery.config.overide.toml \
--config-path /other_path/to/overide-periphery-config-directory \
--config-keyword periphery \
--config-keyword config \
--merge-nested-config \
--home_dir /home/username
```
## passing config files
when you pass multiple config files, later --config-path given in the command will always overide previous ones.
Either file paths or directory paths can be passed to ```--config-path```.
there are two ways to merge config files. The default behavior is to completely replace any base fields with whatever fields are present in the overide config. So if you pass ```allowed_ips = []``` in your overide config, the final allowed_ips will be an empty list as well.
When using directories, the file entries can be filtered by name with the ```--config-keyword``` argument, which can be passed multiple times to add more keywords. If passed, then only config files with file names that contain all keywords will be merged.
When passing multiple config files, later --config-path given in the command will always overide previous ones. Directory config files are merged in alphabetical order by name, so ```config_b.toml``` will overide ```config_a.toml```.
There are two ways to merge config files. The default behavior is to completely replace any base fields with whatever fields are present in the overide config. So if you pass ```allowed_ips = []``` in your overide config, the final allowed_ips will be an empty list as well.
```--merge-nested-config``` will merge config fields recursively and extend config array fields.

View File

@@ -11,6 +11,7 @@ const Users = lazy(() => import("./components/users/Users"));
const User = lazy(() => import("./components/users/User"));
const Stats = lazy(() => import("./components/stats/Stats"));
const Account = lazy(() => import("./components/account/Account"));
const Updates = lazy(() => import("./components/Updates"));
const App: Component = () => {
const { user } = useUser();
@@ -19,6 +20,7 @@ const App: Component = () => {
<Topbar />
<Routes>
<Route path="/" component={Home} />
<Route path="/updates" component={Updates} />
<Route path="/build/:id" component={Build} />
<Route path="/deployment/:id" component={Deployment} />
<Route path="/server/:id" component={Server} />

View File

@@ -69,10 +69,10 @@ const CopyMenu: Component<{
targetClass="blue"
content={() => (
<Grid placeItems="center">
<Flex alignItems="center">
<Flex class="full-width" alignItems="center">
<Input
placeholder="copy name"
class="card dark"
class="card dark full-width"
style={{ padding: "0.5rem" }}
value={newName()}
onEdit={setNewName}
@@ -87,6 +87,8 @@ const CopyMenu: Component<{
targetClass="blue"
targetStyle={{ display: "flex", gap: "0.5rem" }}
searchStyle={{ width: "100%" }}
menuClass="scroller"
menuStyle={{ "max-height": "40vh" }}
position="bottom right"
useSearch
/>

View File

@@ -0,0 +1,157 @@
import { A } from "@solidjs/router";
import {
Component,
createEffect,
createMemo,
createSignal,
For,
Show,
} from "solid-js";
import { OPERATIONS } from "..";
import { useAppDimensions } from "../state/DimensionProvider";
import { useAppState } from "../state/StateProvider";
import { Operation, Update as UpdateType, UpdateStatus } from "../types";
import { readableMonitorTimestamp, readableVersion } from "../util/helpers";
import Icon from "./shared/Icon";
import Input from "./shared/Input";
import Flex from "./shared/layout/Flex";
import Grid from "./shared/layout/Grid";
import Loading from "./shared/loading/Loading";
import Selector from "./shared/menu/Selector";
import UpdateMenu from "./update/UpdateMenu";
const Updates: Component<{}> = (p) => {
const { isMobile } = useAppDimensions();
const { updates, usernames, name_from_update_target } = useAppState();
const [operation, setOperation] = createSignal<Operation>();
createEffect(() => {
if (operation()) {
updates.load([operation()!]);
} else {
updates.load();
}
});
const [search, setSearch] = createSignal("");
const filtered_updates = createMemo(() => {
return updates.collection()?.filter((u) => {
const name = name_from_update_target(u.target);
if (name.includes(search())) return true;
const username = usernames.get(u.operator);
if (username?.includes(search())) return true;
});
});
return (
<Grid class="full-width card shadow">
<Flex alignItems="center" justifyContent="space-between">
<h1>updates</h1>
<Flex alignItems="center">
<Input class="lightgrey" placeholder="search" onEdit={setSearch} />
<Selector
label={isMobile() ? undefined : "operation: "}
selected={operation() ? operation()! : "all"}
items={["all", ...OPERATIONS]}
onSelect={(o) =>
o === "all"
? setOperation(undefined)
: setOperation(o.replaceAll(" ", "_") as Operation)
}
targetClass="blue"
position="bottom right"
searchStyle={{ width: "15rem" }}
menuClass="scroller"
menuStyle={{ "max-height": "50vh" }}
useSearch
/>
</Flex>
</Flex>
<Show
when={updates.loaded()}
fallback={
<Flex justifyContent="center">
<Loading type="three-dot" />
</Flex>
}
>
<For each={filtered_updates()}>
{(update) => <Update update={update} />}
</For>
<Show when={!updates.noMore()}>
<button
class="grey full-width"
onClick={() =>
operation()
? updates.loadMore([operation()!])
: updates.loadMore()
}
>
load more
</button>
</Show>
</Show>
</Grid>
);
};
export default Updates;
const Update: Component<{ update: UpdateType }> = (p) => {
const { isMobile } = useAppDimensions();
const { usernames, name_from_update_target } = useAppState();
const name = () => name_from_update_target(p.update.target);
const operation = () => {
if (p.update.operation === Operation.BuildBuild) {
return `build ${readableVersion(p.update.version!)}`;
}
return `${p.update.operation.replaceAll("_", " ")}${
p.update.version ? " " + readableVersion(p.update.version) : ""
}`;
};
const link_to = () => {
return p.update.target.type === "System"
? "/"
: `/${p.update.target.type.toLowerCase()}/${p.update.target.id}`;
};
return (
<Flex
class="card light shadow wrap"
justifyContent="space-between"
alignItems="center"
>
<Flex
alignItems="center"
justifyContent="space-between"
style={{ width: isMobile() ? "100%" : undefined }}
>
<A style={{ padding: 0 }} href={link_to()}>
<h2 class="text-hover">{name()}</h2>
</A>
<div
style={{
color: !p.update.success ? "rgb(182, 47, 52)" : "inherit",
}}
>
{operation()}
</div>
<Show when={p.update.status === UpdateStatus.InProgress}>
<div style={{ opacity: 0.7 }}>(in progress)</div>
</Show>
</Flex>
<Flex
alignItems="center"
justifyContent="space-between"
style={{ width: isMobile() ? "100%" : undefined }}
>
<Flex gap="0.5rem">
<Icon type="user" />
<div>{usernames.get(p.update.operator)}</div>
</Flex>
<Flex alignItems="center">
<div style={{ "place-self": "center end" }}>
{readableMonitorTimestamp(p.update.start_ts)}
</div>
<UpdateMenu update={p.update} />
</Flex>
</Flex>
</Flex>
);
};

View File

@@ -11,6 +11,8 @@ import BuildArgs from "./BuildArgs";
import Version from "./Version";
import Repo from "./Repo";
import WebhookUrl from "./WebhookUrl";
import ExtraArgs from "./ExtraArgs";
import UseBuildx from "./UseBuildx";
const BuildConfig: Component<{}> = (p) => {
const { build, reset, save, userCanUpdate } = useConfig();
@@ -23,6 +25,8 @@ const BuildConfig: Component<{}> = (p) => {
<Docker />
<CliBuild />
<BuildArgs />
<ExtraArgs />
<UseBuildx />
<Show when={userCanUpdate()}>
<WebhookUrl />
</Show>

View File

@@ -0,0 +1,59 @@
import { Component, For, Show } from "solid-js";
import Icon from "../../../shared/Icon";
import Input from "../../../shared/Input";
import Flex from "../../../shared/layout/Flex";
import Grid from "../../../shared/layout/Grid";
import { useConfig } from "../Provider";
const ExtraArgs: Component<{}> = (p) => {
const { build, setBuild, userCanUpdate } = useConfig();
const onAdd = () => {
setBuild("docker_build_args", "extra_args", (extra_args: any) => [
...extra_args,
"",
]);
};
const onRemove = (index: number) => {
setBuild("docker_build_args", "extra_args", (extra_args) =>
extra_args!.filter((_, i) => i !== index)
);
};
return (
<Grid class="config-item shadow">
<Flex justifyContent="space-between" alignItems="center">
<h1>extra args</h1>
<Show when={userCanUpdate()}>
<button class="green" onClick={onAdd}>
<Icon type="plus" />
</button>
</Show>
</Flex>
<For each={[...build.docker_build_args!.extra_args!.keys()]}>
{(_, index) => (
<Flex
justifyContent={userCanUpdate() ? "space-between" : undefined}
alignItems="center"
style={{ "flex-wrap": "wrap" }}
>
<Input
placeholder="--extra-arg=value"
value={build.docker_build_args!.extra_args![index()]}
style={{ width: "80%" }}
onEdit={(value) =>
setBuild("docker_build_args", "extra_args", index(), value)
}
disabled={!userCanUpdate()}
/>
<Show when={userCanUpdate()}>
<button class="red" onClick={() => onRemove(index())}>
<Icon type="minus" />
</button>
</Show>
</Flex>
)}
</For>
</Grid>
);
};
export default ExtraArgs;

View File

@@ -0,0 +1,30 @@
import { Component, Show } from "solid-js";
import Flex from "../../../shared/layout/Flex";
import { useConfig } from "../Provider";
const UseBuildx: Component<{}> = (p) => {
const { build, setBuild, userCanUpdate } = useConfig();
const use_buildx = () => build.docker_build_args?.use_buildx || false;
return (
<Flex
class="config-item shadow"
alignItems="center"
justifyContent="space-between"
>
<h1>use buildx</h1>
<Show
when={userCanUpdate()}
fallback={<div>{use_buildx() ? "enabled" : "disabled"}</div>}
>
<button
class={use_buildx() ? "green" : "red"}
onClick={() => setBuild("docker_build_args", "use_buildx", (c) => !c)}
>
{use_buildx() ? "enabled" : "disabled"}
</button>
</Show>
</Flex>
);
};
export default UseBuildx;

View File

@@ -119,7 +119,18 @@ const Header: Component<{}> = (p) => {
/>
</Show>
</Show>
<div style={{ opacity: 0.7 }}>{image()}</div>
<Show
when={deployment().deployment.build_id}
fallback={<div style={{ opacity: 0.7 }}>{image()}</div>}
>
<A
href={`/build/${deployment().deployment.build_id}`}
class="text-hover"
style={{ opacity: 0.7, padding: 0 }}
>
{image()}
</A>
</Show>
</Flex>
<Show when={userCanUpdate()}>
<Flex alignItems="center">

View File

@@ -14,26 +14,29 @@ const Summary: Component<{}> = (p) => {
const serverCount = useServerCount();
return (
<Grid
class="card shadow"
class="full-size"
gridTemplateColumns={isMobile() ? "1fr" : "1fr 1fr"}
style={{
width: "100%",
height: "100%",
"box-sizing": "border-box",
}}
placeItems="center"
gap="0"
>
<div
style={{ width: `${PIE_CHART_SIZE}px`, height: `${PIE_CHART_SIZE}px` }}
>
<PieChart title="deployments" sections={deployentCount()} />
</div>
<div
style={{ width: `${PIE_CHART_SIZE}px`, height: `${PIE_CHART_SIZE}px` }}
>
<PieChart title="servers" sections={serverCount()} />
</div>
<Grid class="card shadow full-size" placeItems="center">
<div
style={{
width: `${PIE_CHART_SIZE}px`,
height: `${PIE_CHART_SIZE}px`,
}}
>
<PieChart title="deployments" sections={deployentCount()} />
</div>
</Grid>
<Grid class="card shadow full-size" placeItems="center">
<div
style={{
width: `${PIE_CHART_SIZE}px`,
height: `${PIE_CHART_SIZE}px`,
}}
>
<PieChart title="servers" sections={serverCount()} />
</div>
</Grid>
</Grid>
);
};

View File

@@ -1,166 +0,0 @@
import { Component, createMemo, For, Show } from "solid-js";
import { useAppState } from "../../state/StateProvider";
import { DockerContainerState, ServerStatus } from "../../types";
import Grid from "../shared/layout/Grid";
import Flex from "../shared/layout/Flex";
const Summary: Component<{}> = (p) => {
return (
<Grid class="card shadow" gridTemplateRows="auto 1fr 1fr 1fr">
<h1>summary</h1>
<DeploymentsSummary />
<ServersSummary />
<BuildsSummary />
</Grid>
);
};
export default Summary;
const SummaryItem: Component<{
title: string;
metrics: Array<{ title: string; class: string; count?: number }>;
}> = (p) => {
return (
<Flex
class="card light shadow wrap"
justifyContent="space-between"
alignItems="center"
>
<h2>{p.title}</h2>
<Flex class="wrap">
<For each={p.metrics}>
{(metric) => (
<Show when={metric?.count && metric.count > 0}>
<Flex gap="0.4rem" alignItems="center">
<div>{metric.title}</div>
<h2 class={metric.class}>{metric.count}</h2>
</Flex>
</Show>
)}
</For>
</Flex>
</Flex>
);
};
const BuildsSummary = () => {
const { builds } = useAppState();
return (
<SummaryItem
title="builds"
metrics={[
{ title: "total", class: "text-green", count: builds.ids()?.length },
]}
/>
);
};
const DeploymentsSummary = () => {
const deployentCount = useDeploymentCount();
return (
<SummaryItem
title="deployments"
metrics={[
{
title: "total",
class: "text-green",
count: deployentCount().total,
},
{
title: "running",
class: "text-green",
count: deployentCount().running,
},
{
title: "stopped",
class: "text-red",
count: deployentCount().stopped,
},
{
title: "not deployed",
class: "text-blue",
count: deployentCount().notDeployed,
},
{
title: "unknown",
class: "text-blue",
count: deployentCount().unknown,
},
]}
/>
);
};
const ServersSummary = () => {
const serverCount = useServerCount();
return (
<SummaryItem
title="servers"
metrics={[
{ title: "total", class: "text-green", count: serverCount().total },
{ title: "healthy", class: "text-green", count: serverCount().healthy },
{
title: "unhealthy",
class: "text-red",
count: serverCount().unhealthy,
},
{
title: "disabled",
class: "text-blue",
count: serverCount().disabled,
},
]}
/>
);
};
function useDeploymentCount() {
const { deployments } = useAppState();
const count = createMemo(() => {
const ids = deployments.ids();
if (!ids)
return { total: 0, running: 0, stopped: 0, notDeployed: 0, unknown: 0 };
let running = 0;
let stopped = 0;
let notDeployed = 0;
let unknown = 0;
for (const id of ids) {
const state = deployments.get(id)!.state;
if (state === DockerContainerState.NotDeployed) {
notDeployed++;
} else if (state === DockerContainerState.Running) {
running++;
} else if (state === DockerContainerState.Exited) {
stopped++;
} else if (state === DockerContainerState.Unknown) {
unknown++;
}
}
return { total: ids.length, running, stopped, notDeployed, unknown };
});
return count;
}
function useServerCount() {
const { servers } = useAppState();
const count = createMemo(() => {
const ids = servers.ids();
if (!ids) return { total: 0, healthy: 0, unhealthy: 0, disabled: 0 };
let healthy = 0;
let unhealthy = 0;
let disabled = 0;
for (const id of ids) {
const server = servers.get(id)!;
if (server.status === ServerStatus.Disabled) {
disabled++;
} else if (server.status === ServerStatus.Ok) {
healthy++;
} else if (server.status === ServerStatus.NotOk) {
unhealthy++;
}
}
return { total: ids.length, healthy, unhealthy, disabled };
});
return count;
}

View File

@@ -14,18 +14,9 @@ import UpdateMenu from "../../update/UpdateMenu";
import s from "./update.module.scss";
const Update: Component<{ update: UpdateType }> = (p) => {
const { deployments, servers, builds, usernames } = useAppState();
const name = () => {
if (p.update.target.type === "Deployment" && deployments.loaded()) {
return deployments.get(p.update.target.id!)?.deployment.name || "deleted";
} else if (p.update.target.type === "Server" && servers.loaded()) {
return servers.get(p.update.target.id)?.server.name || "deleted";
} else if (p.update.target.type === "Build" && builds.loaded()) {
return builds.get(p.update.target.id)?.name || "deleted";
} else {
return "monitor";
}
};
const { usernames, name_from_update_target } =
useAppState();
const name = () => name_from_update_target(p.update.target);
const operation = () => {
if (p.update.operation === Operation.BuildBuild) {
return `build ${readableVersion(p.update.version!)}`;

View File

@@ -1,4 +1,6 @@
import { A } from "@solidjs/router";
import { Component, createEffect, createSignal, For, Show } from "solid-js";
import { OPERATIONS } from "../../..";
import { useAppState } from "../../../state/StateProvider";
import { Operation } from "../../../types";
import Flex from "../../shared/layout/Flex";
@@ -7,10 +9,6 @@ import Loading from "../../shared/loading/Loading";
import Selector from "../../shared/menu/Selector";
import Update from "./Update";
const OPERATIONS = Object.values(Operation)
.filter((e) => e !== "none" && !e.includes("user"))
.map((e) => e.replaceAll("_", " "));
const Updates: Component<{}> = () => {
const { updates } = useAppState();
const [operation, setOperation] = createSignal<Operation>();
@@ -24,8 +22,11 @@ const Updates: Component<{}> = () => {
return (
<Grid class="card shadow" style={{ "flex-grow": 1 }}>
<Flex alignItems="center" justifyContent="space-between">
<h1>updates</h1>
<A href="/updates" style={{ padding: 0 }}>
<h1>updates</h1>
</A>
<Selector
label="operation: "
selected={operation() ? operation()! : "all"}
items={["all", ...OPERATIONS]}
onSelect={(o) =>
@@ -50,7 +51,7 @@ const Updates: Component<{}> = () => {
}
>
<Grid class="updates-container-small scroller">
<For each={updates.collection()!}>
<For each={updates.collection()}>
{(update) => <Update update={update} />}
</For>
<Show when={!updates.noMore()}>

View File

@@ -7,7 +7,6 @@ import { readableStorageAmount } from "../../../util/helpers";
import Flex from "../../shared/layout/Flex";
import Grid from "../../shared/layout/Grid";
import Loading from "../../shared/loading/Loading";
import HoverMenu from "../../shared/menu/HoverMenu";
const Info: Component<{}> = (p) => {
const { isMobile } = useAppDimensions();

View File

@@ -76,7 +76,7 @@ const Child: Component<{
>
<Grid
class={combineClasses(s.Menu, "shadow")}
style={{ padding: (p.padding as any) || "1rem", ...p.style }}
style={{ padding: (p.padding as any) || "2rem", ...p.style }}
onClick={(e) => e.stopPropagation()}
onPointerDown={(e) => e.stopPropagation()}
>

View File

@@ -37,10 +37,10 @@ const Selector: Component<{
}> = (p) => {
const [show, toggle] = useToggle();
const [search, setSearch] = createSignal("");
let ref: HTMLInputElement | undefined;
let search_ref: HTMLInputElement | undefined;
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected);
createEffect(() => {
if (show()) setTimeout(() => ref?.focus(), 200);
if (show()) setTimeout(() => search_ref?.focus(), 200);
});
return (
<Show
@@ -70,7 +70,7 @@ const Selector: Component<{
<>
<Show when={p.useSearch}>
<Input
ref={ref}
ref={search_ref}
placeholder="search"
value={search()}
onEdit={setSearch}

View File

@@ -23,6 +23,7 @@
width: fit-content;
/* border: solid 1px rgba(2, 107, 121, 0.25); */
background-color: c.$grey;
border: solid c.$darkgrey 2px;
z-index: 21;
border-radius: 0.25rem;
box-sizing: border-box;
@@ -142,6 +143,11 @@ $anim-time: 350ms;
background-color: rgba(0, 0, 0, 0.4);
}
.CenterMenuHeader {
border-bottom: solid rgba(c.$lightgrey, 0.9) 2px;
padding-bottom: 1rem;
}
.SelectorItem:hover {
background-color: c.$lightgrey;
}

View File

@@ -9,7 +9,7 @@
.TabTitle {
display: grid;
place-items: center;
padding: 0.25rem 0.5rem;
padding: 0.75rem;
border-radius: 0rem;
cursor: pointer;
font-size: 1rem;

View File

@@ -1,10 +1,19 @@
import { Accessor, Component, For, ParentComponent, Show } from "solid-js";
import {
Accessor,
Component,
For,
JSXElement,
ParentComponent,
Show,
} from "solid-js";
import { COLORS } from "../../style/colors";
import { SystemStats, SystemStatsRecord } from "../../types";
import {
convertTsMsToLocalUnixTsInSecs,
get_to_one_sec_divisor,
} from "../../util/helpers";
import { useLocalStorage, useLocalStorageToggle } from "../../util/hooks";
import Flex from "../shared/layout/Flex";
import Grid from "../shared/layout/Grid";
import LightweightChart, { LightweightValue } from "../shared/LightweightChart";
import s from "./stats.module.scss";
@@ -15,13 +24,18 @@ const SMALL_CHART_HEIGHT = "150px";
const SingleStatChart: Component<{
line?: LightweightValue[];
header: string;
headerRight?: JSXElement;
label: string;
color: string;
small?: boolean;
disableScroll?: boolean;
}> = (p) => {
return (
<StatChartContainer header={p.header} small={p.small}>
<StatChartContainer
header={p.header}
headerRight={p.headerRight}
small={p.small}
>
<Show when={p.line}>
<LightweightChart
class={s.LightweightChart}
@@ -44,23 +58,25 @@ const SingleStatChart: Component<{
const StatChartContainer: ParentComponent<{
header: string;
headerRight?: JSXElement;
small?: boolean;
}> = (p) => {
return (
<Grid
gap="0.5rem"
class="card shadow"
class="card shadow full-width"
style={{
height: "fit-content",
width: "100%",
"box-sizing": "border-box",
"padding-top": "0.5rem",
"padding-bottom": "0.2rem",
}}
>
<Show when={!p.small} fallback={<div>{p.header}</div>}>
<h2>{p.header}</h2>
</Show>
<Flex justifyContent="space-between">
<Show when={!p.small} fallback={<div>{p.header}</div>}>
<h2>{p.header}</h2>
</Show>
{p.headerRight}
</Flex>
{p.children}
</Grid>
);
@@ -152,20 +168,42 @@ export const MemChart: Component<{
small?: boolean;
disableScroll?: boolean;
}> = (p) => {
const [absolute, toggleAbsolute] = useLocalStorageToggle("stats-mem-mode-v2");
const symbol = () => (absolute() ? "GiB" : "%");
const line = () => {
return p.stats()?.map((s) => {
return {
time: convertTsMsToLocalUnixTsInSecs(
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
),
value: (100 * s.mem_used_gb) / s.mem_total_gb,
};
});
if (absolute()) {
return p.stats()?.map((s) => {
return {
time: convertTsMsToLocalUnixTsInSecs(
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
),
value: s.mem_used_gb,
};
});
} else {
return p.stats()?.map((s) => {
return {
time: convertTsMsToLocalUnixTsInSecs(
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
),
value: (100 * s.mem_used_gb) / s.mem_total_gb,
};
});
}
};
return (
<SingleStatChart
header="memory"
label="mem %"
headerRight={
<button
class="green"
style={{ padding: "0.2rem" }}
onClick={toggleAbsolute}
>
{symbol()}
</button>
}
label={`mem ${symbol()}`}
color={COLORS.green}
line={line()}
small={p.small}
@@ -179,20 +217,43 @@ export const DiskChart: Component<{
small?: boolean;
disableScroll?: boolean;
}> = (p) => {
const [absolute, toggleAbsolute] =
useLocalStorageToggle("stats-disk-mode-v2");
const symbol = () => (absolute() ? "GiB" : "%");
const line = () => {
return p.stats()?.map((s) => {
return {
time: convertTsMsToLocalUnixTsInSecs(
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
),
value: (100 * s.disk.used_gb) / s.disk.total_gb,
};
});
if (absolute()) {
return p.stats()?.map((s) => {
return {
time: convertTsMsToLocalUnixTsInSecs(
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
),
value: s.disk.used_gb,
};
});
} else {
return p.stats()?.map((s) => {
return {
time: convertTsMsToLocalUnixTsInSecs(
(s as SystemStatsRecord).ts || (s as SystemStats).refresh_ts
),
value: (100 * s.disk.used_gb) / s.disk.total_gb,
};
});
}
};
return (
<SingleStatChart
header="disk"
label="disk %"
headerRight={
<button
class="orange"
style={{ padding: "0.2rem" }}
onClick={toggleAbsolute}
>
{symbol()}
</button>
}
label={`disk ${symbol()}`}
color={COLORS.orange}
line={line()}
small={p.small}

View File

@@ -29,20 +29,23 @@ const HistoricalStats: Component<{
const params = useParams();
const { timelength, page } = useStatsState();
const [stats, setStats] = createSignal<SystemStatsRecord[]>();
createEffect(() => {
client
const [loading, setLoading] = createSignal(false);
createEffect(async () => {
setLoading(true);
const stats = await client
.get_server_stats_history(params.id, {
interval: timelength(),
page: page(),
limit: 500,
networks: true,
components: true,
})
.then(setStats);
});
setStats(stats);
setLoading(false);
});
return (
<Grid class={s.Content} placeItems="start center">
<Show when={stats()} fallback={<Loading type="three-dot" />}>
<Show when={stats() && !loading()} fallback={<Loading type="three-dot" />}>
<SimpleTabs
localStorageKey="historical-stats-view-v3"
defaultSelected="basic"

View File

@@ -1,12 +1,19 @@
import { useParams } from "@solidjs/router";
import { ParentComponent, createContext, useContext, createSignal, createResource } from "solid-js";
import { client } from "../..";
import { useAppState } from "../../state/StateProvider";
import { SystemInformation, Timelength } from "../../types";
import { useLocalStorage } from "../../util/hooks";
export enum StatsView {
Current = "current",
Historical = "historical",
Info = "info"
}
const value = () => {
const params = useParams();
const [view, setView] = useLocalStorage("current", "stats-view-v1");
const [view, setView] = useLocalStorage(StatsView.Current, "stats-view-v2");
const [timelength, setTimelength] = useLocalStorage(
Timelength.OneMinute,
"stats-timelength-v3"
@@ -16,12 +23,7 @@ const value = () => {
`${params.id}-stats-poll-v3`
);
const [page, setPage] = createSignal(0);
// const [wsOpen, setWsOpen] = createSignal(false);
const [sysInfo] = createResource<SystemInformation>(() =>
client.get_server_system_info(params.id)
);
return {
sysInfo,
view,
setView,
timelength,

View File

@@ -1,20 +1,16 @@
import { A, useParams } from "@solidjs/router";
import {
Component,
Match,
Show,
Switch,
} from "solid-js";
import { MAX_PAGE_WIDTH } from "../..";
import { Component, createResource, For, Match, Show, Switch } from "solid-js";
import { client } from "../..";
import { useAppState } from "../../state/StateProvider";
import { ServerStatus, Timelength } from "../../types";
import { readableStorageAmount } from "../../util/helpers";
import Icon from "../shared/Icon";
import Flex from "../shared/layout/Flex";
import Grid from "../shared/layout/Grid";
import Selector from "../shared/menu/Selector";
import CurrentStats from "./CurrentStats";
import HistoricalStats from "./HistoricalStats";
import { StatsProvider, useStatsState } from "./Provider";
import { StatsProvider, useStatsState, StatsView } from "./Provider";
const TIMELENGTHS = [
Timelength.FifteenSeconds,
@@ -38,115 +34,182 @@ const Stats = () => {
const StatsComp: Component<{}> = () => {
const { view } = useStatsState();
return (
<Grid
style={{
width: "100%",
"box-sizing": "border-box",
}}
>
<Flex justifyContent="space-between" style={{ width: "100%" }}>
<Header />
<SysInfo />
</Flex>
<Show when={view() === "historical"}>
<Grid class="full-width">
<Header />
<Show when={view() === StatsView.Historical}>
<Flex alignItems="center" style={{ "place-self": "center" }}>
<PageManager />
</Flex>
</Show>
<Switch>
<Match when={view() === "current"}>
<Match when={view() === StatsView.Current}>
<CurrentStats />
</Match>
<Match when={view() === "historical"}>
<Match when={view() === StatsView.Historical}>
<HistoricalStats />
</Match>
<Match when={view() === StatsView.Info}>
<SysInfo />
</Match>
</Switch>
</Grid>
);
};
export const Header: Component<{}> = (p) => {
const { servers } = useAppState();
const { servers, serverInfo } = useAppState();
const params = useParams();
const server = () => servers.get(params.id);
const { view, setView, timelength, setTimelength, setPage, pollRate, setPollRate } = useStatsState();
const {
view,
setView,
timelength,
setTimelength,
setPage,
pollRate,
setPollRate,
} = useStatsState();
const sysInfo = () => serverInfo.get(params.id);
return (
<Flex alignItems="center" style={{ height: "fit-content" }}>
<h1>{server()?.server.name}</h1>
<A
href={`/server/${params.id}`}
class={
server()?.server.enabled
? server()?.status === ServerStatus.Ok
? "green"
: "red"
: "blue"
}
style={{
"border-radius": ".35rem",
transition: "background-color 125ms ease-in-out",
}}
onClick={(e) => {
e.stopPropagation();
}}
>
{server()?.status.replaceAll("_", " ").toUpperCase()}
</A>
<Grid gap="0" gridTemplateColumns="repeat(2, 1fr)">
<button
class={view() === "current" ? "selected" : "grey"}
style={{ width: "100%" }}
onClick={() => setView("current")}
>
current
</button>
<button
class={view() === "historical" ? "selected" : "grey"}
style={{ width: "100%" }}
onClick={() => setView("historical")}
>
historical
</button>
</Grid>
<Show when={view() === "historical"}>
<Selector
targetClass="grey"
selected={timelength()}
items={TIMELENGTHS}
onSelect={(selected) => {
setPage(0);
setTimelength(selected as Timelength);
<Flex alignItems="center" justifyContent="space-between">
<Flex alignItems="center" style={{ height: "fit-content" }}>
<h1>{server()?.server.name}</h1>
<A
href={`/server/${params.id}`}
class={
server()?.server.enabled
? server()?.status === ServerStatus.Ok
? "green"
: "red"
: "blue"
}
style={{
"border-radius": ".35rem",
transition: "background-color 125ms ease-in-out",
}}
onClick={(e) => {
e.stopPropagation();
}}
>
{server()?.status.replaceAll("_", " ").toUpperCase()}
</A>
<Selector
targetClass="blue"
selected={view()}
items={Object.values(StatsView)}
onSelect={(v) => setView(v as StatsView)}
position="bottom right"
/>
</Show>
<Show when={view() === "current"}>
<Flex gap="0.5rem" alignItems="center">
<div>poll:</div>
<Show when={view() === "historical"}>
<Selector
targetClass="grey"
selected={timelength()}
items={TIMELENGTHS}
itemMap={(t) => t.replaceAll("-", " ")}
itemClass="full-width"
onSelect={(selected) => {
setPage(0);
setTimelength(selected as Timelength);
}}
position="bottom right"
/>
</Show>
<Show when={view() === "current"}>
<Selector
targetClass="grey"
label="poll: "
selected={pollRate()}
items={[Timelength.OneSecond, Timelength.FiveSeconds]}
onSelect={(selected) => {
setPollRate(selected as Timelength);
}}
position="bottom right"
/>
</Flex>
</Show>
</Show>
</Flex>
<Flex>
<div>{sysInfo()?.cpu_brand}</div>
<div>
{sysInfo()?.core_count} core
{sysInfo()?.core_count && sysInfo()?.core_count! > 1 ? "s" : ""}
</div>
</Flex>
</Flex>
);
};
const SysInfo = () => {
const { sysInfo } = useStatsState();
const { serverInfo } = useAppState();
const params = useParams();
const sysInfo = () => serverInfo.get(params.id);
const [stats] = createResource(() =>
client.get_server_stats(params.id, { disks: true })
);
const os_cards = () => {
return [
{
label: "os",
info: sysInfo()?.os,
},
{
label: "kernel",
info: sysInfo()?.kernel,
},
].filter((i) => i.info) as Array<{ label: string; info: string }>;
};
const cpu_cards = () => {
return [
{
label: "cpu",
info: sysInfo()?.cpu_brand,
},
{
label: "core count",
info: `${sysInfo()?.core_count} cores`,
},
].filter((i) => i.info) as Array<{ label: string; info: string }>;
};
const stats_cards = () => {
return [
{
label: "mem",
info:
stats()?.mem_total_gb &&
readableStorageAmount(stats()?.mem_total_gb!),
},
{
label: "disk",
info:
stats()?.disk.total_gb &&
readableStorageAmount(stats()?.disk.total_gb!),
},
].filter((i) => i.info) as Array<{ label: string; info: string }>;
};
return (
<Flex
alignItems="center"
style={{ "place-self": "center end", width: "fit-content" }}
>
<div>{sysInfo()?.os}</div>
{/* <div>{sysInfo()?.kernel}</div> */}
<div>{sysInfo()?.cpu_brand}</div>
<div>{sysInfo()?.core_count} cores</div>
<Grid class="full-width" placeItems="center">
<Show when={sysInfo()?.host_name}>
<Grid class="card full-width" style={{ "max-width": "700px" }}>
<InfoCard info={{ label: "hostname", info: sysInfo()?.host_name! }} />
</Grid>
</Show>
<Grid class="card full-width" style={{ "max-width": "700px" }}>
<For each={os_cards()}>{(i) => <InfoCard info={i} />}</For>
</Grid>
<Grid class="card full-width" style={{ "max-width": "700px" }}>
<For each={cpu_cards()}>{(i) => <InfoCard info={i} />}</For>
</Grid>
<Grid class="card full-width" style={{ "max-width": "700px" }}>
<For each={stats_cards()}>{(i) => <InfoCard info={i} />}</For>
</Grid>
</Grid>
);
};
const InfoCard: Component<{ info: { label: string; info: string } }> = (p) => {
return (
<Flex class="full-width" justifyContent="space-between">
<h2>{p.info.label}</h2>
<div>{p.info.info}</div>
</Flex>
);
};

View File

@@ -58,7 +58,8 @@ export const Search: Component<{}> = (p) => {
>
<Input
ref={inputRef}
class={s.SearchInput}
class="lightgrey"
style={{ width: "30rem" }}
placeholder="search"
value={search.value()}
onEdit={input.onEdit}

View File

@@ -11,6 +11,7 @@ import { UserProvider } from "./state/UserProvider";
import { Client } from "./util/client";
import { Router } from "@solidjs/router";
import { AppStateProvider } from "./state/StateProvider";
import { Operation } from "./types";
export const TOPBAR_HEIGHT = 50;
export const MAX_PAGE_WIDTH = 1200;
@@ -29,6 +30,10 @@ const token =
export const client = new Client(MONITOR_BASE_URL, token);
export const OPERATIONS = Object.values(Operation)
.filter((e) => e !== "none" && !e.includes("user"))
.map((e) => e.replaceAll("_", " "));
export const { Notifications, pushNotification } = makeNotifications();
client.initialize().then(() => {

View File

@@ -17,7 +17,7 @@ import {
} from "./hooks";
import connectToWs from "./ws";
import { useUser } from "./UserProvider";
import { AwsBuilderConfig, PermissionLevel } from "../types";
import { AwsBuilderConfig, PermissionLevel, UpdateTarget } from "../types";
import { client } from "..";
export type State = {
@@ -42,6 +42,7 @@ export type State = {
aws_builder_config: Resource<AwsBuilderConfig>;
docker_organizations: Resource<string[]>;
github_webhook_base_url: Resource<string>;
name_from_update_target: (target: UpdateTarget) => string;
};
const context = createContext<
@@ -148,6 +149,17 @@ export const AppStateProvider: ParentComponent = (p) => {
aws_builder_config,
docker_organizations,
github_webhook_base_url,
name_from_update_target: (target) => {
if (target.type === "Deployment" && deployments) {
return deployments.get(target.id!)?.deployment.name || "deleted";
} else if (target.type === "Server" && servers) {
return servers.get(target.id)?.server.name || "deleted";
} else if (target.type === "Build" && builds) {
return builds.get(target.id)?.name || "deleted";
} else {
return "admin";
}
}
};
// createEffect(() => {

View File

@@ -288,7 +288,7 @@ export function useUpdates(target?: UpdateTarget, show_builds?: boolean) {
operations
);
updates.addManyToEnd(newUpdates);
if (newUpdates.length !== 10) {
if (newUpdates.length !== 20) {
setNoMore(true);
}
}

View File

@@ -318,6 +318,22 @@ svg {
opacity: 0.7;
}
.full-size {
width: 100%;
height: 100%;
box-sizing: border-box;
}
.full-width {
width: 100%;
box-sizing: border-box;
}
.full-height {
height: 100%;
box-sizing: border-box;
}
// .hoverable {
// transition: all 250ms ease-in-out;
// }

View File

@@ -56,6 +56,8 @@ export interface DockerBuildArgs {
build_path: string;
dockerfile_path?: string;
build_args?: EnvironmentVar[];
extra_args?: string[];
use_buildx?: boolean;
}
export interface BuildVersionsReponse {

View File

@@ -1,8 +1,14 @@
import {
Build,
Deployment,
DeploymentWithContainerState,
DockerContainerState,
EnvironmentVar,
Server,
ServerStatus,
ServerWithStatus,
Timelength,
UpdateTarget,
User,
Version,
} from "../types";
@@ -238,10 +244,10 @@ export function readableVersion(version: Version) {
export function readableUserType(user: User) {
if (user.github_id) {
return "github"
return "github";
} else if (user.google_id) {
return "google"
return "google";
} else {
return "local"
return "local";
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "db_client"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,6 +1,6 @@
[package]
name = "monitor_helpers"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
authors = ["MoghTech"]
description = "helpers used as dependency for mogh tech monitor"

View File

@@ -1,113 +1,10 @@
use std::{borrow::Borrow, fs::File, io::Read, net::SocketAddr, str::FromStr};
use std::{borrow::Borrow, net::SocketAddr, str::FromStr};
use anyhow::{anyhow, Context};
use anyhow::anyhow;
use axum::http::StatusCode;
use rand::{distributions::Alphanumeric, Rng};
use serde::de::DeserializeOwned;
use serde_json::{Map, Value};
use types::Log;
pub fn parse_config_files<'a, T: DeserializeOwned>(
paths: impl IntoIterator<Item = impl Borrow<String>>,
merge_nested: bool,
extend_array: bool,
) -> anyhow::Result<T> {
let mut target = Map::new();
for path in paths {
target = merge_objects(
target,
parse_config_file(path.borrow())?,
merge_nested,
extend_array,
)?;
}
serde_json::from_str(&serde_json::to_string(&target)?)
.context("failed to parse final config into expected type")
}
pub fn parse_config_file<T: DeserializeOwned>(path: &str) -> anyhow::Result<T> {
let mut file = File::open(&path).expect(&format!("failed to find config at {path}"));
let config = if path.ends_with("toml") {
let mut contents = String::new();
file.read_to_string(&mut contents)
.context(format!("failed to read toml at {path}"))?;
toml::from_str(&contents).context(format!("failed to parse toml at {path}"))?
} else if path.ends_with("json") {
serde_json::from_reader(file).context(format!("failed to parse json at {path}"))?
} else {
panic!("unsupported config file type: {}", path)
};
Ok(config)
}
/// object is serde_json::Map<String, serde_json::Value>
/// source will overide target
/// will recurse when field is object if merge_object = true, otherwise object will be replaced
/// will extend when field is array if extend_array = true, otherwise array will be replaced
/// will return error when types on source and target fields do not match
fn merge_objects(
mut target: Map<String, Value>,
source: Map<String, Value>,
merge_nested: bool,
extend_array: bool,
) -> anyhow::Result<Map<String, Value>> {
for (key, value) in source {
let curr = target.remove(&key);
if curr.is_none() {
target.insert(key, value);
continue;
}
let curr = curr.unwrap();
match curr {
Value::Object(target_obj) => {
if !merge_nested {
target.insert(key, value);
continue;
}
match value {
Value::Object(source_obj) => {
target.insert(
key,
Value::Object(merge_objects(
target_obj,
source_obj,
merge_nested,
extend_array,
)?),
);
}
_ => {
return Err(anyhow!(
"types on field {key} do not match. got {value:?}, expected object"
))
}
}
}
Value::Array(mut target_arr) => {
if !extend_array {
target.insert(key, value);
continue;
}
match value {
Value::Array(source_arr) => {
target_arr.extend(source_arr);
target.insert(key, Value::Array(target_arr));
}
_ => {
return Err(anyhow!(
"types on field {key} do not match. got {value:?}, expected array"
))
}
}
}
_ => {
target.insert(key, value);
}
}
}
Ok(target)
}
pub fn parse_comma_seperated_list<T: FromStr>(
comma_sep_list: impl Borrow<str>,
) -> anyhow::Result<Vec<T>> {

View File

@@ -1,6 +1,6 @@
[package]
name = "monitor_client"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
authors = ["MoghTech"]
description = "a client to interact with the monitor system"
@@ -9,8 +9,7 @@ license = "GPL-3.0-or-later"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
monitor_types = "0.2.10"
# monitor_types = { path = "../types" }
monitor_types = "0.2.14"
reqwest = { version = "0.11", features = ["json"] }
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
tokio = { version = "1.25", features = ["full"] }

View File

@@ -1,6 +1,6 @@
[package]
name = "periphery_client"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,6 +1,6 @@
[package]
name = "monitor_types"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
authors = ["MoghTech"]
description = "types for the mogh tech monitor"

View File

@@ -155,6 +155,12 @@ pub struct DockerBuildArgs {
#[serde(default)]
#[builder(default)]
pub build_args: Vec<EnvironmentVar>,
#[serde(default)]
#[builder(default)]
pub extra_args: Vec<String>,
#[serde(default)]
#[builder(default)]
pub use_buildx: bool,
}
#[typeshare]

View File

@@ -1,6 +1,6 @@
[package]
name = "monitor_periphery"
version = "0.2.10"
version = "0.2.14"
edition = "2021"
authors = ["MoghTech"]
description = "monitor periphery binary"
@@ -31,5 +31,6 @@ envy = "0.4"
sysinfo = "0.28"
toml = "0.7"
daemonize = "0.5.0"
clap = { version = "4.1", features = ["derive"] }
clap = { version = "4.2", features = ["derive"] }
svi = "0.1.3"
merge_config_files = "0.1.3"

View File

@@ -3,7 +3,8 @@ use std::sync::Arc;
use axum::Extension;
use clap::Parser;
use dotenv::dotenv;
use helpers::{parse_comma_seperated_list, parse_config_files};
use helpers::parse_comma_seperated_list;
use merge_config_files::parse_config_paths;
use serde::Deserialize;
use types::PeripheryConfig;
@@ -25,14 +26,18 @@ pub struct Args {
#[arg(long, default_value = "~/.monitor/periphery.log.err")]
pub stderr: String,
/// Sets the path of a config file to use. can use multiple times
/// Sets the path of a config file or directory to use. can use multiple times
#[arg(short, long)]
pub config_path: Option<Vec<String>>,
/// Sets the keywords to match directory periphery config file names on. can use multiple times. default "periphery" and "config"
#[arg(long)]
pub config_keyword: Option<Vec<String>>,
#[arg(short, long)]
pub merge_nested_config: bool,
#[arg(short, long)]
#[arg(long)]
pub home_dir: Option<String>,
#[arg(short, long)]
@@ -43,6 +48,8 @@ pub struct Args {
struct Env {
#[serde(default = "default_config_path")]
config_paths: String,
#[serde(default)]
config_keywords: String,
}
pub fn load() -> (Args, u16, PeripheryConfigExtension, HomeDirExtension) {
@@ -63,9 +70,18 @@ pub fn load() -> (Args, u16, PeripheryConfigExtension, HomeDirExtension) {
)
.into_iter()
.map(|p| p.replace("~", &home_dir))
.collect();
let config = parse_config_files::<PeripheryConfig>(
&config_paths,
.collect::<Vec<_>>();
let env_match_keywords = parse_comma_seperated_list(env.config_keywords)
.expect("failed to parse environemt CONFIG_KEYWORDS into comma seperated list");
let match_keywords = args
.config_keyword
.as_ref()
.unwrap_or(&env_match_keywords)
.into_iter()
.map(|kw| kw.as_str());
let config = parse_config_paths::<PeripheryConfig>(
config_paths.clone(),
match_keywords,
args.merge_nested_config,
args.merge_nested_config,
)
@@ -117,7 +133,7 @@ fn default_config_path() -> String {
fn get_home_dir(home_dir_arg: &Option<String>) -> String {
match home_dir_arg {
Some(home_dir) => home_dir.to_string(),
None => std::env::var("$HOME")
None => std::env::var("HOME")
.expect("did not find $HOME env var, should pass home dir with arg --home-dir"),
}
}

View File

@@ -6,7 +6,7 @@ use types::{Build, DockerBuildArgs, EnvironmentVar, Log, Version};
use crate::helpers::run_monitor_command;
use super::docker_login;
use super::{docker_login, parse_extra_args};
pub async fn prune_images() -> Log {
let command = format!("docker image prune -a -f");
@@ -32,6 +32,8 @@ pub async fn build(
build_path,
dockerfile_path,
build_args,
extra_args,
use_buildx,
} = docker_build_args
.as_ref()
.ok_or(anyhow!("build missing docker build args"))?;
@@ -46,6 +48,8 @@ pub async fn build(
None => "Dockerfile".to_owned(),
};
let build_args = parse_build_args(build_args);
let extra_args = parse_extra_args(extra_args);
let buildx = if *use_buildx { " buildx" } else { "" };
let image_name = get_image_name(&name, docker_account, docker_organization);
let image_tags = image_tags(&image_name, &version);
let docker_push = if using_account {
@@ -54,7 +58,7 @@ pub async fn build(
String::new()
};
let command = format!(
"cd {} && docker build {build_args}{image_tags} -f {dockerfile_path} .{docker_push}",
"cd {} && docker{buildx} build{build_args}{extra_args}{image_tags} -f {dockerfile_path} .{docker_push}",
build_dir.display()
);
if *skip_secret_interp {
@@ -97,7 +101,7 @@ fn get_latest_image_name(image_name: &str) -> String {
fn image_tags(image_name: &str, version: &Version) -> String {
format!(
"-t {} -t {}",
" -t {} -t {}",
get_version_image_name(image_name, version),
get_latest_image_name(image_name)
)

View File

@@ -7,7 +7,7 @@ use types::{
Conversion, Deployment, DockerContainerStats, DockerRunArgs, EnvironmentVar, Log, RestartMode,
};
use crate::helpers::run_monitor_command;
use crate::helpers::{docker::parse_extra_args, run_monitor_command};
use super::docker_login;
@@ -197,12 +197,3 @@ fn parse_post_image(post_image: &Option<String>) -> String {
String::new()
}
}
fn parse_extra_args(extra_args: &Vec<String>) -> String {
let args = extra_args.join(" ");
if args.len() > 0 {
format!(" {args}")
} else {
args
}
}

View File

@@ -38,3 +38,12 @@ pub async fn docker_login(
Ok(false)
}
}
fn parse_extra_args(extra_args: &Vec<String>) -> String {
let args = extra_args.join(" ");
if args.len() > 0 {
format!(" {args}")
} else {
args
}
}

View File

@@ -108,8 +108,7 @@ pub async fn test_build(monitor: &MonitorClient) -> anyhow::Result<Update> {
});
build.docker_build_args = Some(DockerBuildArgs {
build_path: "periphery".to_string(),
dockerfile_path: None,
build_args: Vec::new(),
..Default::default()
});
let build = monitor.update_build(build).await?;
println!("updated build.");