forked from github-starred/komodo
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a07624e9b9 | ||
|
|
bb8054af8a | ||
|
|
7738f3e066 | ||
|
|
5dee16a100 | ||
|
|
35f3bcdf2f | ||
|
|
130ca8e1f1 | ||
|
|
ced4c21688 | ||
|
|
6ec7078024 | ||
|
|
b28d8f2506 | ||
|
|
c88a9291a0 | ||
|
|
1e82d19306 | ||
|
|
dd87e50cb2 | ||
|
|
4c8f96a30f | ||
|
|
c4f45e05f1 | ||
|
|
6aa382c7c1 | ||
|
|
ccb9f059e6 | ||
|
|
1cdcea0771 | ||
|
|
88dda0de80 | ||
|
|
30ed99e2b0 | ||
|
|
e5953b7541 | ||
|
|
1f9d01c59f | ||
|
|
cc5210a3d8 | ||
|
|
26559e2d3b | ||
|
|
7eeddb300f | ||
|
|
1e01bae16b | ||
|
|
87c03924e5 | ||
|
|
f0998b1d43 | ||
|
|
1995a04244 | ||
|
|
420fe6bcd5 | ||
|
|
d4e26c0553 | ||
|
|
5f5e7cb45e | ||
|
|
8aa0304738 | ||
|
|
8ec98c33a4 | ||
|
|
2667182ca3 |
164
Cargo.lock
generated
164
Cargo.lock
generated
@@ -804,7 +804,7 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core"
|
name = "core"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async_timing_util",
|
"async_timing_util",
|
||||||
@@ -824,7 +824,7 @@ dependencies = [
|
|||||||
"jwt",
|
"jwt",
|
||||||
"merge_config_files",
|
"merge_config_files",
|
||||||
"monitor_helpers",
|
"monitor_helpers",
|
||||||
"monitor_types 0.3.0",
|
"monitor_types 0.3.4",
|
||||||
"mungos",
|
"mungos",
|
||||||
"periphery_client",
|
"periphery_client",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -1058,10 +1058,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "db_client"
|
name = "db_client"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"monitor_types 0.3.0",
|
"monitor_types 0.3.4",
|
||||||
"mungos",
|
"mungos",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1934,14 +1934,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_cli"
|
name = "monitor_cli"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async_timing_util",
|
"async_timing_util",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"monitor_types 0.3.0",
|
"monitor_types 0.3.4",
|
||||||
"rand",
|
"rand",
|
||||||
"run_command",
|
"run_command 0.0.5",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"strum",
|
"strum",
|
||||||
@@ -1951,12 +1951,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_client"
|
name = "monitor_client"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"envy",
|
"envy",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"monitor_types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"monitor_types 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -1968,11 +1968,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_helpers"
|
name = "monitor_helpers"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
"monitor_types 0.3.0",
|
"monitor_types 0.3.4",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -1981,7 +1981,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_periphery"
|
name = "monitor_periphery"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async_timing_util",
|
"async_timing_util",
|
||||||
@@ -1994,8 +1994,9 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"merge_config_files",
|
"merge_config_files",
|
||||||
"monitor_helpers",
|
"monitor_helpers",
|
||||||
"monitor_types 0.3.0",
|
"monitor_types 0.3.4",
|
||||||
"run_command",
|
"parse_csl",
|
||||||
|
"run_command 0.0.6",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2008,7 +2009,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_types"
|
name = "monitor_types"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bollard",
|
"bollard",
|
||||||
@@ -2025,9 +2026,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "monitor_types"
|
name = "monitor_types"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bba1f3fb118a665daf75e64267e24630825319903fc6967c3939eeb6a2f2baa"
|
checksum = "24ce7541ee0fad1452ae11d0db73285cfbb9df79ad3e49c0680d0bc27c64db24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bollard",
|
"bollard",
|
||||||
@@ -2251,6 +2252,12 @@ dependencies = [
|
|||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parse_csl"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffa94c2e5674923c67d7f3dfce1279507b191e10eb064881b46ed3e1256e5ca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbkdf2"
|
name = "pbkdf2"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -2268,11 +2275,11 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "periphery_client"
|
name = "periphery_client"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"monitor_types 0.3.0",
|
"monitor_types 0.3.4",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2493,6 +2500,12 @@ name = "run_command"
|
|||||||
version = "0.0.5"
|
version = "0.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb00d9c9c67906454ba83e62aaa52368814512ac1c5b65b8791bb313a9257b0e"
|
checksum = "cb00d9c9c67906454ba83e62aaa52368814512ac1c5b65b8791bb313a9257b0e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "run_command"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388e1106aa4bd809ba57afecb8b3efc60b6599cd06a774d5798a7c5a29675307"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@@ -2858,9 +2871,9 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.7"
|
version = "0.4.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"winapi",
|
"winapi",
|
||||||
@@ -3083,14 +3096,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.26.0"
|
version = "1.28.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
|
checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
@@ -3098,18 +3110,18 @@ dependencies = [
|
|||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "1.8.2"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
|
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3685,13 +3697,13 @@ version = "0.42.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm 0.42.1",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.42.1",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.42.1",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.42.1",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.42.1",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm 0.42.1",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.42.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3700,7 +3712,16 @@ version = "0.45.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets 0.42.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3709,13 +3730,28 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm 0.42.1",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.42.1",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.42.1",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.42.1",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.42.1",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm 0.42.1",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.42.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.0",
|
||||||
|
"windows_aarch64_msvc 0.48.0",
|
||||||
|
"windows_i686_gnu 0.48.0",
|
||||||
|
"windows_i686_msvc 0.48.0",
|
||||||
|
"windows_x86_64_gnu 0.48.0",
|
||||||
|
"windows_x86_64_gnullvm 0.48.0",
|
||||||
|
"windows_x86_64_msvc 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3724,42 +3760,84 @@ version = "0.42.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.42.1"
|
version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_cli"
|
name = "monitor_cli"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "monitor cli | tools to setup monitor system"
|
description = "monitor cli | tools to setup monitor system"
|
||||||
|
|||||||
@@ -10,19 +10,16 @@ port = 9000
|
|||||||
# daily utc offset in hours to send daily update. eg 8:00 eastern time is 13:00 UTC, so offset should be 13. default of 0 runs at UTC midnight.
|
# daily utc offset in hours to send daily update. eg 8:00 eastern time is 13:00 UTC, so offset should be 13. default of 0 runs at UTC midnight.
|
||||||
daily_offset_hours = 13
|
daily_offset_hours = 13
|
||||||
|
|
||||||
# number of days to keep stats around, or 0 to disable pruning. stats older than this number of days are deleted daily
|
|
||||||
keep_stats_for_days = 120
|
|
||||||
|
|
||||||
# secret used to generate the jwt. should be some randomly generated hash.
|
# secret used to generate the jwt. should be some randomly generated hash.
|
||||||
jwt_secret = "your_jwt_secret"
|
jwt_secret = "your_jwt_secret"
|
||||||
|
|
||||||
# can be 1-hr, 12-hr, 1-day, 3-day, 1-wk, 2-wk, 30-day
|
# can be 1-hr, 12-hr, 1-day, 3-day, 1-wk, 2-wk, 30-day
|
||||||
jwt_valid_for = "1-wk"
|
jwt_valid_for = "1-wk"
|
||||||
|
|
||||||
# webhook url given by slack app
|
# webhook url given by slack app that monitor will send alerts and a daily update to
|
||||||
slack_url = "your_slack_app_webhook_url"
|
slack_url = "your_slack_app_webhook_url"
|
||||||
|
|
||||||
# token that has to be given to github during webhook config as the secret
|
# token that has to be given to github during repo webhook config as the secret
|
||||||
github_webhook_secret = "your_random_webhook_secret"
|
github_webhook_secret = "your_random_webhook_secret"
|
||||||
|
|
||||||
# optional. an alternate base url that is used to recieve github webhook requests. if not provided, will use 'host' address as base
|
# optional. an alternate base url that is used to recieve github webhook requests. if not provided, will use 'host' address as base
|
||||||
@@ -31,30 +28,20 @@ github_webhook_base_url = "https://monitor-github-webhook.mogh.tech"
|
|||||||
# token used to authenticate core requests to periphery
|
# token used to authenticate core requests to periphery
|
||||||
passkey = "your_random_passkey"
|
passkey = "your_random_passkey"
|
||||||
|
|
||||||
# can be 30-sec, 1-min, 2-min, 5-min
|
# controls the granularity of the system stats collection by monitor core
|
||||||
|
# can be 15-sec, 30-sec, 1-min, 2-min, 5-min
|
||||||
monitoring_interval = "1-min"
|
monitoring_interval = "1-min"
|
||||||
|
|
||||||
|
# number of days to keep stats around, or 0 to disable pruning. stats older than this number of days are deleted daily
|
||||||
|
keep_stats_for_days = 14
|
||||||
|
|
||||||
|
# these will be used by the GUI to attach to builds. New build docker orgs will default to first org (or none if empty).
|
||||||
|
# when attached to build, image will be pushed to repo under the specified organization
|
||||||
|
docker_organizations = ["your_docker_org1", "your_docker_org_2"]
|
||||||
|
|
||||||
# allow or deny user login with username / password
|
# allow or deny user login with username / password
|
||||||
local_auth = true
|
local_auth = true
|
||||||
|
|
||||||
# these will be given in the GUI to attach to builds. New build docker orgs will default to first org (or none if empty).
|
|
||||||
docker_organizations = ["your_docker_org1", "your_docker_org_2"]
|
|
||||||
|
|
||||||
[aws]
|
|
||||||
access_key_id = "your_aws_key_id"
|
|
||||||
secret_access_key = "your_aws_secret_key"
|
|
||||||
default_region = "us-east-1"
|
|
||||||
default_ami_id = "your_periphery_ami"
|
|
||||||
default_key_pair_name = "your_default_key_pair_name"
|
|
||||||
default_instance_type = "m5.2xlarge"
|
|
||||||
default_volume_gb = 8
|
|
||||||
default_subnet_id = "your_default_subnet_id"
|
|
||||||
default_security_group_ids = ["sg_id_1", "sg_id_2"]
|
|
||||||
default_assign_public_ip = false
|
|
||||||
|
|
||||||
[aws.available_ami_accounts]
|
|
||||||
your_periphery_ami = { name = "default ami", github = ["github_username"], docker = ["docker_username"] }
|
|
||||||
|
|
||||||
[github_oauth]
|
[github_oauth]
|
||||||
enabled = true
|
enabled = true
|
||||||
id = "your_github_client_id"
|
id = "your_github_client_id"
|
||||||
@@ -68,4 +55,19 @@ secret = "your_google_client_secret"
|
|||||||
[mongo]
|
[mongo]
|
||||||
uri = "your_mongo_uri"
|
uri = "your_mongo_uri"
|
||||||
app_name = "monitor_core"
|
app_name = "monitor_core"
|
||||||
db_name = "monitor"
|
db_name = "monitor" # this is the name of the mongo database that monitor will create its collections in.
|
||||||
|
|
||||||
|
[aws]
|
||||||
|
access_key_id = "your_aws_key_id"
|
||||||
|
secret_access_key = "your_aws_secret_key"
|
||||||
|
default_region = "us-east-1"
|
||||||
|
default_ami_name = "your_ami_name" # must be defined below in [aws.available_ami_accounts]
|
||||||
|
default_instance_type = "m5.2xlarge"
|
||||||
|
default_volume_gb = 8
|
||||||
|
default_subnet_id = "your_default_subnet_id"
|
||||||
|
default_security_group_ids = ["sg_id_1", "sg_id_2"]
|
||||||
|
default_key_pair_name = "your_default_key_pair_name"
|
||||||
|
default_assign_public_ip = false
|
||||||
|
|
||||||
|
[aws.available_ami_accounts]
|
||||||
|
your_ami_name = { ami_id = "ami-1234567890", github = ["github_username"], docker = ["docker_username"] }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "core"
|
name = "core"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ use std::time::Duration;
|
|||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use aws_sdk_ec2::Client;
|
use aws_sdk_ec2::Client;
|
||||||
use diff::Diff;
|
use diff::Diff;
|
||||||
|
use futures_util::future::join_all;
|
||||||
use helpers::{all_logs_success, to_monitor_name};
|
use helpers::{all_logs_success, to_monitor_name};
|
||||||
use mungos::{doc, to_bson};
|
use mungos::{doc, to_bson};
|
||||||
use types::{
|
use types::{
|
||||||
monitor_timestamp,
|
monitor_timestamp,
|
||||||
traits::{Busy, Permissioned},
|
traits::{Busy, Permissioned},
|
||||||
AwsBuilderBuildConfig, Build, Log, Operation, PermissionLevel, Update, UpdateStatus,
|
AwsBuilderBuildConfig, Build, DockerContainerState, Log, Operation, PermissionLevel, Update,
|
||||||
UpdateTarget, Version,
|
UpdateStatus, UpdateTarget, Version,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -415,6 +416,8 @@ impl State {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.handle_post_build_redeploy(build_id, &mut update).await;
|
||||||
|
|
||||||
update.success = all_logs_success(&update.logs);
|
update.success = all_logs_success(&update.logs);
|
||||||
update.status = UpdateStatus::Complete;
|
update.status = UpdateStatus::Complete;
|
||||||
update.end_ts = Some(monitor_timestamp());
|
update.end_ts = Some(monitor_timestamp());
|
||||||
@@ -424,6 +427,85 @@ impl State {
|
|||||||
Ok(update)
|
Ok(update)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_post_build_redeploy(&self, build_id: &str, update: &mut Update) {
|
||||||
|
let redeploy_deployments = self
|
||||||
|
.db
|
||||||
|
.deployments
|
||||||
|
.get_some(
|
||||||
|
doc! { "build_id": build_id, "redeploy_on_build": true },
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
if let Ok(deployments) = redeploy_deployments {
|
||||||
|
let futures = deployments.into_iter().map(|d| async move {
|
||||||
|
let request_user = RequestUser {
|
||||||
|
id: "auto redeploy".to_string(),
|
||||||
|
is_admin: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let state = self
|
||||||
|
.get_deployment_with_container_state(&request_user, &d.id)
|
||||||
|
.await
|
||||||
|
.map(|r| r.state)
|
||||||
|
.unwrap_or_default();
|
||||||
|
if state == DockerContainerState::Running {
|
||||||
|
Some((
|
||||||
|
d.id.clone(),
|
||||||
|
self.deploy_container(
|
||||||
|
&d.id,
|
||||||
|
&RequestUser {
|
||||||
|
id: "auto redeploy".to_string(),
|
||||||
|
is_admin: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let redeploy_results = join_all(futures).await;
|
||||||
|
|
||||||
|
let mut redeploys = Vec::<String>::new();
|
||||||
|
let mut redeploy_failures = Vec::<String>::new();
|
||||||
|
|
||||||
|
for res in redeploy_results {
|
||||||
|
if res.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (id, res) = res.unwrap();
|
||||||
|
match res {
|
||||||
|
Ok(_) => redeploys.push(id),
|
||||||
|
Err(e) => redeploy_failures.push(format!("{id}: {e:#?}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if redeploys.len() > 0 {
|
||||||
|
update.logs.push(Log::simple(
|
||||||
|
"redeploy",
|
||||||
|
format!("redeployed deployments: {}", redeploys.join(", ")),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
if redeploy_failures.len() > 0 {
|
||||||
|
update.logs.push(Log::simple(
|
||||||
|
"redeploy failures",
|
||||||
|
redeploy_failures.join("\n"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else if let Err(e) = redeploy_deployments {
|
||||||
|
update.logs.push(Log::simple(
|
||||||
|
"redeploys failed",
|
||||||
|
format!("failed to get deployments to redeploy: {e:#?}"),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_ec2_instance_for_build(
|
async fn create_ec2_instance_for_build(
|
||||||
&self,
|
&self,
|
||||||
build: &Build,
|
build: &Build,
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ use types::{
|
|||||||
monitor_timestamp,
|
monitor_timestamp,
|
||||||
traits::{Busy, Permissioned},
|
traits::{Busy, Permissioned},
|
||||||
Deployment, DeploymentWithContainerState, DockerContainerState, Log, Operation,
|
Deployment, DeploymentWithContainerState, DockerContainerState, Log, Operation,
|
||||||
PermissionLevel, ServerStatus, ServerWithStatus, Update, UpdateStatus, UpdateTarget,
|
PermissionLevel, ServerStatus, ServerWithStatus, TerminationSignal, Update, UpdateStatus,
|
||||||
|
UpdateTarget,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -112,6 +113,8 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Deployment> {
|
) -> anyhow::Result<Deployment> {
|
||||||
if self.deployment_busy(deployment_id).await {
|
if self.deployment_busy(deployment_id).await {
|
||||||
return Err(anyhow!("deployment busy"));
|
return Err(anyhow!("deployment busy"));
|
||||||
@@ -123,7 +126,7 @@ impl State {
|
|||||||
let server = self.db.get_server(&deployment.server_id).await?;
|
let server = self.db.get_server(&deployment.server_id).await?;
|
||||||
let log = match self
|
let log = match self
|
||||||
.periphery
|
.periphery
|
||||||
.container_remove(&server, &deployment.name)
|
.container_remove(&server, &deployment.name, stop_signal, stop_time)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(log) => log,
|
Ok(log) => log,
|
||||||
@@ -367,7 +370,9 @@ impl State {
|
|||||||
self.update_update(update).await?;
|
self.update_update(update).await?;
|
||||||
return Err(deployment_state.err().unwrap());
|
return Err(deployment_state.err().unwrap());
|
||||||
}
|
}
|
||||||
let DeploymentWithContainerState { state, .. } = deployment_state.unwrap();
|
let DeploymentWithContainerState {
|
||||||
|
deployment, state, ..
|
||||||
|
} = deployment_state.unwrap();
|
||||||
if state != DockerContainerState::NotDeployed {
|
if state != DockerContainerState::NotDeployed {
|
||||||
let log = self
|
let log = self
|
||||||
.periphery
|
.periphery
|
||||||
@@ -406,7 +411,6 @@ impl State {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to update deployment name on mongo");
|
.context("failed to update deployment name on mongo");
|
||||||
|
|
||||||
if let Err(e) = res {
|
if let Err(e) = res {
|
||||||
update
|
update
|
||||||
.logs
|
.logs
|
||||||
@@ -418,6 +422,20 @@ impl State {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if deployment.repo.is_some() {
|
||||||
|
let res = self.reclone_deployment(deployment_id, user, false).await;
|
||||||
|
if let Err(e) = res {
|
||||||
|
update
|
||||||
|
.logs
|
||||||
|
.push(Log::error("reclone repo", format!("{e:?}")));
|
||||||
|
} else {
|
||||||
|
update.logs.push(Log::simple(
|
||||||
|
"reclone repo",
|
||||||
|
"deployment repo cloned with new name".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update.end_ts = monitor_timestamp().into();
|
update.end_ts = monitor_timestamp().into();
|
||||||
update.status = UpdateStatus::Complete;
|
update.status = UpdateStatus::Complete;
|
||||||
update.success = all_logs_success(&update.logs);
|
update.success = all_logs_success(&update.logs);
|
||||||
@@ -431,8 +449,9 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
check_deployment_busy: bool,
|
||||||
) -> anyhow::Result<Update> {
|
) -> anyhow::Result<Update> {
|
||||||
if self.deployment_busy(deployment_id).await {
|
if check_deployment_busy && self.deployment_busy(deployment_id).await {
|
||||||
return Err(anyhow!("deployment busy"));
|
return Err(anyhow!("deployment busy"));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -494,6 +513,8 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Update> {
|
) -> anyhow::Result<Update> {
|
||||||
if self.deployment_busy(deployment_id).await {
|
if self.deployment_busy(deployment_id).await {
|
||||||
return Err(anyhow!("deployment busy"));
|
return Err(anyhow!("deployment busy"));
|
||||||
@@ -503,7 +524,9 @@ impl State {
|
|||||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||||
entry.deploying = true;
|
entry.deploying = true;
|
||||||
}
|
}
|
||||||
let res = self.deploy_container_inner(deployment_id, user).await;
|
let res = self
|
||||||
|
.deploy_container_inner(deployment_id, user, stop_signal, stop_time)
|
||||||
|
.await;
|
||||||
{
|
{
|
||||||
let mut lock = self.deployment_action_states.lock().await;
|
let mut lock = self.deployment_action_states.lock().await;
|
||||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||||
@@ -516,6 +539,8 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Update> {
|
) -> anyhow::Result<Update> {
|
||||||
let start_ts = monitor_timestamp();
|
let start_ts = monitor_timestamp();
|
||||||
let mut deployment = self
|
let mut deployment = self
|
||||||
@@ -553,7 +578,14 @@ impl State {
|
|||||||
|
|
||||||
update.id = self.add_update(update.clone()).await?;
|
update.id = self.add_update(update.clone()).await?;
|
||||||
|
|
||||||
let deploy_log = match self.periphery.deploy(&server, &deployment).await {
|
let stop_signal = stop_signal.unwrap_or(deployment.termination_signal).into();
|
||||||
|
let stop_time = stop_time.unwrap_or(deployment.termination_timeout).into();
|
||||||
|
|
||||||
|
let deploy_log = match self
|
||||||
|
.periphery
|
||||||
|
.deploy(&server, &deployment, stop_signal, stop_time)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(log) => log,
|
Ok(log) => log,
|
||||||
Err(e) => Log::error("deploy container", format!("{e:#?}")),
|
Err(e) => Log::error("deploy container", format!("{e:#?}")),
|
||||||
};
|
};
|
||||||
@@ -642,6 +674,8 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Update> {
|
) -> anyhow::Result<Update> {
|
||||||
if self.deployment_busy(deployment_id).await {
|
if self.deployment_busy(deployment_id).await {
|
||||||
return Err(anyhow!("deployment busy"));
|
return Err(anyhow!("deployment busy"));
|
||||||
@@ -651,7 +685,9 @@ impl State {
|
|||||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||||
entry.stopping = true;
|
entry.stopping = true;
|
||||||
}
|
}
|
||||||
let res = self.stop_container_inner(deployment_id, user).await;
|
let res = self
|
||||||
|
.stop_container_inner(deployment_id, user, stop_signal, stop_time)
|
||||||
|
.await;
|
||||||
{
|
{
|
||||||
let mut lock = self.deployment_action_states.lock().await;
|
let mut lock = self.deployment_action_states.lock().await;
|
||||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||||
@@ -664,6 +700,8 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Update> {
|
) -> anyhow::Result<Update> {
|
||||||
let start_ts = monitor_timestamp();
|
let start_ts = monitor_timestamp();
|
||||||
let deployment = self
|
let deployment = self
|
||||||
@@ -681,9 +719,12 @@ impl State {
|
|||||||
};
|
};
|
||||||
update.id = self.add_update(update.clone()).await?;
|
update.id = self.add_update(update.clone()).await?;
|
||||||
|
|
||||||
|
let stop_signal = stop_signal.unwrap_or(deployment.termination_signal).into();
|
||||||
|
let stop_time = stop_time.unwrap_or(deployment.termination_timeout).into();
|
||||||
|
|
||||||
let log = self
|
let log = self
|
||||||
.periphery
|
.periphery
|
||||||
.container_stop(&server, &deployment.name)
|
.container_stop(&server, &deployment.name, stop_signal, stop_time)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
update.success = match log {
|
update.success = match log {
|
||||||
@@ -712,6 +753,8 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Update> {
|
) -> anyhow::Result<Update> {
|
||||||
if self.deployment_busy(deployment_id).await {
|
if self.deployment_busy(deployment_id).await {
|
||||||
return Err(anyhow!("deployment busy"));
|
return Err(anyhow!("deployment busy"));
|
||||||
@@ -721,7 +764,9 @@ impl State {
|
|||||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||||
entry.removing = true;
|
entry.removing = true;
|
||||||
}
|
}
|
||||||
let res = self.remove_container_inner(deployment_id, user).await;
|
let res = self
|
||||||
|
.remove_container_inner(deployment_id, user, stop_signal, stop_time)
|
||||||
|
.await;
|
||||||
{
|
{
|
||||||
let mut lock = self.deployment_action_states.lock().await;
|
let mut lock = self.deployment_action_states.lock().await;
|
||||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||||
@@ -734,6 +779,8 @@ impl State {
|
|||||||
&self,
|
&self,
|
||||||
deployment_id: &str,
|
deployment_id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Update> {
|
) -> anyhow::Result<Update> {
|
||||||
let start_ts = monitor_timestamp();
|
let start_ts = monitor_timestamp();
|
||||||
let deployment = self
|
let deployment = self
|
||||||
@@ -751,9 +798,12 @@ impl State {
|
|||||||
};
|
};
|
||||||
update.id = self.add_update(update.clone()).await?;
|
update.id = self.add_update(update.clone()).await?;
|
||||||
|
|
||||||
|
let stop_signal = stop_signal.unwrap_or(deployment.termination_signal).into();
|
||||||
|
let stop_time = stop_time.unwrap_or(deployment.termination_timeout).into();
|
||||||
|
|
||||||
let log = self
|
let log = self
|
||||||
.periphery
|
.periphery
|
||||||
.container_remove(&server, &deployment.name)
|
.container_remove(&server, &deployment.name, stop_signal, stop_time)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
update.success = match log {
|
update.success = match log {
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
StopContainer => {
|
StopContainer => {
|
||||||
let update = self
|
let update = self
|
||||||
.stop_container(&target_id, user)
|
.stop_container(&target_id, user, Option::None, Option::None)
|
||||||
.await
|
.await
|
||||||
.context(format!(
|
.context(format!(
|
||||||
"failed at stop container for deployment (id: {target_id})"
|
"failed at stop container for deployment (id: {target_id})"
|
||||||
@@ -218,7 +218,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
RemoveContainer => {
|
RemoveContainer => {
|
||||||
let update = self
|
let update = self
|
||||||
.remove_container(&target_id, user)
|
.remove_container(&target_id, user, Option::None, Option::None)
|
||||||
.await
|
.await
|
||||||
.context(format!(
|
.context(format!(
|
||||||
"failed at remove container for deployment (id: {target_id})"
|
"failed at remove container for deployment (id: {target_id})"
|
||||||
@@ -227,7 +227,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
DeployContainer => {
|
DeployContainer => {
|
||||||
let update = self
|
let update = self
|
||||||
.deploy_container(&target_id, user)
|
.deploy_container(&target_id, user, Option::None, Option::None)
|
||||||
.await
|
.await
|
||||||
.context(format!(
|
.context(format!(
|
||||||
"failed at deploy container for deployment (id: {target_id})"
|
"failed at deploy container for deployment (id: {target_id})"
|
||||||
@@ -236,14 +236,18 @@ impl State {
|
|||||||
}
|
}
|
||||||
RecloneDeployment => {
|
RecloneDeployment => {
|
||||||
let update = self
|
let update = self
|
||||||
.reclone_deployment(&target_id, user)
|
.reclone_deployment(&target_id, user, true)
|
||||||
.await
|
.await
|
||||||
.context(format!("failed at reclone deployment (id: {target_id})"))?;
|
.context(format!("failed at reclone deployment (id: {target_id})"))?;
|
||||||
updates.push(update);
|
updates.push(update);
|
||||||
}
|
}
|
||||||
PullDeployment => {
|
PullDeployment => {
|
||||||
// implement this one
|
// implement this one
|
||||||
// let update = self.pull
|
let update = self
|
||||||
|
.pull_deployment_repo(&target_id, user)
|
||||||
|
.await
|
||||||
|
.context(format!("failed at pull deployment (id: {target_id})"))?;
|
||||||
|
updates.push(update);
|
||||||
}
|
}
|
||||||
// build
|
// build
|
||||||
BuildBuild => {
|
BuildBuild => {
|
||||||
|
|||||||
@@ -121,14 +121,14 @@ impl State {
|
|||||||
.get_some(doc! { "server_id": server_id }, None)
|
.get_some(doc! { "server_id": server_id }, None)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| async move { self.delete_deployment(&d.id, user).await });
|
.map(|d| async move { self.delete_deployment(&d.id, user, None, None).await });
|
||||||
let delete_builds = self
|
let delete_builds = self
|
||||||
.db
|
.db
|
||||||
.builds
|
.builds
|
||||||
.get_some(doc! { "server_id": server_id }, None)
|
.get_some(doc! { "server_id": server_id }, None)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| async move { self.delete_deployment(&d.id, user).await });
|
.map(|d| async move { self.delete_deployment(&d.id, user, None, None).await });
|
||||||
let update_groups = self
|
let update_groups = self
|
||||||
.db
|
.db
|
||||||
.groups
|
.groups
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
|
use std::{cmp::Ordering, collections::HashMap};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use async_timing_util::unix_timestamp_ms;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, Query},
|
extract::{Path, Query},
|
||||||
routing::{delete, get, patch, post},
|
routing::{delete, get, patch, post},
|
||||||
Extension, Json, Router,
|
Extension, Json, Router,
|
||||||
};
|
};
|
||||||
|
use futures_util::TryStreamExt;
|
||||||
use helpers::handle_anyhow_error;
|
use helpers::handle_anyhow_error;
|
||||||
use mungos::{doc, Deserialize, Document, FindOptions, Serialize};
|
use mungos::{doc, Deserialize, Document, FindOptions, Serialize};
|
||||||
use types::{
|
use types::{
|
||||||
traits::Permissioned, AwsBuilderConfig, Build, BuildActionState, BuildVersionsReponse,
|
monitor_ts_from_unix, traits::Permissioned, unix_from_monitor_ts, AwsBuilderConfig, Build,
|
||||||
Operation, PermissionLevel, UpdateStatus,
|
BuildActionState, BuildVersionsReponse, Operation, PermissionLevel, UpdateStatus,
|
||||||
};
|
};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
const NUM_VERSIONS_PER_PAGE: u64 = 10;
|
const NUM_VERSIONS_PER_PAGE: u64 = 10;
|
||||||
|
const ONE_DAY_MS: i64 = 86400000;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::{RequestUser, RequestUserExtension},
|
auth::{RequestUser, RequestUserExtension},
|
||||||
@@ -49,12 +54,35 @@ pub struct BuildVersionsQuery {
|
|||||||
patch: Option<i32>,
|
patch: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct BuildStatsQuery {
|
||||||
|
#[serde(default)]
|
||||||
|
page: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct BuildStatsResponse {
|
||||||
|
pub total_time: f64, // in hours
|
||||||
|
pub total_count: f64, // number of builds
|
||||||
|
pub days: Vec<BuildStatsDay>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
|
pub struct BuildStatsDay {
|
||||||
|
pub time: f64,
|
||||||
|
pub count: f64,
|
||||||
|
pub ts: f64,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn router() -> Router {
|
pub fn router() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/:id",
|
"/:id",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Path(build_id): Path<BuildId>| async move {
|
Path(build_id): Path<BuildId>| async move {
|
||||||
let build = state
|
let build = state
|
||||||
@@ -68,7 +96,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/list",
|
"/list",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Query(query): Query<Document>| async move {
|
Query(query): Query<Document>| async move {
|
||||||
let builds = state
|
let builds = state
|
||||||
@@ -82,7 +110,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/create",
|
"/create",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Json(build): Json<CreateBuildBody>| async move {
|
Json(build): Json<CreateBuildBody>| async move {
|
||||||
let build = state
|
let build = state
|
||||||
@@ -96,7 +124,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/create_full",
|
"/create_full",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Json(build): Json<Build>| async move {
|
Json(build): Json<Build>| async move {
|
||||||
let build = spawn_request_action(async move {
|
let build = spawn_request_action(async move {
|
||||||
@@ -113,7 +141,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/copy",
|
"/:id/copy",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Path(BuildId { id }): Path<BuildId>,
|
Path(BuildId { id }): Path<BuildId>,
|
||||||
Json(build): Json<CopyBuildBody>| async move {
|
Json(build): Json<CopyBuildBody>| async move {
|
||||||
@@ -131,7 +159,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/delete",
|
"/:id/delete",
|
||||||
delete(
|
delete(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Path(build_id): Path<BuildId>| async move {
|
Path(build_id): Path<BuildId>| async move {
|
||||||
let build = spawn_request_action(async move {
|
let build = spawn_request_action(async move {
|
||||||
@@ -148,7 +176,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/update",
|
"/update",
|
||||||
patch(
|
patch(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Json(build): Json<Build>| async move {
|
Json(build): Json<Build>| async move {
|
||||||
let build = spawn_request_action(async move {
|
let build = spawn_request_action(async move {
|
||||||
@@ -165,7 +193,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/build",
|
"/:id/build",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Path(build_id): Path<BuildId>| async move {
|
Path(build_id): Path<BuildId>| async move {
|
||||||
let update = spawn_request_action(async move {
|
let update = spawn_request_action(async move {
|
||||||
@@ -182,7 +210,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/action_state",
|
"/:id/action_state",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Path(BuildId { id }): Path<BuildId>| async move {
|
Path(BuildId { id }): Path<BuildId>| async move {
|
||||||
let action_state = state
|
let action_state = state
|
||||||
@@ -196,7 +224,7 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/versions",
|
"/:id/versions",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
Extension(user): RequestUserExtension,
|
||||||
Path(BuildId { id }),
|
Path(BuildId { id }),
|
||||||
Query(query): Query<BuildVersionsQuery>| async move {
|
Query(query): Query<BuildVersionsQuery>| async move {
|
||||||
@@ -210,7 +238,7 @@ pub fn router() -> Router {
|
|||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/aws_builder_defaults",
|
"/aws_builder_defaults",
|
||||||
get(|Extension(state): StateExtension| async move {
|
get(|state: StateExtension| async move {
|
||||||
Json(AwsBuilderConfig {
|
Json(AwsBuilderConfig {
|
||||||
access_key_id: String::new(),
|
access_key_id: String::new(),
|
||||||
secret_access_key: String::new(),
|
secret_access_key: String::new(),
|
||||||
@@ -220,10 +248,17 @@ pub fn router() -> Router {
|
|||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/docker_organizations",
|
"/docker_organizations",
|
||||||
get(|Extension(state): StateExtension| async move {
|
get(|state: StateExtension| async move {
|
||||||
Json(state.config.docker_organizations.clone())
|
Json(state.config.docker_organizations.clone())
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/stats",
|
||||||
|
get(|state: StateExtension, query: Query<BuildStatsQuery>| async move {
|
||||||
|
let stats = state.get_build_stats(query.page).await.map_err(handle_anyhow_error)?;
|
||||||
|
response!(Json(stats))
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@@ -268,7 +303,7 @@ impl State {
|
|||||||
Ok(action_state)
|
Ok(action_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_build_versions(
|
async fn get_build_versions(
|
||||||
&self,
|
&self,
|
||||||
id: &str,
|
id: &str,
|
||||||
user: &RequestUser,
|
user: &RequestUser,
|
||||||
@@ -317,4 +352,86 @@ impl State {
|
|||||||
.collect();
|
.collect();
|
||||||
Ok(versions)
|
Ok(versions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_build_stats(&self, page: u32) -> anyhow::Result<BuildStatsResponse> {
|
||||||
|
let curr_ts = unix_timestamp_ms() as i64;
|
||||||
|
let next_day = curr_ts - curr_ts % ONE_DAY_MS + ONE_DAY_MS;
|
||||||
|
|
||||||
|
let close_ts = next_day - page as i64 * 30 * ONE_DAY_MS;
|
||||||
|
let open_ts = close_ts - 30 * ONE_DAY_MS;
|
||||||
|
|
||||||
|
let mut build_updates = self
|
||||||
|
.db
|
||||||
|
.updates
|
||||||
|
.collection
|
||||||
|
.find(
|
||||||
|
doc! {
|
||||||
|
"start_ts": {
|
||||||
|
"$gte": monitor_ts_from_unix(open_ts)
|
||||||
|
.context("open_ts out of bounds")?,
|
||||||
|
"$lt": monitor_ts_from_unix(close_ts)
|
||||||
|
.context("close_ts out of bounds")?
|
||||||
|
},
|
||||||
|
"operation": Operation::BuildBuild.to_string(),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut days = HashMap::<i64, BuildStatsDay>::with_capacity(32);
|
||||||
|
|
||||||
|
let mut curr = open_ts;
|
||||||
|
|
||||||
|
while curr < close_ts {
|
||||||
|
let stats = BuildStatsDay {
|
||||||
|
ts: curr as f64,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
days.insert(curr, stats);
|
||||||
|
curr += ONE_DAY_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(update) = build_updates.try_next().await? {
|
||||||
|
if let Some(end_ts) = update.end_ts {
|
||||||
|
let start_ts = unix_from_monitor_ts(&update.start_ts)
|
||||||
|
.context("failed to parse update start_ts")?;
|
||||||
|
let end_ts =
|
||||||
|
unix_from_monitor_ts(&end_ts).context("failed to parse update end_ts")?;
|
||||||
|
let day = start_ts - start_ts % ONE_DAY_MS;
|
||||||
|
let mut entry = days.entry(day).or_default();
|
||||||
|
entry.count += 1.0;
|
||||||
|
entry.time += ms_to_hour(end_ts - start_ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(BuildStatsResponse::new(days.into_values().collect()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildStatsResponse {
|
||||||
|
fn new(mut days: Vec<BuildStatsDay>) -> BuildStatsResponse {
|
||||||
|
days.sort_by(|a, b| {
|
||||||
|
if a.ts < b.ts {
|
||||||
|
Ordering::Less
|
||||||
|
} else {
|
||||||
|
Ordering::Greater
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut total_time = 0.0;
|
||||||
|
let mut total_count = 0.0;
|
||||||
|
for day in &days {
|
||||||
|
total_time += day.time;
|
||||||
|
total_count += day.count;
|
||||||
|
}
|
||||||
|
BuildStatsResponse {
|
||||||
|
total_time,
|
||||||
|
total_count,
|
||||||
|
days,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MS_TO_HOUR_DIVISOR: f64 = 1000.0 * 60.0 * 60.0;
|
||||||
|
fn ms_to_hour(duration: i64) -> f64 {
|
||||||
|
duration as f64 / MS_TO_HOUR_DIVISOR
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use anyhow::Context;
|
|||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, Query},
|
extract::{Path, Query},
|
||||||
routing::{delete, get, patch, post},
|
routing::{delete, get, patch, post},
|
||||||
Extension, Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
use futures_util::future::join_all;
|
use futures_util::future::join_all;
|
||||||
use helpers::handle_anyhow_error;
|
use helpers::handle_anyhow_error;
|
||||||
@@ -12,7 +12,7 @@ use mungos::{doc, options::FindOneOptions, Deserialize, Document, Serialize};
|
|||||||
use types::{
|
use types::{
|
||||||
traits::Permissioned, Deployment, DeploymentActionState, DeploymentWithContainerState,
|
traits::Permissioned, Deployment, DeploymentActionState, DeploymentWithContainerState,
|
||||||
DockerContainerState, DockerContainerStats, Log, Operation, PermissionLevel, Server,
|
DockerContainerState, DockerContainerStats, Log, Operation, PermissionLevel, Server,
|
||||||
UpdateStatus,
|
TerminationSignal, UpdateStatus,
|
||||||
};
|
};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
@@ -55,16 +55,23 @@ pub struct GetContainerLogQuery {
|
|||||||
tail: Option<u32>,
|
tail: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct StopContainerQuery {
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn router() -> Router {
|
pub fn router() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/:id",
|
"/:id",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId { id })| async move {
|
||||||
let res = state
|
let res = state
|
||||||
.get_deployment_with_container_state(&user, &deployment_id.id)
|
.get_deployment_with_container_state(&user, &id)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)?;
|
.map_err(handle_anyhow_error)?;
|
||||||
response!(Json(res))
|
response!(Json(res))
|
||||||
@@ -74,8 +81,8 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/list",
|
"/list",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Query(query): Query<Document>| async move {
|
Query(query): Query<Document>| async move {
|
||||||
let deployments = state
|
let deployments = state
|
||||||
.list_deployments_with_container_state(&user, query)
|
.list_deployments_with_container_state(&user, query)
|
||||||
@@ -88,8 +95,8 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/create",
|
"/create",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Json(deployment): Json<CreateDeploymentBody>| async move {
|
Json(deployment): Json<CreateDeploymentBody>| async move {
|
||||||
let deployment = state
|
let deployment = state
|
||||||
.create_deployment(&deployment.name, deployment.server_id, &user)
|
.create_deployment(&deployment.name, deployment.server_id, &user)
|
||||||
@@ -102,8 +109,8 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/create_full",
|
"/create_full",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Json(full_deployment): Json<Deployment>| async move {
|
Json(full_deployment): Json<Deployment>| async move {
|
||||||
let deployment = spawn_request_action(async move {
|
let deployment = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
@@ -119,9 +126,9 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/copy",
|
"/:id/copy",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(DeploymentId { id }): Path<DeploymentId>,
|
Path(DeploymentId { id }),
|
||||||
Json(deployment): Json<CopyDeploymentBody>| async move {
|
Json(deployment): Json<CopyDeploymentBody>| async move {
|
||||||
let deployment = spawn_request_action(async move {
|
let deployment = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
@@ -137,12 +144,13 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/delete",
|
"/:id/delete",
|
||||||
delete(
|
delete(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId{ id }),
|
||||||
|
Query(StopContainerQuery { stop_signal, stop_time })| async move {
|
||||||
let deployment = spawn_request_action(async move {
|
let deployment = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
.delete_deployment(&deployment_id.id, &user)
|
.delete_deployment(&id, &user, stop_signal, stop_time)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
})
|
})
|
||||||
@@ -154,8 +162,8 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/update",
|
"/update",
|
||||||
patch(
|
patch(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Json(deployment): Json<Deployment>| async move {
|
Json(deployment): Json<Deployment>| async move {
|
||||||
let deployment = spawn_request_action(async move {
|
let deployment = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
@@ -189,12 +197,12 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/reclone",
|
"/:id/reclone",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId { id })| async move {
|
||||||
let update = spawn_request_action(async move {
|
let update = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
.reclone_deployment(&deployment_id.id, &user)
|
.reclone_deployment(&id, &user, true)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
})
|
})
|
||||||
@@ -206,12 +214,13 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/deploy",
|
"/:id/deploy",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId { id }),
|
||||||
|
Query(StopContainerQuery { stop_signal, stop_time })| async move {
|
||||||
let update = spawn_request_action(async move {
|
let update = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
.deploy_container(&deployment_id.id, &user)
|
.deploy_container(&id, &user, stop_signal, stop_time)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
})
|
})
|
||||||
@@ -223,12 +232,12 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/start_container",
|
"/:id/start_container",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId { id })| async move {
|
||||||
let update = spawn_request_action(async move {
|
let update = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
.start_container(&deployment_id.id, &user)
|
.start_container(&id, &user)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
})
|
})
|
||||||
@@ -240,12 +249,13 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/stop_container",
|
"/:id/stop_container",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId { id }),
|
||||||
|
Query(StopContainerQuery { stop_signal, stop_time })| async move {
|
||||||
let update = spawn_request_action(async move {
|
let update = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
.stop_container(&deployment_id.id, &user)
|
.stop_container(&id, &user, stop_signal, stop_time)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
})
|
})
|
||||||
@@ -257,12 +267,13 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/remove_container",
|
"/:id/remove_container",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId { id }),
|
||||||
|
Query(StopContainerQuery { stop_signal, stop_time })| async move {
|
||||||
let update = spawn_request_action(async move {
|
let update = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
.remove_container(&deployment_id.id, &user)
|
.remove_container(&id, &user, stop_signal, stop_time)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
})
|
})
|
||||||
@@ -274,12 +285,12 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/pull",
|
"/:id/pull",
|
||||||
post(
|
post(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>| async move {
|
Path(DeploymentId { id })| async move {
|
||||||
let update = spawn_request_action(async move {
|
let update = spawn_request_action(async move {
|
||||||
state
|
state
|
||||||
.pull_deployment_repo(&deployment_id.id, &user)
|
.pull_deployment_repo(&id, &user)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
})
|
})
|
||||||
@@ -291,8 +302,8 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/action_state",
|
"/:id/action_state",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(DeploymentId { id }): Path<DeploymentId>| async move {
|
Path(DeploymentId { id }): Path<DeploymentId>| async move {
|
||||||
let action_state = state
|
let action_state = state
|
||||||
.get_deployment_action_states(id, &user)
|
.get_deployment_action_states(id, &user)
|
||||||
@@ -305,12 +316,12 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/log",
|
"/:id/log",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(deployment_id): Path<DeploymentId>,
|
Path(DeploymentId { id }),
|
||||||
Query(query): Query<GetContainerLogQuery>| async move {
|
Query(query): Query<GetContainerLogQuery>| async move {
|
||||||
let log = state
|
let log = state
|
||||||
.get_deployment_container_log(&deployment_id.id, &user, query.tail)
|
.get_deployment_container_log(&id, &user, query.tail)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)?;
|
.map_err(handle_anyhow_error)?;
|
||||||
response!(Json(log))
|
response!(Json(log))
|
||||||
@@ -320,8 +331,8 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/stats",
|
"/:id/stats",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(DeploymentId { id })| async move {
|
Path(DeploymentId { id })| async move {
|
||||||
let stats = state
|
let stats = state
|
||||||
.get_deployment_container_stats(&id, &user)
|
.get_deployment_container_stats(&id, &user)
|
||||||
@@ -334,8 +345,8 @@ pub fn router() -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/deployed_version",
|
"/:id/deployed_version",
|
||||||
get(
|
get(
|
||||||
|Extension(state): StateExtension,
|
|state: StateExtension,
|
||||||
Extension(user): RequestUserExtension,
|
user: RequestUserExtension,
|
||||||
Path(DeploymentId { id })| async move {
|
Path(DeploymentId { id })| async move {
|
||||||
let version = state
|
let version = state
|
||||||
.get_deployment_deployed_version(&id, &user)
|
.get_deployment_deployed_version(&id, &user)
|
||||||
|
|||||||
@@ -68,10 +68,15 @@ async fn callback(
|
|||||||
.context("failed to generate jwt")?,
|
.context("failed to generate jwt")?,
|
||||||
None => {
|
None => {
|
||||||
let ts = monitor_timestamp();
|
let ts = monitor_timestamp();
|
||||||
|
let no_users_exist = state.db.users.find_one(None, None).await?.is_none();
|
||||||
let user = User {
|
let user = User {
|
||||||
username: github_user.login,
|
username: github_user.login,
|
||||||
avatar: github_user.avatar_url.into(),
|
avatar: github_user.avatar_url.into(),
|
||||||
github_id: github_id.into(),
|
github_id: github_id.into(),
|
||||||
|
enabled: no_users_exist,
|
||||||
|
admin: no_users_exist,
|
||||||
|
create_server_permissions: no_users_exist,
|
||||||
|
create_build_permissions: no_users_exist,
|
||||||
created_at: ts.clone(),
|
created_at: ts.clone(),
|
||||||
updated_at: ts,
|
updated_at: ts,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ async fn callback(
|
|||||||
.context("failed to generate jwt")?,
|
.context("failed to generate jwt")?,
|
||||||
None => {
|
None => {
|
||||||
let ts = monitor_timestamp();
|
let ts = monitor_timestamp();
|
||||||
|
let no_users_exist = state.db.users.find_one(None, None).await?.is_none();
|
||||||
let user = User {
|
let user = User {
|
||||||
username: google_user
|
username: google_user
|
||||||
.email
|
.email
|
||||||
@@ -95,6 +96,10 @@ async fn callback(
|
|||||||
.to_string(),
|
.to_string(),
|
||||||
avatar: google_user.picture.into(),
|
avatar: google_user.picture.into(),
|
||||||
google_id: google_id.into(),
|
google_id: google_id.into(),
|
||||||
|
enabled: no_users_exist,
|
||||||
|
admin: no_users_exist,
|
||||||
|
create_server_permissions: no_users_exist,
|
||||||
|
create_build_permissions: no_users_exist,
|
||||||
created_at: ts.clone(),
|
created_at: ts.clone(),
|
||||||
updated_at: ts,
|
updated_at: ts,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub type RequestUserExtension = Extension<Arc<RequestUser>>;
|
|||||||
|
|
||||||
type ExchangeTokenMap = Mutex<HashMap<String, (String, u128)>>;
|
type ExchangeTokenMap = Mutex<HashMap<String, (String, u128)>>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct RequestUser {
|
pub struct RequestUser {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ async fn create_user_handler(
|
|||||||
enabled: no_users_exist,
|
enabled: no_users_exist,
|
||||||
admin: no_users_exist,
|
admin: no_users_exist,
|
||||||
create_server_permissions: no_users_exist,
|
create_server_permissions: no_users_exist,
|
||||||
|
create_build_permissions: no_users_exist,
|
||||||
created_at: ts.clone(),
|
created_at: ts.clone(),
|
||||||
updated_at: ts,
|
updated_at: ts,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ type ResponseResult<T> = Result<T, (StatusCode, String)>;
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
println!("version: v{}", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
let (config, spa_router) = config::load();
|
let (config, spa_router) = config::load();
|
||||||
|
|
||||||
println!("starting monitor core on port {}...", config.port);
|
println!("starting monitor core on port {}...", config.port);
|
||||||
|
|||||||
40
docsite/docs/core-setup.md
Normal file
40
docsite/docs/core-setup.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# core setup
|
||||||
|
|
||||||
|
setting up monitor core is fairly simple. there are some requirements to run monitor core:
|
||||||
|
|
||||||
|
- a valid configuration file
|
||||||
|
- an instance of MongoDB to which monitor core can connect
|
||||||
|
- docker must be installed on the host
|
||||||
|
|
||||||
|
## 1. create the configuration file
|
||||||
|
|
||||||
|
create a configuration file on the system, for example at `~/.monitor/core.config.toml`, and copy the [example config](https://github.com/mbecker20/monitor/blob/main/config_example/core.config.example.toml). fill in all the necessary information before continuing.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
to enable OAuth2 login, you must create a client on the respective OAuth provider,
|
||||||
|
for example [google](https://developers.google.com/identity/protocols/oauth2)
|
||||||
|
or [github](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps).
|
||||||
|
monitor uses the `web application` login flow.
|
||||||
|
the redirect uri is `<base_url>/auth/google/callback` for google and `<base_url>/auth/github/callback` for github.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 2. start monitor core
|
||||||
|
|
||||||
|
monitor core is distributed via dockerhub under the public repo [mbecker2020/monitor_core](https://hub.docker.com/r/mbecker2020/monitor_core).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run -d --name monitor-core \
|
||||||
|
-v $HOME/.monitor/core.config.toml:/config/config.toml \
|
||||||
|
-p 9000:9000 \
|
||||||
|
mbecker2020/monitor_core
|
||||||
|
```
|
||||||
|
|
||||||
|
## first login
|
||||||
|
|
||||||
|
monitor core should now be accessible on the specified port, so navigating to `http://<address>:<port>` will display the login page.
|
||||||
|
|
||||||
|
the first user to log in will be auto enabled and made admin. any additional users to create accounts will be disabled by default.
|
||||||
|
|
||||||
|
## https
|
||||||
|
|
||||||
|
monitor core itself only supports http, so a reverse proxy like [caddy](https://caddyserver.com/) should be used for https
|
||||||
@@ -19,6 +19,7 @@ const sidebars = {
|
|||||||
// But you can create a sidebar manually
|
// But you can create a sidebar manually
|
||||||
docs: [
|
docs: [
|
||||||
"intro",
|
"intro",
|
||||||
|
"core-setup",
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
label: "connecting servers",
|
label: "connecting servers",
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
display: flex;
|
display: grid;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,22 +17,44 @@ function HomepageHeader() {
|
|||||||
<div style={{ display: "flex", gap: "1rem", justifyContent: "center" }}>
|
<div style={{ display: "flex", gap: "1rem", justifyContent: "center" }}>
|
||||||
<div style={{ position: "relative" }}>
|
<div style={{ position: "relative" }}>
|
||||||
<MonitorLogo width="600px" />
|
<MonitorLogo width="600px" />
|
||||||
<h1 className="hero__title" style={{ margin: 0, position: "absolute", top: "40%", left: "50%", transform: "translate(-50%, -50%)" }}>
|
<h1
|
||||||
|
className="hero__title"
|
||||||
|
style={{
|
||||||
|
margin: 0,
|
||||||
|
position: "absolute",
|
||||||
|
top: "40%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
monitor
|
monitor
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
<p className="hero__subtitle">{siteConfig.tagline}</p>
|
||||||
<div className={styles.buttons}>
|
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||||
<Link className="button button--secondary button--lg" to="/intro">
|
<div className={styles.buttons}>
|
||||||
docs
|
<Link className="button button--secondary button--lg" to="/intro">
|
||||||
</Link>
|
docs
|
||||||
<Link
|
</Link>
|
||||||
className="button button--secondary button--lg"
|
<Link
|
||||||
to="https://github.com/mbecker20/monitor"
|
className="button button--secondary button--lg"
|
||||||
>
|
to="https://github.com/mbecker20/monitor"
|
||||||
github
|
>
|
||||||
</Link>
|
github
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
className="button button--secondary button--lg"
|
||||||
|
to="https://github.com/mbecker20/monitor#readme"
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
gridColumn: "span 2",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
screenshots
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ export const NewGroup: Component<{}> = (p) => {
|
|||||||
<Show
|
<Show
|
||||||
when={showNew()}
|
when={showNew()}
|
||||||
fallback={
|
fallback={
|
||||||
<button class="green" onClick={toggleShowNew} style={{ height: "100%" }}>
|
<button
|
||||||
|
class="green"
|
||||||
|
onClick={toggleShowNew}
|
||||||
|
style={{ height: "100%" }}
|
||||||
|
>
|
||||||
<Icon type="plus" />
|
<Icon type="plus" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@@ -33,8 +37,12 @@ export const NewDeployment: Component<{ serverID: string }> = (p) => {
|
|||||||
<Show
|
<Show
|
||||||
when={showNew()}
|
when={showNew()}
|
||||||
fallback={
|
fallback={
|
||||||
<button class="green" onClick={toggleShowNew} style={{ width: "100%" }}>
|
<button
|
||||||
<Icon type="plus" />
|
class="green"
|
||||||
|
onClick={toggleShowNew}
|
||||||
|
style={{ width: "100%", height: "fit-content" }}
|
||||||
|
>
|
||||||
|
<Icon type="plus" width="1.2rem" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -56,8 +64,12 @@ export const NewBuild: Component<{}> = (p) => {
|
|||||||
<Show
|
<Show
|
||||||
when={showNew()}
|
when={showNew()}
|
||||||
fallback={
|
fallback={
|
||||||
<button class="green" onClick={toggleShowNew} style={{ width: "100%" }}>
|
<button
|
||||||
<Icon type="plus" />
|
class="green"
|
||||||
|
onClick={toggleShowNew}
|
||||||
|
style={{ width: "100%", height: "fit-content" }}
|
||||||
|
>
|
||||||
|
<Icon type="plus" width="1.2rem" />
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -87,14 +99,14 @@ const New: Component<{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Flex justifyContent="space-between">
|
<Flex justifyContent="space-between" style={{ height: "fit-content", width: "100%" }}>
|
||||||
<Input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={p.placeholder}
|
placeholder={p.placeholder}
|
||||||
value={name()}
|
value={name()}
|
||||||
onEdit={setName}
|
onEdit={setName}
|
||||||
onEnter={create}
|
onEnter={create}
|
||||||
style={{ width: "20rem" }}
|
style={{ width: "100%", "min-width": "20rem" }}
|
||||||
/>
|
/>
|
||||||
<Flex gap="0.4rem">
|
<Flex gap="0.4rem">
|
||||||
<button class="green" onClick={create}>
|
<button class="green" onClick={create}>
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
import { Component, Match, Show, Switch } from "solid-js";
|
import {
|
||||||
|
Component,
|
||||||
|
Match,
|
||||||
|
Setter,
|
||||||
|
Show,
|
||||||
|
Signal,
|
||||||
|
Switch,
|
||||||
|
createSignal,
|
||||||
|
} from "solid-js";
|
||||||
import { client } from "../..";
|
import { client } from "../..";
|
||||||
import { useAppState } from "../../state/StateProvider";
|
import { useAppState } from "../../state/StateProvider";
|
||||||
import { useUser } from "../../state/UserProvider";
|
import { useUser } from "../../state/UserProvider";
|
||||||
@@ -13,10 +21,15 @@ import { combineClasses } from "../../util/helpers";
|
|||||||
import { A, useParams } from "@solidjs/router";
|
import { A, useParams } from "@solidjs/router";
|
||||||
import {
|
import {
|
||||||
DockerContainerState,
|
DockerContainerState,
|
||||||
|
Operation,
|
||||||
PermissionLevel,
|
PermissionLevel,
|
||||||
ServerStatus,
|
ServerStatus,
|
||||||
|
TerminationSignal,
|
||||||
|
TerminationSignalLabel,
|
||||||
|
UpdateStatus,
|
||||||
} from "../../types";
|
} from "../../types";
|
||||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||||
|
import Selector from "../shared/menu/Selector";
|
||||||
|
|
||||||
const Actions: Component<{}> = (p) => {
|
const Actions: Component<{}> = (p) => {
|
||||||
const { deployments, builds, servers, getPermissionOnDeployment } =
|
const { deployments, builds, servers, getPermissionOnDeployment } =
|
||||||
@@ -161,12 +174,12 @@ const Build: Component = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||||
// const { deployments } = useAppState();
|
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
// const deployment = () => deployments.get(params.id)!;
|
|
||||||
const actions = useActionStates();
|
const actions = useActionStates();
|
||||||
const { deployments } = useAppState();
|
const { deployments } = useAppState();
|
||||||
const name = () => deployments.get(params.id)?.deployment.name;
|
const deployment = () => deployments.get(params.id);
|
||||||
|
const name = () => deployment()?.deployment.name;
|
||||||
|
const [termSignalLabel, setTermSignalLabel] = useTermSignalLabel();
|
||||||
return (
|
return (
|
||||||
<Show
|
<Show
|
||||||
when={!actions.deploying}
|
when={!actions.deploying}
|
||||||
@@ -194,9 +207,19 @@ const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
|||||||
<ConfirmMenuButton
|
<ConfirmMenuButton
|
||||||
class="green"
|
class="green"
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
client.deploy_container(params.id);
|
client.deploy_container(params.id, {
|
||||||
|
stop_signal: ((termSignalLabel().signal as any) === "default"
|
||||||
|
? undefined
|
||||||
|
: termSignalLabel().signal) as TerminationSignal,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
title="redeploy container"
|
title="redeploy container"
|
||||||
|
configs={
|
||||||
|
<TermSignalSelector
|
||||||
|
termSignalLabel={termSignalLabel()}
|
||||||
|
setTermSignalLabel={setTermSignalLabel}
|
||||||
|
/>
|
||||||
|
}
|
||||||
match={name()!}
|
match={name()!}
|
||||||
>
|
>
|
||||||
<Icon type={"reset"} />
|
<Icon type={"reset"} />
|
||||||
@@ -216,6 +239,7 @@ const RemoveContainer = () => {
|
|||||||
const actions = useActionStates();
|
const actions = useActionStates();
|
||||||
const { deployments } = useAppState();
|
const { deployments } = useAppState();
|
||||||
const name = () => deployments.get(params.id)?.deployment.name;
|
const name = () => deployments.get(params.id)?.deployment.name;
|
||||||
|
const [termSignalLabel, setTermSignalLabel] = useTermSignalLabel();
|
||||||
return (
|
return (
|
||||||
<Show
|
<Show
|
||||||
when={!actions.removing}
|
when={!actions.removing}
|
||||||
@@ -230,9 +254,19 @@ const RemoveContainer = () => {
|
|||||||
<ConfirmMenuButton
|
<ConfirmMenuButton
|
||||||
class="red"
|
class="red"
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
client.remove_container(params.id);
|
client.remove_container(params.id, {
|
||||||
|
stop_signal: ((termSignalLabel().signal as any) === "default"
|
||||||
|
? undefined
|
||||||
|
: termSignalLabel().signal) as TerminationSignal,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
title="destroy container"
|
title="destroy container"
|
||||||
|
configs={
|
||||||
|
<TermSignalSelector
|
||||||
|
termSignalLabel={termSignalLabel()}
|
||||||
|
setTermSignalLabel={setTermSignalLabel}
|
||||||
|
/>
|
||||||
|
}
|
||||||
match={name()!}
|
match={name()!}
|
||||||
>
|
>
|
||||||
<Icon type="trash" />
|
<Icon type="trash" />
|
||||||
@@ -282,6 +316,7 @@ const Stop = () => {
|
|||||||
const actions = useActionStates();
|
const actions = useActionStates();
|
||||||
const { deployments } = useAppState();
|
const { deployments } = useAppState();
|
||||||
const name = () => deployments.get(params.id)?.deployment.name;
|
const name = () => deployments.get(params.id)?.deployment.name;
|
||||||
|
const [termSignalLabel, setTermSignalLabel] = useTermSignalLabel();
|
||||||
return (
|
return (
|
||||||
<Show
|
<Show
|
||||||
when={!actions.stopping}
|
when={!actions.stopping}
|
||||||
@@ -296,9 +331,17 @@ const Stop = () => {
|
|||||||
<ConfirmMenuButton
|
<ConfirmMenuButton
|
||||||
class="orange"
|
class="orange"
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
client.stop_container(params.id);
|
client.stop_container(params.id, {
|
||||||
|
stop_signal: termSignalLabel().signal,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
title="stop container"
|
title="stop container"
|
||||||
|
configs={
|
||||||
|
<TermSignalSelector
|
||||||
|
termSignalLabel={termSignalLabel()}
|
||||||
|
setTermSignalLabel={setTermSignalLabel}
|
||||||
|
/>
|
||||||
|
}
|
||||||
match={name()!}
|
match={name()!}
|
||||||
>
|
>
|
||||||
<Icon type="pause" />
|
<Icon type="pause" />
|
||||||
@@ -374,4 +417,68 @@ const Reclone = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TermSignalSelector: Component<{
|
||||||
|
termSignalLabel: TerminationSignalLabel;
|
||||||
|
setTermSignalLabel: Setter<TerminationSignalLabel>;
|
||||||
|
}> = (p) => {
|
||||||
|
const params = useParams();
|
||||||
|
const { deployments } = useAppState();
|
||||||
|
const deployment = () => deployments.get(params.id);
|
||||||
|
return (
|
||||||
|
<Show
|
||||||
|
when={
|
||||||
|
deployment()?.state === DockerContainerState.Running &&
|
||||||
|
(deployment()?.deployment.term_signal_labels?.length || 0) > 1
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
class="full-width wrap"
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<div class="dimmed">termination signal: </div>
|
||||||
|
<Selector
|
||||||
|
targetClass="blue"
|
||||||
|
selected={p.termSignalLabel}
|
||||||
|
items={deployment()?.deployment.term_signal_labels || []}
|
||||||
|
itemMap={({ signal, label }) => (
|
||||||
|
<Flex gap="0.5rem" alignItems="center">
|
||||||
|
<div>{signal}</div>
|
||||||
|
<Show when={label.length > 0}>
|
||||||
|
<div class="dimmed">{label}</div>
|
||||||
|
</Show>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
onSelect={(signal) => p.setTermSignalLabel(signal)}
|
||||||
|
position="bottom right"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function useTermSignalLabel(): Signal<TerminationSignalLabel> {
|
||||||
|
const params = useParams();
|
||||||
|
const { deployments, ws } = useAppState();
|
||||||
|
const deployment = () => deployments.get(params.id)?.deployment;
|
||||||
|
const term_signal = () =>
|
||||||
|
deployment()?.termination_signal || TerminationSignal.SigTerm;
|
||||||
|
const default_term_signal_label = () => ({
|
||||||
|
signal: term_signal(),
|
||||||
|
label:
|
||||||
|
deployment()?.term_signal_labels?.find(
|
||||||
|
({ signal }) => signal === term_signal()
|
||||||
|
)?.label || "",
|
||||||
|
});
|
||||||
|
const [label, setLabel] = createSignal<TerminationSignalLabel>(
|
||||||
|
default_term_signal_label()
|
||||||
|
);
|
||||||
|
ws.subscribe([Operation.UpdateDeployment], (update) => {
|
||||||
|
if (update.status === UpdateStatus.Complete) {
|
||||||
|
setTimeout(() => setLabel(default_term_signal_label()), 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return [label, setLabel];
|
||||||
|
}
|
||||||
|
|
||||||
export default Actions;
|
export default Actions;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Component, Show } from "solid-js";
|
|||||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||||
import { useAppState } from "../../state/StateProvider";
|
import { useAppState } from "../../state/StateProvider";
|
||||||
import { useUser } from "../../state/UserProvider";
|
import { useUser } from "../../state/UserProvider";
|
||||||
import { PermissionLevel } from "../../types";
|
import { PermissionLevel, TerminationSignal } from "../../types";
|
||||||
import Description from "../Description";
|
import Description from "../Description";
|
||||||
import NotFound from "../NotFound";
|
import NotFound from "../NotFound";
|
||||||
import Grid from "../shared/layout/Grid";
|
import Grid from "../shared/layout/Grid";
|
||||||
@@ -16,6 +16,13 @@ import Updates from "./Updates";
|
|||||||
const POLLING_RATE = 10000;
|
const POLLING_RATE = 10000;
|
||||||
// let interval = -1;
|
// let interval = -1;
|
||||||
|
|
||||||
|
export const TERM_SIGNALS = [
|
||||||
|
TerminationSignal.SigTerm,
|
||||||
|
TerminationSignal.SigInt,
|
||||||
|
TerminationSignal.SigQuit,
|
||||||
|
TerminationSignal.SigHup,
|
||||||
|
];
|
||||||
|
|
||||||
const Deployment: Component<{}> = (p) => {
|
const Deployment: Component<{}> = (p) => {
|
||||||
const { user, user_id } = useUser();
|
const { user, user_id } = useUser();
|
||||||
const { servers, deployments } = useAppState();
|
const { servers, deployments } = useAppState();
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import { useAppDimensions } from "../../../../state/DimensionProvider";
|
|||||||
import SimpleTabs from "../../../shared/tabs/SimpleTabs";
|
import SimpleTabs from "../../../shared/tabs/SimpleTabs";
|
||||||
import ExtraArgs from "./container/ExtraArgs";
|
import ExtraArgs from "./container/ExtraArgs";
|
||||||
import WebhookUrl from "./container/WebhookUrl";
|
import WebhookUrl from "./container/WebhookUrl";
|
||||||
|
import RedeployOnBuild from "./container/RedeployOnBuild";
|
||||||
|
import TerminationSignals, { DefaultTerminationSignal, DefaultTerminationTimeout } from "./termination/TerminationSignals";
|
||||||
|
|
||||||
const Config: Component<{}> = () => {
|
const Config: Component<{}> = () => {
|
||||||
const { deployment, reset, save, userCanUpdate } = useConfig();
|
const { deployment, reset, save, userCanUpdate } = useConfig();
|
||||||
@@ -47,6 +49,20 @@ const Config: Component<{}> = () => {
|
|||||||
<Mounts />
|
<Mounts />
|
||||||
<ExtraArgs />
|
<ExtraArgs />
|
||||||
<PostImage />
|
<PostImage />
|
||||||
|
<RedeployOnBuild />
|
||||||
|
<Show when={isMobile()}>
|
||||||
|
<div style={{ height: "1rem" }} />
|
||||||
|
</Show>
|
||||||
|
</Grid>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "termination",
|
||||||
|
element: () => (
|
||||||
|
<Grid class="config-items" placeItems="start center" style={{ "margin-bottom": "" }}>
|
||||||
|
<TerminationSignals />
|
||||||
|
<DefaultTerminationSignal />
|
||||||
|
<DefaultTerminationTimeout />
|
||||||
<Show when={isMobile()}>
|
<Show when={isMobile()}>
|
||||||
<div style={{ height: "1rem" }} />
|
<div style={{ height: "1rem" }} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { client } from "../../../../..";
|
|||||||
import { useAppState } from "../../../../../state/StateProvider";
|
import { useAppState } from "../../../../../state/StateProvider";
|
||||||
import {
|
import {
|
||||||
combineClasses,
|
combineClasses,
|
||||||
string_to_version,
|
readableVersion,
|
||||||
version_to_string,
|
readableMonitorTimestamp,
|
||||||
} from "../../../../../util/helpers";
|
} from "../../../../../util/helpers";
|
||||||
import Input from "../../../../shared/Input";
|
import Input from "../../../../shared/Input";
|
||||||
import Flex from "../../../../shared/layout/Flex";
|
import Flex from "../../../../shared/layout/Flex";
|
||||||
@@ -14,11 +14,6 @@ import { useConfig } from "../Provider";
|
|||||||
const Image: Component<{}> = (p) => {
|
const Image: Component<{}> = (p) => {
|
||||||
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||||
const { builds } = useAppState();
|
const { builds } = useAppState();
|
||||||
const [versions] = createResource(() => {
|
|
||||||
if (deployment.build_id) {
|
|
||||||
return client.get_build_versions(deployment.build_id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
class={combineClasses("config-item shadow")}
|
class={combineClasses("config-item shadow")}
|
||||||
@@ -41,7 +36,8 @@ const Image: Component<{}> = (p) => {
|
|||||||
<Selector
|
<Selector
|
||||||
targetClass="blue"
|
targetClass="blue"
|
||||||
selected={
|
selected={
|
||||||
(deployment.build_id && (builds.get(deployment.build_id)?.name || "unknown")) ||
|
(deployment.build_id &&
|
||||||
|
(builds.get(deployment.build_id)?.name || "unknown")) ||
|
||||||
"custom image"
|
"custom image"
|
||||||
}
|
}
|
||||||
items={[
|
items={[
|
||||||
@@ -65,33 +61,7 @@ const Image: Component<{}> = (p) => {
|
|||||||
useSearch
|
useSearch
|
||||||
/>
|
/>
|
||||||
<Show when={deployment.build_id}>
|
<Show when={deployment.build_id}>
|
||||||
<Selector
|
<VersionSelector />
|
||||||
targetClass="blue"
|
|
||||||
selected={
|
|
||||||
deployment.build_version
|
|
||||||
? `v${version_to_string(deployment.build_version)}`
|
|
||||||
: "latest"
|
|
||||||
}
|
|
||||||
items={[
|
|
||||||
"latest",
|
|
||||||
...(versions()?.map(
|
|
||||||
(v) => `v${version_to_string(v.version)}`
|
|
||||||
) || []),
|
|
||||||
]}
|
|
||||||
onSelect={(version) => {
|
|
||||||
if (version === "latest") {
|
|
||||||
setDeployment("build_version", undefined);
|
|
||||||
} else {
|
|
||||||
setDeployment(
|
|
||||||
"build_version",
|
|
||||||
string_to_version(version.replace("v", ""))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
position="bottom right"
|
|
||||||
disabled={!userCanUpdate()}
|
|
||||||
useSearch
|
|
||||||
/>
|
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -100,3 +70,54 @@ const Image: Component<{}> = (p) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default Image;
|
export default Image;
|
||||||
|
|
||||||
|
|
||||||
|
const VersionSelector: Component<{}> = (p) => {
|
||||||
|
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||||
|
const [versions] = createResource(() => {
|
||||||
|
if (deployment.build_id) {
|
||||||
|
return client.get_build_versions(deployment.build_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const selected = () => ({
|
||||||
|
version: deployment.build_version || {
|
||||||
|
major: 0,
|
||||||
|
minor: 0,
|
||||||
|
patch: 0,
|
||||||
|
},
|
||||||
|
ts: "",
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Selector
|
||||||
|
targetClass="blue"
|
||||||
|
selected={selected()}
|
||||||
|
items={[
|
||||||
|
{ version: { major: 0, minor: 0, patch: 0 }, ts: "" },
|
||||||
|
...(versions() || []),
|
||||||
|
]}
|
||||||
|
itemMap={({ version, ts }) => (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
{version.major === 0 && version.minor === 0 && version.patch === 0
|
||||||
|
? "latest"
|
||||||
|
: readableVersion(version)}
|
||||||
|
</div>
|
||||||
|
<Show when={ts.length > 0}>
|
||||||
|
<div class="dimmed">{readableMonitorTimestamp(ts)}</div>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
searchItemMap={({ version }) => readableVersion(version)}
|
||||||
|
onSelect={({ version, ts }) => {
|
||||||
|
if (ts.length === 0) {
|
||||||
|
setDeployment("build_version", undefined);
|
||||||
|
} else {
|
||||||
|
setDeployment("build_version", version);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
position="bottom right"
|
||||||
|
disabled={!userCanUpdate()}
|
||||||
|
useSearch
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Component, Show } from "solid-js";
|
||||||
|
import { useConfig } from "../Provider";
|
||||||
|
import Flex from "../../../../shared/layout/Flex";
|
||||||
|
|
||||||
|
const RedeployOnBuild: Component<{}> = (p) => {
|
||||||
|
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||||
|
return (
|
||||||
|
<Show when={deployment.build_id}>
|
||||||
|
<Flex
|
||||||
|
class="config-item shadow"
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<h1>redeploy on build</h1>
|
||||||
|
<Show
|
||||||
|
when={userCanUpdate()}
|
||||||
|
fallback={<h2>{deployment.redeploy_on_build ? "yes" : "no"}</h2>}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class={deployment.redeploy_on_build ? "green" : "red"}
|
||||||
|
onClick={() => setDeployment("redeploy_on_build", (v) => !v)}
|
||||||
|
>
|
||||||
|
{deployment.redeploy_on_build ? "yes" : "no"}
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</Flex>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RedeployOnBuild;
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
import { Component, For, Show, createSignal } from "solid-js";
|
||||||
|
import { useConfig } from "../Provider";
|
||||||
|
import Grid from "../../../../shared/layout/Grid";
|
||||||
|
import Flex from "../../../../shared/layout/Flex";
|
||||||
|
import Icon from "../../../../shared/Icon";
|
||||||
|
import { TERM_SIGNALS } from "../../../Deployment";
|
||||||
|
import { TerminationSignal } from "../../../../../types";
|
||||||
|
import Input from "../../../../shared/Input";
|
||||||
|
import Menu from "../../../../shared/menu/Menu";
|
||||||
|
import Selector from "../../../../shared/menu/Selector";
|
||||||
|
import { pushNotification } from "../../../../..";
|
||||||
|
|
||||||
|
const TerminationSignals: Component<{}> = (p) => {
|
||||||
|
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||||
|
const signals_to_add = () =>
|
||||||
|
TERM_SIGNALS.filter(
|
||||||
|
(sig) =>
|
||||||
|
!deployment.term_signal_labels
|
||||||
|
?.map(({ signal }) => signal)
|
||||||
|
.includes(sig)
|
||||||
|
);
|
||||||
|
const onAdd = (signal: TerminationSignal) => {
|
||||||
|
setDeployment("term_signal_labels", (term_signals: any) => [
|
||||||
|
...term_signals,
|
||||||
|
{ signal, label: "" },
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
const onRemove = (index: number) => {
|
||||||
|
setDeployment("term_signal_labels", (term_signals) =>
|
||||||
|
term_signals?.filter((_, i) => i !== index)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const [menuOpen, setMenuOpen] = createSignal(false);
|
||||||
|
return (
|
||||||
|
<Grid class="config-item shadow">
|
||||||
|
<Flex alignItems="center" justifyContent="space-between">
|
||||||
|
<h1>termination signals</h1>
|
||||||
|
<Show when={userCanUpdate() && signals_to_add().length > 0}>
|
||||||
|
<Menu
|
||||||
|
show={menuOpen()}
|
||||||
|
close={() => setMenuOpen(false)}
|
||||||
|
target={
|
||||||
|
<button class="green" onClick={() => setMenuOpen(true)}>
|
||||||
|
<Icon type="plus" />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
content={
|
||||||
|
<For each={signals_to_add()}>
|
||||||
|
{(signal) => (
|
||||||
|
<button
|
||||||
|
class="grey"
|
||||||
|
onClick={() => {
|
||||||
|
onAdd(signal);
|
||||||
|
setMenuOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h2>{signal}</h2>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</Flex>
|
||||||
|
<Show when={(deployment.term_signal_labels?.length || 0) > 0}>
|
||||||
|
<Grid gridTemplateColumns="auto 1fr auto" placeItems="center start">
|
||||||
|
<For each={deployment.term_signal_labels}>
|
||||||
|
{({ signal, label }, index) => (
|
||||||
|
<>
|
||||||
|
<h2>{signal}</h2>
|
||||||
|
<Input
|
||||||
|
class="full-width"
|
||||||
|
placeholder="label this termination signal"
|
||||||
|
value={label}
|
||||||
|
onConfirm={(value) =>
|
||||||
|
setDeployment("term_signal_labels", index(), "label", value)
|
||||||
|
}
|
||||||
|
disabled={!userCanUpdate()}
|
||||||
|
/>
|
||||||
|
<Show when={userCanUpdate()}>
|
||||||
|
<button class="red" onClick={() => onRemove(index())}>
|
||||||
|
<Icon type="minus" />
|
||||||
|
</button>
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</Grid>
|
||||||
|
</Show>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TerminationSignals;
|
||||||
|
|
||||||
|
export const DefaultTerminationSignal: Component<{}> = () => {
|
||||||
|
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||||
|
const term_signal = () =>
|
||||||
|
deployment.termination_signal || TerminationSignal.SigTerm;
|
||||||
|
const selected = () => ({
|
||||||
|
signal: term_signal(),
|
||||||
|
label:
|
||||||
|
deployment.term_signal_labels?.find(
|
||||||
|
({ signal }) => signal === term_signal()
|
||||||
|
)?.label || "",
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Show when={deployment.term_signal_labels?.length || 0 > 0}>
|
||||||
|
<Flex
|
||||||
|
class="config-item shadow"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="space-between"
|
||||||
|
>
|
||||||
|
<h1>default termination signal</h1>
|
||||||
|
<Selector
|
||||||
|
disabled={!userCanUpdate()}
|
||||||
|
targetClass="blue"
|
||||||
|
selected={selected()}
|
||||||
|
items={deployment.term_signal_labels || []}
|
||||||
|
onSelect={({ signal }) => setDeployment("termination_signal", signal)}
|
||||||
|
itemMap={({ signal }) => signal}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultTerminationTimeout: Component<{}> = () => {
|
||||||
|
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
class="config-item shadow"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="space-between"
|
||||||
|
>
|
||||||
|
<h1>termination timeout</h1>
|
||||||
|
<div style={{ position: "relative" }}>
|
||||||
|
<Input
|
||||||
|
disabled={!userCanUpdate()}
|
||||||
|
style={{ width: "10rem" }}
|
||||||
|
placeholder="10"
|
||||||
|
value={deployment.termination_timeout}
|
||||||
|
onConfirm={(value) => {
|
||||||
|
const val = Number(value);
|
||||||
|
if (val) {
|
||||||
|
setDeployment("termination_timeout", val);
|
||||||
|
} else {
|
||||||
|
pushNotification("bad", "timeout must be number");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div class="dimmed" style={{ position: "absolute", right: "1rem", top: "50%", transform: "translateY(-50%)" }}>seconds</div>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
131
frontend/src/components/home/BuildSummary.tsx
Normal file
131
frontend/src/components/home/BuildSummary.tsx
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import { Component, Show, createMemo } from "solid-js";
|
||||||
|
import { useAppState } from "../../state/StateProvider";
|
||||||
|
import Flex from "../shared/layout/Flex";
|
||||||
|
import Grid from "../shared/layout/Grid";
|
||||||
|
import LightweightChart from "../shared/LightweightChart";
|
||||||
|
import Loading from "../shared/loading/Loading";
|
||||||
|
import { COLORS } from "../../style/colors";
|
||||||
|
import { BuildStatsResponse } from "../../util/client_types";
|
||||||
|
import { useLocalStorage } from "../../util/hooks";
|
||||||
|
|
||||||
|
const BuildSummary: Component<{}> = (p) => {
|
||||||
|
const { build_stats } = useAppState();
|
||||||
|
return (
|
||||||
|
<Grid class="full-size card" gridTemplateRows="auto 1fr" style={{ "padding-bottom": "0.6rem" }}>
|
||||||
|
<Show
|
||||||
|
when={build_stats.get()}
|
||||||
|
fallback={
|
||||||
|
<Grid class="full-size" placeItems="center">
|
||||||
|
<Loading type="three-dot" />
|
||||||
|
</Grid>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
style={{ height: "fit-content" }}
|
||||||
|
>
|
||||||
|
<h2>last 30 days</h2>
|
||||||
|
<Flex>
|
||||||
|
<Flex alignItems="center" gap="0.5rem">
|
||||||
|
<div class="dimmed">build time: </div>
|
||||||
|
<h2>{build_stats.get()?.total_time.toFixed(1)} hrs</h2>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems="center" gap="0.5rem">
|
||||||
|
<div class="dimmed">build count: </div>
|
||||||
|
<h2>{build_stats.get()?.total_count.toFixed()}</h2>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
<BuildStatsChart build_stats={build_stats.get()!} />
|
||||||
|
</Show>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BuildSummary;
|
||||||
|
|
||||||
|
const BuildStatsChart: Component<{ build_stats: BuildStatsResponse }> = (p) => {
|
||||||
|
const [mode, setMode] = useLocalStorage<"time" | "count">(
|
||||||
|
"time",
|
||||||
|
"build-stats-chart-mode-v2"
|
||||||
|
);
|
||||||
|
const max = createMemo(() => {
|
||||||
|
return p.build_stats.days.reduce((max, day) => {
|
||||||
|
if (mode() === "count") {
|
||||||
|
if (day.count > max) {
|
||||||
|
return day.count;
|
||||||
|
} else return max;
|
||||||
|
} else {
|
||||||
|
if (day.time > max) {
|
||||||
|
return day.time;
|
||||||
|
} else return max;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div class="full-size" style={{ position: "relative" }}>
|
||||||
|
<LightweightChart
|
||||||
|
class="full-size"
|
||||||
|
style={{ "min-height": "200px" }}
|
||||||
|
histograms={[
|
||||||
|
{
|
||||||
|
line: p.build_stats.days.map((day) => ({
|
||||||
|
value: mode() === "count" ? day.count : (day.time * 60),
|
||||||
|
time: day.ts / 1000,
|
||||||
|
color:
|
||||||
|
mode() === "count"
|
||||||
|
? day.count > max() * 0.7
|
||||||
|
? COLORS.red
|
||||||
|
: day.count > max() * 0.35
|
||||||
|
? COLORS.orange
|
||||||
|
: COLORS.green
|
||||||
|
: day.time > max() * 0.7
|
||||||
|
? COLORS.red
|
||||||
|
: day.time > max() * 0.35
|
||||||
|
? COLORS.orange
|
||||||
|
: COLORS.green,
|
||||||
|
})),
|
||||||
|
priceLineVisible: false,
|
||||||
|
// priceFormat:
|
||||||
|
// mode() === "count"
|
||||||
|
// ? {
|
||||||
|
// minMove: 1,
|
||||||
|
// }
|
||||||
|
// : undefined,
|
||||||
|
priceFormat: {
|
||||||
|
minMove: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
timeVisible={false}
|
||||||
|
options={{
|
||||||
|
grid: {
|
||||||
|
horzLines: { visible: false },
|
||||||
|
vertLines: { visible: false },
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
disableScroll
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
class="blue opaque"
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
"z-index": 20,
|
||||||
|
padding: "0.3rem",
|
||||||
|
}}
|
||||||
|
onClick={() => setMode(mode => {
|
||||||
|
if (mode === "count") {
|
||||||
|
return "time"
|
||||||
|
} else {
|
||||||
|
return "count"
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{mode()}{mode() === "time" ? " (min)" : ""}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,30 +1,44 @@
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component, Match, Show, Switch,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||||
import Grid from "../shared/layout/Grid";
|
import Grid from "../shared/layout/Grid";
|
||||||
import SimpleTabs from "../shared/tabs/SimpleTabs";
|
import { ControlledSimpleTabs } from "../shared/tabs/SimpleTabs";
|
||||||
import Summary from "./Summary";
|
import Summary from "./Summary";
|
||||||
import Builds from "./Tree/Builds";
|
import Builds from "./Tree/Builds";
|
||||||
import Groups from "./Tree/Groups";
|
import Groups from "./Tree/Groups";
|
||||||
import { TreeProvider } from "./Tree/Provider";
|
import { TreeProvider } from "./Tree/Provider";
|
||||||
import Updates from "./Updates/Updates";
|
import Updates from "./Updates/Updates";
|
||||||
|
import { useLocalStorage } from "../../util/hooks";
|
||||||
|
import BuildSummary from "./BuildSummary";
|
||||||
|
|
||||||
const Home: Component<{}> = (p) => {
|
const Home: Component<{}> = (p) => {
|
||||||
const { isSemiMobile } = useAppDimensions();
|
const { isSemiMobile } = useAppDimensions();
|
||||||
|
const [selectedTab, setTab] = useLocalStorage<"servers" | "builds">(
|
||||||
|
"servers",
|
||||||
|
"home-groups-servers-tab-v2"
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid
|
<Grid
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||||
>
|
>
|
||||||
<Summary />
|
<Switch>
|
||||||
|
<Match when={selectedTab() === "servers"}>
|
||||||
|
<Summary />
|
||||||
|
</Match>
|
||||||
|
<Match when={selectedTab() === "builds"}>
|
||||||
|
<BuildSummary />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
<Updates />
|
<Updates />
|
||||||
</Grid>
|
</Grid>
|
||||||
<TreeProvider>
|
<TreeProvider>
|
||||||
<SimpleTabs
|
<ControlledSimpleTabs
|
||||||
|
selected={selectedTab}
|
||||||
|
set={setTab as any}
|
||||||
containerStyle={{ width: "100%" }}
|
containerStyle={{ width: "100%" }}
|
||||||
localStorageKey="home-groups-servers-tab-v1"
|
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
title: "servers",
|
title: "servers",
|
||||||
@@ -32,8 +46,8 @@ const Home: Component<{}> = (p) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "builds",
|
title: "builds",
|
||||||
element: () => <Builds />
|
element: () => <Builds />,
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</TreeProvider>
|
</TreeProvider>
|
||||||
|
|||||||
@@ -74,7 +74,11 @@ const Groups: Component<{}> = (p) => {
|
|||||||
/>
|
/>
|
||||||
<Flex alignItems="center" style={{ width: "fit-content" }}>
|
<Flex alignItems="center" style={{ width: "fit-content" }}>
|
||||||
<Selector
|
<Selector
|
||||||
label={<div class="dimmed">sort by:</div>}
|
label={
|
||||||
|
<div class="dimmed" style={{ "white-space": "nowrap" }}>
|
||||||
|
sort by:
|
||||||
|
</div>
|
||||||
|
}
|
||||||
selected={sort()}
|
selected={sort()}
|
||||||
items={TREE_SORTS as any as string[]}
|
items={TREE_SORTS as any as string[]}
|
||||||
onSelect={(mode) => setSort(mode as TreeSortType)}
|
onSelect={(mode) => setSort(mode as TreeSortType)}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { PermissionLevel } from "../../types";
|
|||||||
import { NewDeployment } from "../New";
|
import { NewDeployment } from "../New";
|
||||||
import Deployment from "./Deployment";
|
import Deployment from "./Deployment";
|
||||||
import { useAppState } from "../../state/StateProvider";
|
import { useAppState } from "../../state/StateProvider";
|
||||||
|
import Flex from "../shared/layout/Flex";
|
||||||
|
|
||||||
const ServerChildren: Component<{ id: string }> = (p) => {
|
const ServerChildren: Component<{ id: string }> = (p) => {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
@@ -35,7 +36,9 @@ const ServerChildren: Component<{ id: string }> = (p) => {
|
|||||||
PermissionLevel.Update
|
PermissionLevel.Update
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<NewDeployment serverID={p.id} />
|
<Flex class="full-width" alignItems="center">
|
||||||
|
<NewDeployment serverID={p.id} />
|
||||||
|
</Flex>
|
||||||
</Show>
|
</Show>
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const ConfirmMenuButton: Component<{
|
|||||||
title: string;
|
title: string;
|
||||||
match: string;
|
match: string;
|
||||||
info?: JSX.Element;
|
info?: JSX.Element;
|
||||||
|
configs?: JSX.Element;
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
const [show, toggleShow] = useToggle();
|
const [show, toggleShow] = useToggle();
|
||||||
@@ -38,6 +39,7 @@ const ConfirmMenuButton: Component<{
|
|||||||
title={p.title}
|
title={p.title}
|
||||||
match={p.match}
|
match={p.match}
|
||||||
info={p.info}
|
info={p.info}
|
||||||
|
configs={p.configs}
|
||||||
onConfirm={p.onConfirm}
|
onConfirm={p.onConfirm}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -52,6 +54,7 @@ const ConfirmMenuContent: Component<{
|
|||||||
match: string;
|
match: string;
|
||||||
onConfirm?: () => void;
|
onConfirm?: () => void;
|
||||||
info?: JSX.Element;
|
info?: JSX.Element;
|
||||||
|
configs?: JSX.Element;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
const [input, setInput] = createSignal("");
|
const [input, setInput] = createSignal("");
|
||||||
return (
|
return (
|
||||||
@@ -71,6 +74,7 @@ const ConfirmMenuContent: Component<{
|
|||||||
value={input()}
|
value={input()}
|
||||||
autofocus
|
autofocus
|
||||||
/>
|
/>
|
||||||
|
{p.configs}
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
class={p.class}
|
class={p.class}
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
AreaSeriesPartialOptions,
|
AreaSeriesPartialOptions,
|
||||||
|
BarSeriesPartialOptions,
|
||||||
|
ChartOptions,
|
||||||
ColorType,
|
ColorType,
|
||||||
createChart,
|
createChart,
|
||||||
|
DeepPartial,
|
||||||
|
HistogramSeriesPartialOptions,
|
||||||
IChartApi,
|
IChartApi,
|
||||||
ISeriesApi,
|
ISeriesApi,
|
||||||
LineSeriesPartialOptions,
|
LineSeriesPartialOptions,
|
||||||
@@ -18,6 +22,7 @@ import {
|
|||||||
export type LightweightValue = {
|
export type LightweightValue = {
|
||||||
time: number | string;
|
time: number | string;
|
||||||
value: number;
|
value: number;
|
||||||
|
color?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LightweightLine = {
|
export type LightweightLine = {
|
||||||
@@ -28,9 +33,14 @@ export type LightweightArea = {
|
|||||||
line: LightweightValue[];
|
line: LightweightValue[];
|
||||||
} & AreaSeriesPartialOptions;
|
} & AreaSeriesPartialOptions;
|
||||||
|
|
||||||
|
export type LightweightHistogram = {
|
||||||
|
line: LightweightValue[];
|
||||||
|
} & HistogramSeriesPartialOptions;
|
||||||
|
|
||||||
const LightweightChart: Component<{
|
const LightweightChart: Component<{
|
||||||
lines?: LightweightLine[];
|
lines?: LightweightLine[];
|
||||||
areas?: LightweightArea[];
|
areas?: LightweightArea[];
|
||||||
|
histograms?: LightweightHistogram[];
|
||||||
class?: string;
|
class?: string;
|
||||||
style?: JSX.CSSProperties;
|
style?: JSX.CSSProperties;
|
||||||
width?: string;
|
width?: string;
|
||||||
@@ -38,11 +48,15 @@ const LightweightChart: Component<{
|
|||||||
disableScroll?: boolean;
|
disableScroll?: boolean;
|
||||||
onCreateLineSeries?: (series: ISeriesApi<"Line">) => void;
|
onCreateLineSeries?: (series: ISeriesApi<"Line">) => void;
|
||||||
onCreateAreaSeries?: (series: ISeriesApi<"Area">) => void;
|
onCreateAreaSeries?: (series: ISeriesApi<"Area">) => void;
|
||||||
|
onCreateHistogramSeries?: (series: ISeriesApi<"Histogram">) => void;
|
||||||
|
timeVisible?: boolean;
|
||||||
|
options?: DeepPartial<ChartOptions>;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
let el: HTMLDivElement;
|
let el: HTMLDivElement;
|
||||||
const [chart, setChart] = createSignal<IChartApi>();
|
const [chart, setChart] = createSignal<IChartApi>();
|
||||||
let lineSeries: ISeriesApi<"Line">[] = [];
|
let lineSeries: ISeriesApi<"Line">[] = [];
|
||||||
let areaSeries: ISeriesApi<"Area">[] = [];
|
let areaSeries: ISeriesApi<"Area">[] = [];
|
||||||
|
let histogramSeries: ISeriesApi<"Histogram">[] = [];
|
||||||
const [loaded, setLoaded] = createSignal(false);
|
const [loaded, setLoaded] = createSignal(false);
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (loaded()) return;
|
if (loaded()) return;
|
||||||
@@ -58,9 +72,10 @@ const LightweightChart: Component<{
|
|||||||
horzLines: { color: "#3f454d" },
|
horzLines: { color: "#3f454d" },
|
||||||
vertLines: { color: "#3f454d" },
|
vertLines: { color: "#3f454d" },
|
||||||
},
|
},
|
||||||
timeScale: { timeVisible: true },
|
timeScale: { timeVisible: p.timeVisible ?? true },
|
||||||
handleScroll: p.disableScroll ? false : true,
|
handleScroll: p.disableScroll ? false : true,
|
||||||
handleScale: p.disableScroll ? false : true,
|
handleScale: p.disableScroll ? false : true,
|
||||||
|
...p.options
|
||||||
});
|
});
|
||||||
chart.timeScale().fitContent();
|
chart.timeScale().fitContent();
|
||||||
setChart(chart);
|
setChart(chart);
|
||||||
@@ -95,6 +110,20 @@ const LightweightChart: Component<{
|
|||||||
});
|
});
|
||||||
areaSeries = series;
|
areaSeries = series;
|
||||||
}
|
}
|
||||||
|
for (const series of histogramSeries) {
|
||||||
|
chart()!.removeSeries(series);
|
||||||
|
}
|
||||||
|
if (p.histograms) {
|
||||||
|
const series = p.histograms.map((line) => {
|
||||||
|
const series = chart()!.addHistogramSeries(line);
|
||||||
|
series.setData(line.line as any);
|
||||||
|
if (p.onCreateHistogramSeries) {
|
||||||
|
p.onCreateHistogramSeries(series);
|
||||||
|
}
|
||||||
|
return series;
|
||||||
|
});
|
||||||
|
histogramSeries = series;
|
||||||
|
}
|
||||||
chart()!.timeScale().fitContent();
|
chart()!.timeScale().fitContent();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import { Position } from "./helpers";
|
|||||||
import Menu from "./Menu";
|
import Menu from "./Menu";
|
||||||
import s from "./menu.module.scss";
|
import s from "./menu.module.scss";
|
||||||
|
|
||||||
const Selector: Component<{
|
const Selector = <T,>(p: {
|
||||||
selected: string;
|
selected: T;
|
||||||
items: string[];
|
items: T[];
|
||||||
onSelect?: (item: string, index: number) => void;
|
onSelect?: (item: T, index: number) => void;
|
||||||
position?: Position;
|
position?: Position;
|
||||||
targetClass?: string;
|
targetClass?: string;
|
||||||
targetStyle?: JSX.CSSProperties;
|
targetStyle?: JSX.CSSProperties;
|
||||||
@@ -33,12 +33,13 @@ const Selector: Component<{
|
|||||||
itemClass?: string;
|
itemClass?: string;
|
||||||
itemStyle?: JSX.CSSProperties;
|
itemStyle?: JSX.CSSProperties;
|
||||||
label?: JSXElement;
|
label?: JSXElement;
|
||||||
itemMap?: (item: string) => string;
|
itemMap?: (item: T) => JSXElement;
|
||||||
}> = (p) => {
|
searchItemMap?: (item: T) => string;
|
||||||
|
}) => {
|
||||||
const [show, toggle] = useToggle();
|
const [show, toggle] = useToggle();
|
||||||
const [search, setSearch] = createSignal("");
|
const [search, setSearch] = createSignal("");
|
||||||
let search_ref: HTMLInputElement | undefined;
|
let search_ref: HTMLInputElement | undefined;
|
||||||
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected);
|
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected as JSXElement);
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (show()) setTimeout(() => search_ref?.focus(), 200);
|
if (show()) setTimeout(() => search_ref?.focus(), 200);
|
||||||
});
|
});
|
||||||
@@ -87,9 +88,11 @@ const Selector: Component<{
|
|||||||
each={
|
each={
|
||||||
p.useSearch
|
p.useSearch
|
||||||
? p.items.filter((item) =>
|
? p.items.filter((item) =>
|
||||||
p.itemMap
|
p.searchItemMap
|
||||||
? p.itemMap(item).includes(search())
|
? p.searchItemMap(item).includes(search())
|
||||||
: item.includes(search())
|
: p.itemMap && typeof p.itemMap(item) === "string"
|
||||||
|
? (p.itemMap(item) as string).includes(search())
|
||||||
|
: (item as string).includes(search())
|
||||||
)
|
)
|
||||||
: p.items
|
: p.items
|
||||||
}
|
}
|
||||||
@@ -107,7 +110,7 @@ const Selector: Component<{
|
|||||||
}}
|
}}
|
||||||
class={combineClasses(p.itemClass, s.SelectorItem)}
|
class={combineClasses(p.itemClass, s.SelectorItem)}
|
||||||
>
|
>
|
||||||
{p.itemMap ? p.itemMap(item) : item}
|
{p.itemMap ? p.itemMap(item) : item as string}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export type Position =
|
|||||||
| "bottom right"
|
| "bottom right"
|
||||||
| "bottom center";
|
| "bottom center";
|
||||||
|
|
||||||
export function getPositionClass(position: Position = "bottom") {
|
export function getPositionClass(position: Position = "bottom right") {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case "left":
|
case "left":
|
||||||
return s.Left;
|
return s.Left;
|
||||||
|
|||||||
@@ -28,19 +28,19 @@ const SimpleTabs: Component<{
|
|||||||
containerClass?: string;
|
containerClass?: string;
|
||||||
containerStyle?: JSX.CSSProperties;
|
containerStyle?: JSX.CSSProperties;
|
||||||
}> = (p) => {
|
}> = (p) => {
|
||||||
const def = p.defaultSelected ? p.defaultSelected : p.tabs[0].title;
|
const defaultSelected = p.defaultSelected ? p.defaultSelected : p.tabs[0].title;
|
||||||
const [selected, set] = p.localStorageKey
|
const [selected, set] = p.localStorageKey
|
||||||
? useLocalStorage(def, p.localStorageKey)
|
? useLocalStorage(defaultSelected, p.localStorageKey)
|
||||||
: createSignal(def);
|
: createSignal(defaultSelected);
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (p.tabs.filter((tab) => tab.title === selected())[0] === undefined) {
|
if (p.tabs.filter((tab) => tab.title === selected())[0] === undefined) {
|
||||||
set(p.tabs[0].title);
|
set(p.tabs[0].title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return <ControlledTabs selected={selected} set={set} {...p} />;
|
return <ControlledSimpleTabs selected={selected} set={set} {...p} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ControlledTabs: Component<{
|
export const ControlledSimpleTabs: Component<{
|
||||||
tabs: Tab[];
|
tabs: Tab[];
|
||||||
selected: Accessor<string>;
|
selected: Accessor<string>;
|
||||||
set: LocalStorageSetter<string>;
|
set: LocalStorageSetter<string>;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
convertTsMsToLocalUnixTsInSecs,
|
convertTsMsToLocalUnixTsInSecs,
|
||||||
get_to_one_sec_divisor,
|
get_to_one_sec_divisor,
|
||||||
} from "../../util/helpers";
|
} from "../../util/helpers";
|
||||||
import { useLocalStorage, useLocalStorageToggle } from "../../util/hooks";
|
import { useLocalStorageToggle } from "../../util/hooks";
|
||||||
import Flex from "../shared/layout/Flex";
|
import Flex from "../shared/layout/Flex";
|
||||||
import Grid from "../shared/layout/Grid";
|
import Grid from "../shared/layout/Grid";
|
||||||
import LightweightChart, { LightweightValue } from "../shared/LightweightChart";
|
import LightweightChart, { LightweightValue } from "../shared/LightweightChart";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { createContext, createResource, ParentComponent, Resource, useContext }
|
|||||||
import { useWindowKeyDown } from "../util/hooks";
|
import { useWindowKeyDown } from "../util/hooks";
|
||||||
import {
|
import {
|
||||||
useBuilds,
|
useBuilds,
|
||||||
|
useBuildStats,
|
||||||
useDeployments,
|
useDeployments,
|
||||||
useGroups,
|
useGroups,
|
||||||
useProcedures,
|
useProcedures,
|
||||||
@@ -19,6 +20,7 @@ import connectToWs from "./ws";
|
|||||||
import { useUser } from "./UserProvider";
|
import { useUser } from "./UserProvider";
|
||||||
import { AwsBuilderConfig, PermissionLevel, UpdateTarget } from "../types";
|
import { AwsBuilderConfig, PermissionLevel, UpdateTarget } from "../types";
|
||||||
import { client } from "..";
|
import { client } from "..";
|
||||||
|
import { BuildStatsResponse } from "../util/client_types";
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
usernames: ReturnType<typeof useUsernames>;
|
usernames: ReturnType<typeof useUsernames>;
|
||||||
@@ -43,6 +45,7 @@ export type State = {
|
|||||||
docker_organizations: Resource<string[]>;
|
docker_organizations: Resource<string[]>;
|
||||||
github_webhook_base_url: Resource<string>;
|
github_webhook_base_url: Resource<string>;
|
||||||
name_from_update_target: (target: UpdateTarget) => string;
|
name_from_update_target: (target: UpdateTarget) => string;
|
||||||
|
build_stats: ReturnType<typeof useBuildStats>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = createContext<
|
const context = createContext<
|
||||||
@@ -93,6 +96,7 @@ export const AppStateProvider: ParentComponent = (p) => {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
builds,
|
builds,
|
||||||
|
build_stats: useBuildStats(),
|
||||||
getPermissionOnBuild: (id: string) => {
|
getPermissionOnBuild: (id: string) => {
|
||||||
const build = builds.get(id)!;
|
const build = builds.get(id)!;
|
||||||
const permissions = build.permissions![userId] as
|
const permissions = build.permissions![userId] as
|
||||||
@@ -161,7 +165,7 @@ export const AppStateProvider: ParentComponent = (p) => {
|
|||||||
} else {
|
} else {
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// createEffect(() => {
|
// createEffect(() => {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
intoCollection,
|
intoCollection,
|
||||||
keepOnlyInObj,
|
keepOnlyInObj,
|
||||||
} from "../util/helpers";
|
} from "../util/helpers";
|
||||||
|
import { BuildStatsResponse } from "../util/client_types";
|
||||||
|
|
||||||
type Collection<T> = Record<string, T>;
|
type Collection<T> = Record<string, T>;
|
||||||
|
|
||||||
@@ -245,6 +246,26 @@ export function useBuilds() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let build_stats_loading = false;
|
||||||
|
export function useBuildStats() {
|
||||||
|
const [stats, set] = createSignal<BuildStatsResponse>();
|
||||||
|
const reload = () => {
|
||||||
|
client.get_build_stats().then(set);
|
||||||
|
};
|
||||||
|
const get = () => {
|
||||||
|
if (stats()) {
|
||||||
|
return stats();
|
||||||
|
} else if (!build_stats_loading) {
|
||||||
|
build_stats_loading = true;
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
get,
|
||||||
|
reload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const deploymentIdPath = ["deployment", "_id", "$oid"];
|
const deploymentIdPath = ["deployment", "_id", "$oid"];
|
||||||
|
|
||||||
export function useDeployments() {
|
export function useDeployments() {
|
||||||
|
|||||||
@@ -63,7 +63,15 @@ function connectToWs(state: State) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleMessage(
|
async function handleMessage(
|
||||||
{ deployments, builds, servers, groups, procedures, updates }: State,
|
{
|
||||||
|
deployments,
|
||||||
|
builds,
|
||||||
|
servers,
|
||||||
|
groups,
|
||||||
|
procedures,
|
||||||
|
updates,
|
||||||
|
build_stats,
|
||||||
|
}: State,
|
||||||
update: Update
|
update: Update
|
||||||
) {
|
) {
|
||||||
updates.addOrUpdate(update);
|
updates.addOrUpdate(update);
|
||||||
@@ -135,13 +143,16 @@ async function handleMessage(
|
|||||||
if (update.status === UpdateStatus.Complete) {
|
if (update.status === UpdateStatus.Complete) {
|
||||||
builds.delete(update.target.id!);
|
builds.delete(update.target.id!);
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (update.operation === Operation.UpdateBuild) {
|
||||||
[Operation.UpdateBuild, Operation.BuildBuild].includes(update.operation)
|
|
||||||
) {
|
|
||||||
if (update.status === UpdateStatus.Complete) {
|
if (update.status === UpdateStatus.Complete) {
|
||||||
const build = await client.get_build(update.target.id!);
|
const build = await client.get_build(update.target.id!);
|
||||||
builds.update(build);
|
builds.update(build);
|
||||||
}
|
}
|
||||||
|
} else if (update.operation === Operation.BuildBuild) {
|
||||||
|
if (update.status === UpdateStatus.Complete) {
|
||||||
|
build_stats.reload();
|
||||||
|
client.get_build(update.target.id!).then((build) => builds.update(build));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// server
|
// server
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ svg {
|
|||||||
background-color: rgba(c.$blue, 0.8);
|
background-color: rgba(c.$blue, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blue.opaque {
|
||||||
|
background-color: c.$blue;
|
||||||
|
}
|
||||||
|
|
||||||
.blue:hover {
|
.blue:hover {
|
||||||
background-color: c.$lightblue;
|
background-color: c.$lightblue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,11 @@ export interface Deployment {
|
|||||||
permissions?: PermissionsMap;
|
permissions?: PermissionsMap;
|
||||||
skip_secret_interp?: boolean;
|
skip_secret_interp?: boolean;
|
||||||
docker_run_args: DockerRunArgs;
|
docker_run_args: DockerRunArgs;
|
||||||
|
term_signal_labels?: TerminationSignalLabel[];
|
||||||
|
termination_signal?: TerminationSignal;
|
||||||
|
termination_timeout?: number;
|
||||||
build_id?: string;
|
build_id?: string;
|
||||||
|
redeploy_on_build?: boolean;
|
||||||
build_version?: Version;
|
build_version?: Version;
|
||||||
repo?: string;
|
repo?: string;
|
||||||
branch?: string;
|
branch?: string;
|
||||||
@@ -134,6 +138,11 @@ export interface DeploymentActionState {
|
|||||||
renaming: boolean;
|
renaming: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TerminationSignalLabel {
|
||||||
|
signal: TerminationSignal;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DockerRunArgs {
|
export interface DockerRunArgs {
|
||||||
image: string;
|
image: string;
|
||||||
ports?: Conversion[];
|
ports?: Conversion[];
|
||||||
@@ -412,6 +421,13 @@ export enum RestartMode {
|
|||||||
UnlessStopped = "unless-stopped",
|
UnlessStopped = "unless-stopped",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum TerminationSignal {
|
||||||
|
SigHup = "SIGHUP",
|
||||||
|
SigInt = "SIGINT",
|
||||||
|
SigQuit = "SIGQUIT",
|
||||||
|
SigTerm = "SIGTERM",
|
||||||
|
}
|
||||||
|
|
||||||
export enum AccountType {
|
export enum AccountType {
|
||||||
Github = "github",
|
Github = "github",
|
||||||
Docker = "docker",
|
Docker = "docker",
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import {
|
|||||||
UserCredentials,
|
UserCredentials,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
import {
|
import {
|
||||||
|
BuildStatsQuery,
|
||||||
|
BuildStatsResponse,
|
||||||
BuildVersionsQuery,
|
BuildVersionsQuery,
|
||||||
CopyBuildBody,
|
CopyBuildBody,
|
||||||
CopyDeploymentBody,
|
CopyDeploymentBody,
|
||||||
@@ -44,6 +46,7 @@ import {
|
|||||||
ModifyUserEnabledBody,
|
ModifyUserEnabledBody,
|
||||||
PermissionsUpdateBody,
|
PermissionsUpdateBody,
|
||||||
RenameDeploymentBody,
|
RenameDeploymentBody,
|
||||||
|
StopContainerQuery,
|
||||||
UpdateDescriptionBody,
|
UpdateDescriptionBody,
|
||||||
} from "./client_types";
|
} from "./client_types";
|
||||||
import { generateQuery, QueryObject } from "./helpers";
|
import { generateQuery, QueryObject } from "./helpers";
|
||||||
@@ -217,20 +220,20 @@ export class Client {
|
|||||||
return this.post(`/api/deployment/${deployment_id}/pull`);
|
return this.post(`/api/deployment/${deployment_id}/pull`);
|
||||||
}
|
}
|
||||||
|
|
||||||
deploy_container(deployment_id: string): Promise<Update> {
|
deploy_container(deployment_id: string, query?: StopContainerQuery): Promise<Update> {
|
||||||
return this.post(`/api/deployment/${deployment_id}/deploy`);
|
return this.post(`/api/deployment/${deployment_id}/deploy${generateQuery(query as any)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
start_container(deployment_id: string): Promise<Update> {
|
start_container(deployment_id: string): Promise<Update> {
|
||||||
return this.post(`/api/deployment/${deployment_id}/start_container`);
|
return this.post(`/api/deployment/${deployment_id}/start_container`);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_container(deployment_id: string): Promise<Update> {
|
stop_container(deployment_id: string, query?: StopContainerQuery): Promise<Update> {
|
||||||
return this.post(`/api/deployment/${deployment_id}/stop_container`);
|
return this.post(`/api/deployment/${deployment_id}/stop_container${generateQuery(query as any)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_container(deployment_id: string): Promise<Update> {
|
remove_container(deployment_id: string, query?: StopContainerQuery): Promise<Update> {
|
||||||
return this.post(`/api/deployment/${deployment_id}/remove_container`);
|
return this.post(`/api/deployment/${deployment_id}/remove_container${generateQuery(query as any)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async download_container_log(
|
async download_container_log(
|
||||||
@@ -401,6 +404,10 @@ export class Client {
|
|||||||
return this.get(`/api/build/${id}/versions${generateQuery(query as any)}`);
|
return this.get(`/api/build/${id}/versions${generateQuery(query as any)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_build_stats(query?: BuildStatsQuery): Promise<BuildStatsResponse> {
|
||||||
|
return this.get(`/api/build/stats${generateQuery(query as any)}`);
|
||||||
|
}
|
||||||
|
|
||||||
create_build(body: CreateBuildBody): Promise<Build> {
|
create_build(body: CreateBuildBody): Promise<Build> {
|
||||||
return this.post("/api/build/create", body);
|
return this.post("/api/build/create", body);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Generated by typeshare 1.0.0
|
Generated by typeshare 1.0.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { PermissionLevel, PermissionsTarget, UpdateTarget } from "../types";
|
import { PermissionLevel, PermissionsTarget, TerminationSignal, UpdateTarget } from "../types";
|
||||||
|
|
||||||
export interface CreateBuildBody {
|
export interface CreateBuildBody {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -19,6 +19,22 @@ export interface BuildVersionsQuery {
|
|||||||
patch?: number;
|
patch?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BuildStatsQuery {
|
||||||
|
page?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuildStatsResponse {
|
||||||
|
total_time: number;
|
||||||
|
total_count: number;
|
||||||
|
days: BuildStatsDay[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuildStatsDay {
|
||||||
|
time: number;
|
||||||
|
count: number;
|
||||||
|
ts: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateDeploymentBody {
|
export interface CreateDeploymentBody {
|
||||||
name: string;
|
name: string;
|
||||||
server_id: string;
|
server_id: string;
|
||||||
@@ -37,6 +53,11 @@ export interface GetContainerLogQuery {
|
|||||||
tail?: number;
|
tail?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface StopContainerQuery {
|
||||||
|
stop_signal?: TerminationSignal;
|
||||||
|
stop_time?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CreateGroupBody {
|
export interface CreateGroupBody {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "db_client"
|
name = "db_client"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_helpers"
|
name = "monitor_helpers"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "helpers used as dependency for mogh tech monitor"
|
description = "helpers used as dependency for mogh tech monitor"
|
||||||
|
|||||||
@@ -1,26 +1,9 @@
|
|||||||
use std::{borrow::Borrow, net::SocketAddr, str::FromStr};
|
use std::{net::SocketAddr, str::FromStr};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use types::Log;
|
use types::Log;
|
||||||
|
|
||||||
pub fn parse_comma_seperated_list<T: FromStr>(
|
|
||||||
comma_sep_list: impl Borrow<str>,
|
|
||||||
) -> anyhow::Result<Vec<T>> {
|
|
||||||
comma_sep_list
|
|
||||||
.borrow()
|
|
||||||
.split(",")
|
|
||||||
.filter(|item| item.len() > 0)
|
|
||||||
.map(|item| {
|
|
||||||
let item = item
|
|
||||||
.parse()
|
|
||||||
.map_err(|_| anyhow!("error parsing string {item} into type T"))?;
|
|
||||||
Ok::<T, anyhow::Error>(item)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_socket_addr(port: u16) -> SocketAddr {
|
pub fn get_socket_addr(port: u16) -> SocketAddr {
|
||||||
SocketAddr::from_str(&format!("0.0.0.0:{}", port)).expect("failed to parse socket addr")
|
SocketAddr::from_str(&format!("0.0.0.0:{}", port)).expect("failed to parse socket addr")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_client"
|
name = "monitor_client"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "a client to interact with the monitor system"
|
description = "a client to interact with the monitor system"
|
||||||
@@ -9,7 +9,8 @@ license = "GPL-3.0-or-later"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
monitor_types = "0.3.0"
|
monitor_types = "0.3.4"
|
||||||
|
# monitor_types = { path = "../types" }
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||||
tokio = { version = "1.25", features = ["full"] }
|
tokio = { version = "1.25", features = ["full"] }
|
||||||
|
|||||||
110
lib/monitor_client/README.md
Normal file
110
lib/monitor_client/README.md
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# monitor client
|
||||||
|
|
||||||
|
## *interact with the monitor system programatically*
|
||||||
|
|
||||||
|
with this crate you can leverage all the functionality of monitor through rust code. for example, you can...
|
||||||
|
|
||||||
|
- create and manage complex deployments
|
||||||
|
- execute builds on specific schedules
|
||||||
|
- monitor server stats programatically
|
||||||
|
- program response actions to monitor updates sent over websocket
|
||||||
|
|
||||||
|
## initialize the client
|
||||||
|
|
||||||
|
you can initialize the client by directly passing args to the various initializers:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use monitor_client::MonitorClient;
|
||||||
|
|
||||||
|
let MONITOR_URL: &str = "https://monitor.mogh.tech";
|
||||||
|
|
||||||
|
let monitor = MonitorClient::new_with_token(MONITOR_URL, jwt_token).await?; // pass a valid jwt
|
||||||
|
let monitor = MonitorClient::new_with_password(MONITOR_URL, username, password).await?; // pass local user credentials
|
||||||
|
let monitor = MonitorClient::new_with_secret(MONITOR_URL, username, secret).await?; // pass api secret
|
||||||
|
```
|
||||||
|
|
||||||
|
or from the application environment / dotenv:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
MONITOR_URL=https://monitor.mogh.tech # required
|
||||||
|
MONITOR_TOKEN=<jwt> # optional. pass the jwt directly.
|
||||||
|
MONITOR_USERNAME=<username> # required for password / secret login
|
||||||
|
MONITOR_PASSWORD=<password> # the users password
|
||||||
|
MONITOR_SECRET=<secret> # the api secret
|
||||||
|
```
|
||||||
|
|
||||||
|
to log in, you must pass either
|
||||||
|
1. MONITOR_TOKEN
|
||||||
|
2. MONITOR_USERNAME and MONITOR_PASSWORD
|
||||||
|
3. MONITOR_USERNAME and MONITOR_SECRET
|
||||||
|
|
||||||
|
you can then initialize the client using this method:
|
||||||
|
```rust
|
||||||
|
let monitor = MonitorClient::new_from_env().await?;
|
||||||
|
```
|
||||||
|
|
||||||
|
## use the client
|
||||||
|
|
||||||
|
the following will select a server, build monitor core on it, and deploy it.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let server = monitor
|
||||||
|
.list_servers(None)
|
||||||
|
.await?
|
||||||
|
.pop()
|
||||||
|
.ok_or(anyhow!("no servers"))?;
|
||||||
|
|
||||||
|
let build = BuildBuilder::default()
|
||||||
|
.name("monitor_core".into())
|
||||||
|
.server_id(server.server.id.clone().into())
|
||||||
|
.repo("mbecker20/monitor".to_string().into())
|
||||||
|
.branch("main".to_string().into())
|
||||||
|
.docker_build_args(
|
||||||
|
DockerBuildArgs {
|
||||||
|
build_path: ".".into(),
|
||||||
|
dockerfile_path: "core/Dockerfile".to_string().into(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.pre_build(
|
||||||
|
Command {
|
||||||
|
path: "frontend".into(),
|
||||||
|
command: "yarn && yarn build".into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
let build = monitor.create_full_build(&build).await?;
|
||||||
|
|
||||||
|
println!("{build:#?}");
|
||||||
|
|
||||||
|
let build_update = monitor.build(&build.id).await?;
|
||||||
|
|
||||||
|
println!("{build_update:#?}");
|
||||||
|
|
||||||
|
let deployment = DeploymentBuilder::default()
|
||||||
|
.name("monitor_core_1".into())
|
||||||
|
.server_id(server.server.id.clone())
|
||||||
|
.build_id(build.id.clone().into())
|
||||||
|
.docker_run_args(
|
||||||
|
DockerRunArgsBuilder::default()
|
||||||
|
.volumes(vec![Conversion {
|
||||||
|
local: "/home/max/.monitor/core.config.toml".into(),
|
||||||
|
container: "/config/config.toml".into(),
|
||||||
|
}])
|
||||||
|
.build()?,
|
||||||
|
)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
let deployment = monitor.create_full_deployment(&deployment).await?;
|
||||||
|
|
||||||
|
println!("{deployment:#?}");
|
||||||
|
|
||||||
|
let deploy_update = monitor.deploy_container(&deployment.id).await?;
|
||||||
|
|
||||||
|
println!("{deploy_update:#?}");
|
||||||
|
```
|
||||||
|
|
||||||
|
note. this crate re-exports the [monitor types](https://crates.io/crates/monitor_types) crate under the module "types"
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# monitor client
|
|
||||||
|
|
||||||
## *interact with the monitor system programatically*
|
|
||||||
|
|
||||||
with this crate you can leverage all the functionality of monitor through rust code. for example, you can...
|
|
||||||
|
|
||||||
- create and manage complex deployment cycles
|
|
||||||
- execute builds on specific schedules
|
|
||||||
- monitor server stats programatically
|
|
||||||
- program response actions to monitor updates sent over websocket
|
|
||||||
|
|
||||||
this crate re-exports the [monitor types](https://crates.io/crates/monitor_types) crate under the module "types"
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "periphery_client"
|
name = "periphery_client"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::PeripheryClient;
|
|||||||
impl PeripheryClient {
|
impl PeripheryClient {
|
||||||
pub async fn build(&self, server: &Server, build: &Build) -> anyhow::Result<Option<Vec<Log>>> {
|
pub async fn build(&self, server: &Server, build: &Build) -> anyhow::Result<Option<Vec<Log>>> {
|
||||||
let res = self
|
let res = self
|
||||||
.post_json::<_, Vec<Log>>(server, "/build", build)
|
.post_json(server, "/build", build, ())
|
||||||
.await
|
.await
|
||||||
.context("failed to build image on periphery");
|
.context("failed to build image on periphery");
|
||||||
match res {
|
match res {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::PeripheryClient;
|
|||||||
|
|
||||||
impl PeripheryClient {
|
impl PeripheryClient {
|
||||||
pub async fn run_command(&self, server: &Server, command: &Command) -> anyhow::Result<Log> {
|
pub async fn run_command(&self, server: &Server, command: &Command) -> anyhow::Result<Log> {
|
||||||
self.post_json(server, &format!("/command"), command)
|
self.post_json(server, &format!("/command"), command, ())
|
||||||
.await
|
.await
|
||||||
.context("failed to run command on periphery")
|
.context("failed to run command on periphery")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use types::{BasicContainerInfo, Deployment, DockerContainerStats, Log, Server};
|
use types::{BasicContainerInfo, Deployment, DockerContainerStats, Log, Server, TerminationSignal};
|
||||||
|
|
||||||
use crate::PeripheryClient;
|
use crate::PeripheryClient;
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@ impl PeripheryClient {
|
|||||||
server,
|
server,
|
||||||
"/container/start",
|
"/container/start",
|
||||||
&json!({ "name": container_name }),
|
&json!({ "name": container_name }),
|
||||||
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to start container on periphery")
|
.context("failed to start container on periphery")
|
||||||
@@ -46,11 +47,14 @@ impl PeripheryClient {
|
|||||||
&self,
|
&self,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
container_name: &str,
|
container_name: &str,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Log> {
|
) -> anyhow::Result<Log> {
|
||||||
self.post_json(
|
self.post_json(
|
||||||
server,
|
server,
|
||||||
"/container/stop",
|
"/container/stop",
|
||||||
&json!({ "name": container_name }),
|
&json!({ "name": container_name }),
|
||||||
|
(("stop_signal", stop_signal), ("stop_time", stop_time)),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to stop container on periphery")
|
.context("failed to stop container on periphery")
|
||||||
@@ -60,11 +64,14 @@ impl PeripheryClient {
|
|||||||
&self,
|
&self,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
container_name: &str,
|
container_name: &str,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> anyhow::Result<Log> {
|
) -> anyhow::Result<Log> {
|
||||||
self.post_json(
|
self.post_json(
|
||||||
server,
|
server,
|
||||||
"/container/remove",
|
"/container/remove",
|
||||||
&json!({ "name": container_name }),
|
&json!({ "name": container_name }),
|
||||||
|
(("stop_signal", stop_signal), ("stop_time", stop_time)),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to remove container on periphery")
|
.context("failed to remove container on periphery")
|
||||||
@@ -80,19 +87,31 @@ impl PeripheryClient {
|
|||||||
server,
|
server,
|
||||||
"/container/rename",
|
"/container/rename",
|
||||||
&json!({ "curr_name": curr_name, "new_name": new_name }),
|
&json!({ "curr_name": curr_name, "new_name": new_name }),
|
||||||
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to rename container on periphery")
|
.context("failed to rename container on periphery")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn deploy(&self, server: &Server, deployment: &Deployment) -> anyhow::Result<Log> {
|
pub async fn deploy(
|
||||||
self.post_json(server, "/container/deploy", deployment)
|
&self,
|
||||||
.await
|
server: &Server,
|
||||||
.context("failed to deploy container on periphery")
|
deployment: &Deployment,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
|
) -> anyhow::Result<Log> {
|
||||||
|
self.post_json(
|
||||||
|
server,
|
||||||
|
"/container/deploy",
|
||||||
|
deployment,
|
||||||
|
(("stop_signal", stop_signal), ("stop_time", stop_time)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("failed to deploy container on periphery")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn container_prune(&self, server: &Server) -> anyhow::Result<Log> {
|
pub async fn container_prune(&self, server: &Server) -> anyhow::Result<Log> {
|
||||||
self.post_json(server, "/container/prune", &json!({}))
|
self.post_json(server, "/container/prune", &json!({}), ())
|
||||||
.await
|
.await
|
||||||
.context("failed to prune containers on periphery")
|
.context("failed to prune containers on periphery")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ impl PeripheryClient {
|
|||||||
clone_args: impl Into<CloneArgs>,
|
clone_args: impl Into<CloneArgs>,
|
||||||
) -> anyhow::Result<Vec<Log>> {
|
) -> anyhow::Result<Vec<Log>> {
|
||||||
let clone_args: CloneArgs = clone_args.into();
|
let clone_args: CloneArgs = clone_args.into();
|
||||||
self.post_json(server, "/git/clone", &clone_args)
|
self.post_json(server, "/git/clone", &clone_args, ())
|
||||||
.await
|
.await
|
||||||
.context("failed to clone repo on periphery")
|
.context("failed to clone repo on periphery")
|
||||||
}
|
}
|
||||||
@@ -27,13 +27,14 @@ impl PeripheryClient {
|
|||||||
server,
|
server,
|
||||||
"/git/pull",
|
"/git/pull",
|
||||||
&json!({ "name": name, "branch": branch, "on_pull": on_pull }),
|
&json!({ "name": name, "branch": branch, "on_pull": on_pull }),
|
||||||
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to pull repo on periphery")
|
.context("failed to pull repo on periphery")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_repo(&self, server: &Server, build_name: &str) -> anyhow::Result<Log> {
|
pub async fn delete_repo(&self, server: &Server, build_name: &str) -> anyhow::Result<Log> {
|
||||||
self.post_json(server, "/git/delete", &json!({ "name": build_name }))
|
self.post_json(server, "/git/delete", &json!({ "name": build_name }), ())
|
||||||
.await
|
.await
|
||||||
.context("failed to delete repo on periphery")
|
.context("failed to delete repo on periphery")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ impl PeripheryClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn image_prune(&self, server: &Server) -> anyhow::Result<Log> {
|
pub async fn image_prune(&self, server: &Server) -> anyhow::Result<Log> {
|
||||||
self.post_json(server, &format!("/image/prune"), &())
|
self.post_json(server, &format!("/image/prune"), &(), ())
|
||||||
.await
|
.await
|
||||||
.context("failed to prune images on periphery")
|
.context("failed to prune images on periphery")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,17 +170,19 @@ impl PeripheryClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_json<B: Serialize, R: DeserializeOwned>(
|
async fn post_json<B: Serialize, R: DeserializeOwned, Q: Serialize>(
|
||||||
&self,
|
&self,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
endpoint: &str,
|
endpoint: &str,
|
||||||
body: &B,
|
body: &B,
|
||||||
|
query: impl Into<Option<Q>>,
|
||||||
) -> anyhow::Result<R> {
|
) -> anyhow::Result<R> {
|
||||||
self.health_check(server).await?;
|
self.health_check(server).await?;
|
||||||
let res = self
|
let res = self
|
||||||
.http_client
|
.http_client
|
||||||
.post(format!("{}{endpoint}", server.address))
|
.post(format!("{}{endpoint}", server.address))
|
||||||
.header("authorization", &self.passkey)
|
.header("authorization", &self.passkey)
|
||||||
|
.query(&query.into())
|
||||||
.json(body)
|
.json(body)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -24,19 +24,20 @@ impl PeripheryClient {
|
|||||||
"name": name,
|
"name": name,
|
||||||
"driver": driver
|
"driver": driver
|
||||||
}),
|
}),
|
||||||
|
(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("failed to create network on periphery")
|
.context("failed to create network on periphery")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn network_delete(&self, server: &Server, name: &str) -> anyhow::Result<Log> {
|
pub async fn network_delete(&self, server: &Server, name: &str) -> anyhow::Result<Log> {
|
||||||
self.post_json(server, "/network/delete", &json!({ "name": name }))
|
self.post_json(server, "/network/delete", &json!({ "name": name }), ())
|
||||||
.await
|
.await
|
||||||
.context("failed to delete network on periphery")
|
.context("failed to delete network on periphery")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn network_prune(&self, server: &Server) -> anyhow::Result<Log> {
|
pub async fn network_prune(&self, server: &Server) -> anyhow::Result<Log> {
|
||||||
self.post_json(server, "/network/prune", &json!({}))
|
self.post_json(server, "/network/prune", &json!({}), ())
|
||||||
.await
|
.await
|
||||||
.context("failed to prune networks on periphery")
|
.context("failed to prune networks on periphery")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_types"
|
name = "monitor_types"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "types for the mogh tech monitor"
|
description = "types for the mogh tech monitor"
|
||||||
|
|||||||
@@ -46,10 +46,30 @@ pub struct Deployment {
|
|||||||
#[diff(attr(#[serde(skip_serializing_if = "docker_run_args_diff_no_change")]))]
|
#[diff(attr(#[serde(skip_serializing_if = "docker_run_args_diff_no_change")]))]
|
||||||
pub docker_run_args: DockerRunArgs,
|
pub docker_run_args: DockerRunArgs,
|
||||||
|
|
||||||
|
#[serde(default = "default_term_signal_labels")]
|
||||||
|
#[builder(default = "vec![TerminationSignalLabel::default()]")]
|
||||||
|
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||||
|
pub term_signal_labels: Vec<TerminationSignalLabel>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
#[builder(default)]
|
||||||
|
#[diff(attr(#[serde(skip_serializing_if = "termination_signal_diff_no_change")]))]
|
||||||
|
pub termination_signal: TerminationSignal,
|
||||||
|
|
||||||
|
#[serde(default = "default_termination_timeout")]
|
||||||
|
#[builder(default = "10")]
|
||||||
|
#[diff(attr(#[serde(skip_serializing_if = "i32_diff_no_change")]))]
|
||||||
|
pub termination_timeout: i32,
|
||||||
|
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||||
pub build_id: Option<String>,
|
pub build_id: Option<String>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
#[builder(default)]
|
||||||
|
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||||
|
pub redeploy_on_build: bool,
|
||||||
|
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||||
pub build_version: Option<Version>,
|
pub build_version: Option<Version>,
|
||||||
@@ -89,6 +109,14 @@ pub struct Deployment {
|
|||||||
pub updated_at: String,
|
pub updated_at: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_termination_timeout() -> i32 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_term_signal_labels() -> Vec<TerminationSignalLabel> {
|
||||||
|
vec![TerminationSignalLabel::default()]
|
||||||
|
}
|
||||||
|
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
pub struct DeploymentWithContainerState {
|
pub struct DeploymentWithContainerState {
|
||||||
@@ -110,6 +138,18 @@ pub struct DeploymentActionState {
|
|||||||
pub renaming: bool,
|
pub renaming: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[typeshare]
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, Diff, Builder)]
|
||||||
|
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||||
|
pub struct TerminationSignalLabel {
|
||||||
|
#[builder(default)]
|
||||||
|
#[diff(attr(#[serde(skip_serializing_if = "termination_signal_diff_no_change")]))]
|
||||||
|
pub signal: TerminationSignal,
|
||||||
|
#[builder(default)]
|
||||||
|
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||||
|
pub label: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Diff, Builder)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Diff, Builder)]
|
||||||
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||||
@@ -243,10 +283,22 @@ impl Default for DockerContainerState {
|
|||||||
|
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize, Deserialize, Debug, Display, EnumString, PartialEq, Hash, Eq, Clone, Copy, Diff,
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Debug,
|
||||||
|
Display,
|
||||||
|
EnumString,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Eq,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Diff,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||||
pub enum RestartMode {
|
pub enum RestartMode {
|
||||||
|
#[default]
|
||||||
#[serde(rename = "no")]
|
#[serde(rename = "no")]
|
||||||
#[strum(serialize = "no")]
|
#[strum(serialize = "no")]
|
||||||
NoRestart,
|
NoRestart,
|
||||||
@@ -261,8 +313,32 @@ pub enum RestartMode {
|
|||||||
UnlessStopped,
|
UnlessStopped,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RestartMode {
|
#[typeshare]
|
||||||
fn default() -> RestartMode {
|
#[derive(
|
||||||
RestartMode::NoRestart
|
Serialize,
|
||||||
}
|
Deserialize,
|
||||||
|
Debug,
|
||||||
|
Display,
|
||||||
|
EnumString,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Eq,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Diff,
|
||||||
|
Default,
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
|
#[strum(serialize_all = "UPPERCASE")]
|
||||||
|
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||||
|
pub enum TerminationSignal {
|
||||||
|
#[serde(alias = "1")]
|
||||||
|
SigHup,
|
||||||
|
#[serde(alias = "2")]
|
||||||
|
SigInt,
|
||||||
|
#[serde(alias = "3")]
|
||||||
|
SigQuit,
|
||||||
|
#[default]
|
||||||
|
#[serde(alias = "15")]
|
||||||
|
SigTerm,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use diff::{Diff, OptionDiff, VecDiff};
|
use diff::{Diff, OptionDiff, VecDiff};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
deployment::{DockerRunArgsDiff, RestartModeDiff},
|
deployment::{DockerRunArgsDiff, RestartModeDiff, TerminationSignalDiff},
|
||||||
TimelengthDiff,
|
TimelengthDiff,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -13,6 +13,10 @@ pub fn f32_diff_no_change(f32_diff: &f32) -> bool {
|
|||||||
*f32_diff == 0.0
|
*f32_diff == 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn i32_diff_no_change(i32_diff: &i32) -> bool {
|
||||||
|
*i32_diff == 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn option_diff_no_change<T: Diff>(option_diff: &OptionDiff<T>) -> bool
|
pub fn option_diff_no_change<T: Diff>(option_diff: &OptionDiff<T>) -> bool
|
||||||
where
|
where
|
||||||
<T as Diff>::Repr: PartialEq,
|
<T as Diff>::Repr: PartialEq,
|
||||||
@@ -24,7 +28,7 @@ pub fn vec_diff_no_change<T: Diff>(vec_diff: &VecDiff<T>) -> bool {
|
|||||||
vec_diff.0.is_empty()
|
vec_diff.0.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn hashmap_diff_no_change<T: Diff>(hashmap_diff: &HashMapDiff<String, T>) -> bool {
|
// pub fn hashmap_diff_no_change<K: Hash + Eq, T: Diff>(hashmap_diff: &HashMapDiff<K, T>) -> bool {
|
||||||
// hashmap_diff.altered.is_empty() && hashmap_diff.removed.is_empty()
|
// hashmap_diff.altered.is_empty() && hashmap_diff.removed.is_empty()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@@ -48,3 +52,7 @@ pub fn restart_mode_diff_no_change(restart_mode: &RestartModeDiff) -> bool {
|
|||||||
pub fn timelength_diff_no_change(timelength: &TimelengthDiff) -> bool {
|
pub fn timelength_diff_no_change(timelength: &TimelengthDiff) -> bool {
|
||||||
timelength == &TimelengthDiff::NoChange
|
timelength == &TimelengthDiff::NoChange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn termination_signal_diff_no_change(term_signal: &TerminationSignalDiff) -> bool {
|
||||||
|
term_signal == &TerminationSignalDiff::NoChange
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ::diff::Diff;
|
use ::diff::Diff;
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
use chrono::{DateTime, SecondsFormat, Utc};
|
use chrono::{DateTime, LocalResult, SecondsFormat, TimeZone, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum_macros::{Display, EnumString};
|
use strum_macros::{Display, EnumString};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
@@ -199,7 +199,18 @@ pub enum PermissionsTarget {
|
|||||||
|
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[derive(
|
#[derive(
|
||||||
Serialize, Deserialize, Debug, Display, EnumString, PartialEq, Hash, Eq, Clone, Copy, Diff,
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
Debug,
|
||||||
|
Display,
|
||||||
|
EnumString,
|
||||||
|
PartialEq,
|
||||||
|
Hash,
|
||||||
|
Eq,
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Diff,
|
||||||
|
Default,
|
||||||
)]
|
)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[strum(serialize_all = "snake_case")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
@@ -220,6 +231,7 @@ pub enum Timelength {
|
|||||||
#[serde(rename = "30-sec")]
|
#[serde(rename = "30-sec")]
|
||||||
#[strum(serialize = "30-sec")]
|
#[strum(serialize = "30-sec")]
|
||||||
ThirtySeconds,
|
ThirtySeconds,
|
||||||
|
#[default]
|
||||||
#[serde(rename = "1-min")]
|
#[serde(rename = "1-min")]
|
||||||
#[strum(serialize = "1-min")]
|
#[strum(serialize = "1-min")]
|
||||||
OneMinute,
|
OneMinute,
|
||||||
@@ -270,12 +282,6 @@ pub enum Timelength {
|
|||||||
ThirtyDays,
|
ThirtyDays,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Timelength {
|
|
||||||
fn default() -> Timelength {
|
|
||||||
Timelength::OneMinute
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn monitor_timestamp() -> String {
|
pub fn monitor_timestamp() -> String {
|
||||||
Utc::now().to_rfc3339_opts(SecondsFormat::Millis, false)
|
Utc::now().to_rfc3339_opts(SecondsFormat::Millis, false)
|
||||||
}
|
}
|
||||||
@@ -285,3 +291,11 @@ pub fn unix_from_monitor_ts(ts: &str) -> anyhow::Result<i64> {
|
|||||||
.context("failed to parse rfc3339 timestamp")?
|
.context("failed to parse rfc3339 timestamp")?
|
||||||
.timestamp_millis())
|
.timestamp_millis())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn monitor_ts_from_unix(ts: i64) -> anyhow::Result<String> {
|
||||||
|
match Utc.timestamp_millis_opt(ts) {
|
||||||
|
LocalResult::Single(dt) => Ok(dt.to_rfc3339_opts(SecondsFormat::Millis, false)),
|
||||||
|
LocalResult::None => Err(anyhow!("out of bounds timestamp passed")),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "monitor_periphery"
|
name = "monitor_periphery"
|
||||||
version = "0.3.0"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["MoghTech"]
|
authors = ["MoghTech"]
|
||||||
description = "monitor periphery binary"
|
description = "monitor periphery binary"
|
||||||
@@ -15,7 +15,7 @@ path = "src/main.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
||||||
types = { package = "monitor_types", path = "../lib/types" }
|
types = { package = "monitor_types", path = "../lib/types" }
|
||||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
run_command = { version = "0.0.6", features = ["async_tokio"] }
|
||||||
async_timing_util = "0.1.14"
|
async_timing_util = "0.1.14"
|
||||||
tokio = { version = "1.26", features = ["full"] }
|
tokio = { version = "1.26", features = ["full"] }
|
||||||
axum = { version = "0.6", features = ["ws"] }
|
axum = { version = "0.6", features = ["ws"] }
|
||||||
@@ -34,3 +34,4 @@ daemonize = "0.5.0"
|
|||||||
clap = { version = "4.2", features = ["derive"] }
|
clap = { version = "4.2", features = ["derive"] }
|
||||||
svi = "0.1.3"
|
svi = "0.1.3"
|
||||||
merge_config_files = "0.1.3"
|
merge_config_files = "0.1.3"
|
||||||
|
parse_csl = "0.1.0"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use helpers::handle_anyhow_error;
|
use helpers::handle_anyhow_error;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use types::{Deployment, Log};
|
use types::{Deployment, Log, TerminationSignal};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
helpers::{
|
helpers::{
|
||||||
@@ -32,6 +32,12 @@ struct GetLogQuery {
|
|||||||
tail: Option<u64>, // default is 1000 if not passed
|
tail: Option<u64>, // default is 1000 if not passed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct StopContainerQuery {
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn router() -> Router {
|
pub fn router() -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route(
|
.route(
|
||||||
@@ -79,15 +85,29 @@ pub fn router() -> Router {
|
|||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/stop",
|
"/stop",
|
||||||
post(|container: Json<Container>| async move {
|
post(
|
||||||
Json(docker::stop_container(&container.name).await)
|
|query: Query<StopContainerQuery>, container: Json<Container>| async move {
|
||||||
}),
|
Json(
|
||||||
|
docker::stop_container(&container.name, query.stop_signal, query.stop_time)
|
||||||
|
.await,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/remove",
|
"/remove",
|
||||||
post(|container: Json<Container>| async move {
|
post(
|
||||||
Json(docker::stop_and_remove_container(&container.name).await)
|
|query: Query<StopContainerQuery>, container: Json<Container>| async move {
|
||||||
}),
|
Json(
|
||||||
|
docker::stop_and_remove_container(
|
||||||
|
&container.name,
|
||||||
|
query.stop_signal,
|
||||||
|
query.stop_time,
|
||||||
|
)
|
||||||
|
.await,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/rename",
|
"/rename",
|
||||||
@@ -97,8 +117,8 @@ pub fn router() -> Router {
|
|||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/deploy",
|
"/deploy",
|
||||||
post(|config, deployment| async move {
|
post(|config, query, deployment| async move {
|
||||||
deploy(config, deployment)
|
deploy(config, deployment, query)
|
||||||
.await
|
.await
|
||||||
.map_err(handle_anyhow_error)
|
.map_err(handle_anyhow_error)
|
||||||
}),
|
}),
|
||||||
@@ -118,6 +138,7 @@ pub fn router() -> Router {
|
|||||||
async fn deploy(
|
async fn deploy(
|
||||||
Extension(config): PeripheryConfigExtension,
|
Extension(config): PeripheryConfigExtension,
|
||||||
Json(deployment): Json<Deployment>,
|
Json(deployment): Json<Deployment>,
|
||||||
|
Query(query): Query<StopContainerQuery>,
|
||||||
) -> anyhow::Result<Json<Log>> {
|
) -> anyhow::Result<Json<Log>> {
|
||||||
let log = match get_docker_token(&deployment.docker_run_args.docker_account, &config) {
|
let log = match get_docker_token(&deployment.docker_run_args.docker_account, &config) {
|
||||||
Ok(docker_token) => tokio::spawn(async move {
|
Ok(docker_token) => tokio::spawn(async move {
|
||||||
@@ -126,6 +147,8 @@ async fn deploy(
|
|||||||
&docker_token,
|
&docker_token,
|
||||||
config.repo_dir.clone(),
|
config.repo_dir.clone(),
|
||||||
&config.secrets,
|
&config.secrets,
|
||||||
|
query.stop_signal,
|
||||||
|
query.stop_time,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use std::sync::Arc;
|
|||||||
use axum::Extension;
|
use axum::Extension;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use helpers::parse_comma_seperated_list;
|
|
||||||
use merge_config_files::parse_config_paths;
|
use merge_config_files::parse_config_paths;
|
||||||
|
use parse_csl::parse_comma_seperated;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use types::PeripheryConfig;
|
use types::PeripheryConfig;
|
||||||
|
|
||||||
@@ -65,13 +65,13 @@ pub fn load() -> (Args, u16, PeripheryConfigExtension, HomeDirExtension) {
|
|||||||
.config_path
|
.config_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or(
|
.unwrap_or(
|
||||||
&parse_comma_seperated_list(env.config_paths)
|
&parse_comma_seperated(&env.config_paths)
|
||||||
.expect("failed to parse config paths on environment into comma seperated list"),
|
.expect("failed to parse config paths on environment into comma seperated list"),
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| p.replace("~", &home_dir))
|
.map(|p| p.replace("~", &home_dir))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let env_match_keywords = parse_comma_seperated_list(env.config_keywords)
|
let env_match_keywords = parse_comma_seperated(&env.config_keywords)
|
||||||
.expect("failed to parse environemt CONFIG_KEYWORDS into comma seperated list");
|
.expect("failed to parse environemt CONFIG_KEYWORDS into comma seperated list");
|
||||||
let match_keywords = args
|
let match_keywords = args
|
||||||
.config_keyword
|
.config_keyword
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use helpers::to_monitor_name;
|
|||||||
use run_command::async_run_command;
|
use run_command::async_run_command;
|
||||||
use types::{
|
use types::{
|
||||||
Conversion, Deployment, DockerContainerStats, DockerRunArgs, EnvironmentVar, Log, RestartMode,
|
Conversion, Deployment, DockerContainerStats, DockerRunArgs, EnvironmentVar, Log, RestartMode,
|
||||||
|
TerminationSignal,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::helpers::{docker::parse_extra_args, run_monitor_command};
|
use crate::helpers::{docker::parse_extra_args, run_monitor_command};
|
||||||
@@ -57,16 +58,69 @@ pub async fn start_container(container_name: &str) -> Log {
|
|||||||
run_monitor_command("docker start", command).await
|
run_monitor_command("docker start", command).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn stop_container(container_name: &str) -> Log {
|
pub async fn stop_container(
|
||||||
let container_name = to_monitor_name(container_name);
|
container_name: &str,
|
||||||
let command = format!("docker stop {container_name}");
|
signal: Option<TerminationSignal>,
|
||||||
run_monitor_command("docker stop", command).await
|
time: Option<i32>,
|
||||||
|
) -> Log {
|
||||||
|
let command = stop_container_command(container_name, signal, time);
|
||||||
|
let log = run_monitor_command("docker stop", command).await;
|
||||||
|
if log.stderr.contains("unknown flag: --signal") {
|
||||||
|
let command = stop_container_command(container_name, None, time);
|
||||||
|
let mut log = run_monitor_command("docker stop", command).await;
|
||||||
|
log.stderr = format!(
|
||||||
|
"old docker version: unable to use --signal flag{}",
|
||||||
|
if log.stderr.len() > 0 {
|
||||||
|
format!("\n\n{}", log.stderr)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
log
|
||||||
|
} else {
|
||||||
|
log
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn stop_and_remove_container(container_name: &str) -> Log {
|
pub async fn stop_and_remove_container(
|
||||||
|
container_name: &str,
|
||||||
|
signal: Option<TerminationSignal>,
|
||||||
|
time: Option<i32>,
|
||||||
|
) -> Log {
|
||||||
|
let stop_command = stop_container_command(container_name, signal, time);
|
||||||
|
let command = format!("{stop_command} && docker container rm {container_name}");
|
||||||
|
let log = run_monitor_command("docker stop and remove", command).await;
|
||||||
|
if log.stderr.contains("unknown flag: --signal") {
|
||||||
|
let stop_command = stop_container_command(container_name, None, time);
|
||||||
|
let command = format!("{stop_command} && docker container rm {container_name}");
|
||||||
|
let mut log = run_monitor_command("docker stop", command).await;
|
||||||
|
log.stderr = format!(
|
||||||
|
"old docker version: unable to use --signal flag{}",
|
||||||
|
if log.stderr.len() > 0 {
|
||||||
|
format!("\n\n{}", log.stderr)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
log
|
||||||
|
} else {
|
||||||
|
log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop_container_command(
|
||||||
|
container_name: &str,
|
||||||
|
signal: Option<TerminationSignal>,
|
||||||
|
time: Option<i32>,
|
||||||
|
) -> String {
|
||||||
let container_name = to_monitor_name(container_name);
|
let container_name = to_monitor_name(container_name);
|
||||||
let command = format!("docker stop {container_name} && docker container rm {container_name}");
|
let signal = signal
|
||||||
run_monitor_command("docker stop and remove", command).await
|
.map(|signal| format!(" --signal {signal}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
let time = time
|
||||||
|
.map(|time| format!(" --time {time}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
format!("docker stop{signal}{time} {container_name}")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn rename_container(curr_name: &str, new_name: &str) -> Log {
|
pub async fn rename_container(curr_name: &str, new_name: &str) -> Log {
|
||||||
@@ -86,12 +140,14 @@ pub async fn deploy(
|
|||||||
docker_token: &Option<String>,
|
docker_token: &Option<String>,
|
||||||
repo_dir: PathBuf,
|
repo_dir: PathBuf,
|
||||||
secrets: &HashMap<String, String>,
|
secrets: &HashMap<String, String>,
|
||||||
|
stop_signal: Option<TerminationSignal>,
|
||||||
|
stop_time: Option<i32>,
|
||||||
) -> Log {
|
) -> Log {
|
||||||
if let Err(e) = docker_login(&deployment.docker_run_args.docker_account, docker_token).await {
|
if let Err(e) = docker_login(&deployment.docker_run_args.docker_account, docker_token).await {
|
||||||
return Log::error("docker login", format!("{e:#?}"));
|
return Log::error("docker login", format!("{e:#?}"));
|
||||||
}
|
}
|
||||||
let _ = pull_image(&deployment.docker_run_args.image).await;
|
let _ = pull_image(&deployment.docker_run_args.image).await;
|
||||||
let _ = stop_and_remove_container(&to_monitor_name(&deployment.name)).await;
|
let _ = stop_and_remove_container(&deployment.name, stop_signal, stop_time).await;
|
||||||
let command = docker_run_command(deployment, repo_dir);
|
let command = docker_run_command(deployment, repo_dir);
|
||||||
if deployment.skip_secret_interp {
|
if deployment.skip_secret_interp {
|
||||||
run_monitor_command("docker run", command).await
|
run_monitor_command("docker run", command).await
|
||||||
|
|||||||
@@ -28,11 +28,8 @@ pub async fn pull(
|
|||||||
if on_pull.path.len() > 0 && on_pull.command.len() > 0 {
|
if on_pull.path.len() > 0 && on_pull.command.len() > 0 {
|
||||||
path.push(&on_pull.path);
|
path.push(&on_pull.path);
|
||||||
let path = path.display().to_string();
|
let path = path.display().to_string();
|
||||||
let on_pull_log = run_monitor_command(
|
let on_pull_log =
|
||||||
"on pull",
|
run_monitor_command("on pull", format!("cd {path} && {}", on_pull.command)).await;
|
||||||
format!("cd {path} && {}", on_pull.command),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
logs.push(on_pull_log);
|
logs.push(on_pull_log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
readme.md
10
readme.md
@@ -1,3 +1,13 @@
|
|||||||
# monitor 🦎
|
# monitor 🦎
|
||||||
|
|
||||||
a tool to build and deploy software across many servers. [docs](https://mbecker20.github.io/monitor)
|
a tool to build and deploy software across many servers. [docs](https://mbecker20.github.io/monitor)
|
||||||
|
|
||||||
|
## screenshots
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|||||||
BIN
screenshots/desktop-deployment.png
Normal file
BIN
screenshots/desktop-deployment.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 358 KiB |
BIN
screenshots/home-servers.png
Normal file
BIN
screenshots/home-servers.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 546 KiB |
BIN
screenshots/mobile-deployment.png
Normal file
BIN
screenshots/mobile-deployment.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
BIN
screenshots/search-menu.png
Normal file
BIN
screenshots/search-menu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 646 KiB |
@@ -6,8 +6,8 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.22", features = ["full"] }
|
|
||||||
monitor_client = { path = "../lib/monitor_client" }
|
monitor_client = { path = "../lib/monitor_client" }
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::{anyhow, Context};
|
||||||
use async_timing_util::unix_timestamp_ms;
|
use async_timing_util::unix_timestamp_ms;
|
||||||
use monitor_client::{types::Conversion, MonitorClient};
|
use monitor_client::{
|
||||||
|
types::{
|
||||||
|
BuildBuilder, Command, Conversion, Deployment, DeploymentBuilder, DockerBuildArgs,
|
||||||
|
DockerBuildArgsBuilder, DockerRunArgsBuilder, EnvironmentVar, TerminationSignal,
|
||||||
|
},
|
||||||
|
MonitorClient,
|
||||||
|
};
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod tests;
|
mod tests;
|
||||||
@@ -11,34 +17,81 @@ use tests::*;
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let monitor = config::load().await;
|
let d = DeploymentBuilder::default()
|
||||||
|
.name(String::from("test"))
|
||||||
|
.server_id(String::from("any"))
|
||||||
|
.build()?;
|
||||||
|
|
||||||
println!("\nstarting tests\n");
|
println!("{d:#?}");
|
||||||
|
|
||||||
let start_ts = unix_timestamp_ms();
|
// let monitor = config::load().await;
|
||||||
|
|
||||||
// let (server, deployment, build) = create_test_setup(&monitor, "test").await?;
|
// println!("\nstarting tests\n");
|
||||||
|
|
||||||
// let server_stats = get_server_stats(&monitor).await?;
|
// let start_ts = unix_timestamp_ms();
|
||||||
// println!("server stats:\n{server_stats:#?}\n");
|
|
||||||
|
|
||||||
// subscribe_to_server_stats(&monitor).await?;
|
// let server = monitor
|
||||||
|
// .list_servers(None)
|
||||||
|
// .await?
|
||||||
|
// .pop()
|
||||||
|
// .ok_or(anyhow!("no servers"))?;
|
||||||
|
|
||||||
// let (update, container) = deploy_mongo(&monitor).await?;
|
// let build = BuildBuilder::default()
|
||||||
// println!(
|
// .name("monitor_core".into())
|
||||||
// "mongo deploy update:\n{update:#?}\n\ncontainer: {:#?}\n",
|
// .server_id(server.server.id.clone().into())
|
||||||
// container.container
|
// .repo("mbecker20/monitor".to_string().into())
|
||||||
// );
|
// .branch("main".to_string().into())
|
||||||
|
// .docker_build_args(
|
||||||
|
// DockerBuildArgs {
|
||||||
|
// build_path: ".".into(),
|
||||||
|
// dockerfile_path: "core/Dockerfile".to_string().into(),
|
||||||
|
// ..Default::default()
|
||||||
|
// }
|
||||||
|
// .into(),
|
||||||
|
// )
|
||||||
|
// .pre_build(
|
||||||
|
// Command {
|
||||||
|
// path: "frontend".into(),
|
||||||
|
// command: "yarn && yarn build".into(),
|
||||||
|
// }
|
||||||
|
// .into(),
|
||||||
|
// )
|
||||||
|
// .build()?;
|
||||||
|
|
||||||
// let update = test_build(&monitor).await?;
|
// let build = monitor.create_full_build(&build).await?;
|
||||||
// println!("build update:\n{update:#?}");
|
|
||||||
|
|
||||||
// test_updates(&monitor).await.unwrap();
|
// println!("{build:#?}");
|
||||||
|
|
||||||
let update = test_aws_build(&monitor).await?;
|
// let build_update = monitor.build(&build.id).await?;
|
||||||
|
|
||||||
let end_ts = unix_timestamp_ms();
|
// println!("{build_update:#?}");
|
||||||
let finished_in = (end_ts - start_ts) as f64 / 1000.0;
|
|
||||||
println!("\nfinished in {finished_in} s");
|
// let deployment = DeploymentBuilder::default()
|
||||||
|
// .name("monitor_core_1".into())
|
||||||
|
// .server_id(server.server.id.clone())
|
||||||
|
// .build_id(build.id.clone().into())
|
||||||
|
// .docker_run_args(
|
||||||
|
// DockerRunArgsBuilder::default()
|
||||||
|
// .volumes(vec![Conversion {
|
||||||
|
// local: "/home/max/.monitor/core.config.toml".into(),
|
||||||
|
// container: "/config/config.toml".into(),
|
||||||
|
// }])
|
||||||
|
// .build()?,
|
||||||
|
// )
|
||||||
|
// .build()?;
|
||||||
|
|
||||||
|
// let deployment = monitor.create_full_deployment(&deployment).await?;
|
||||||
|
|
||||||
|
// println!("{deployment:#?}");
|
||||||
|
|
||||||
|
// let deploy_update = monitor.deploy_container(&deployment.id).await?;
|
||||||
|
|
||||||
|
// println!("{deploy_update:#?}");
|
||||||
|
|
||||||
|
// let update = test_aws_build(&monitor).await?;
|
||||||
|
|
||||||
|
// let end_ts = unix_timestamp_ms();
|
||||||
|
// let finished_in = (end_ts - start_ts) as f64 / 1000.0;
|
||||||
|
// println!("\nfinished in {finished_in} s");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user