mirror of
https://github.com/moghtech/komodo.git
synced 2026-03-10 15:56:18 -05:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c0be07ae1 | ||
|
|
ab945aadde | ||
|
|
c1c461c273 | ||
|
|
336742ee69 | ||
|
|
405dacce1c | ||
|
|
9acd45aa93 | ||
|
|
c889c2cc03 | ||
|
|
7ac91ef416 | ||
|
|
8e28669aa1 | ||
|
|
6cdb91f8b8 | ||
|
|
e892474713 | ||
|
|
abdae98816 | ||
|
|
ab4fe49f33 | ||
|
|
1ace35103b | ||
|
|
dbee729eee | ||
|
|
792576ce59 | ||
|
|
a07624e9b9 | ||
|
|
bb8054af8a | ||
|
|
7738f3e066 | ||
|
|
5dee16a100 | ||
|
|
35f3bcdf2f |
352
Cargo.lock
generated
352
Cargo.lock
generated
@@ -126,9 +126,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "0.55.0"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1854be4730cc87602316707045a5c0585287419d54f293bbb52a82c895d9086a"
|
||||
checksum = "fc00553f5f3c06ffd4510a9d576f92143618706c45ea6ff81e84ad9be9588abd"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-http",
|
||||
@@ -156,9 +156,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-credential-types"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4232d3729eefc287adc0d5a8adc97b7d94eefffe6bbe94312cc86c7ab6b06ce"
|
||||
checksum = "4cb57ac6088805821f78d282c0ba8aec809f11cbee10dda19a97b03ab040ccc2"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-types",
|
||||
@@ -170,9 +170,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-endpoint"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f04ab03b3f1cca91f7cccaa213056d732accb14e2e65debfacc1d28627d162"
|
||||
checksum = "9c5f6f84a4f46f95a9bb71d9300b73cd67eb868bc43ae84f66ad34752299f4ac"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
@@ -184,9 +184,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-http"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5ad8c53f7560baaf635b6aa811f3213d39b50555d100f83e43801652d4e318e"
|
||||
checksum = "a754683c322f7dc5167484266489fdebdcd04d26e53c162cad1f3f949f2c5671"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-smithy-http",
|
||||
@@ -203,9 +203,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ec2"
|
||||
version = "0.26.0"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74a7d11843d7b0234b874ed430cba258ac272aec352508371944eceb64d57dd0"
|
||||
checksum = "5ab77050ecd90bf116dcbff6a7b8b2fb294b1399900a97e870c4879fe2010827"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
@@ -231,9 +231,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "0.25.0"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c10657158e12163d6b3fb1e0c9154e43656843794a3071d9ee63ec82a4de2d"
|
||||
checksum = "babfd626348836a31785775e3c08a4c345a5ab4c6e06dfd9167f2bee0e6295d6"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
@@ -256,9 +256,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "0.25.0"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "706a308b7277ac9aac78ab37933509659c6f978a8473e95d8e7a8103093133c3"
|
||||
checksum = "2d0fbe3c2c342bc8dfea4bb43937405a8ec06f99140a0dcb9c7b59e54dfa93a1"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
@@ -282,9 +282,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sig-auth"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d77d879ab210e958ba65a6d3842969a596738c024989cd3e490cf9f9b560ec"
|
||||
checksum = "84dc92a63ede3c2cbe43529cb87ffa58763520c96c6a46ca1ced80417afba845"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-sigv4",
|
||||
@@ -296,9 +296,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sigv4"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ab4eebc8ec484fb9eab04b15a5d1e71f3dc13bee8fdd2d9ed78bcd6ecbd7192"
|
||||
checksum = "392fefab9d6fcbd76d518eb3b1c040b84728ab50f58df0c3c53ada4bea9d327e"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"form_urlencoded",
|
||||
@@ -315,9 +315,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-async"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88573bcfbe1dcfd54d4912846df028b42d6255cbf9ce07be216b1bbfd11fc4b9"
|
||||
checksum = "ae23b9fe7a07d0919000116c4c5c0578303fbce6fc8d32efca1f7759d4c20faf"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
@@ -327,9 +327,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-client"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2f52352bae50d3337d5d6151b695d31a8c10ebea113eca5bead531f8301b067"
|
||||
checksum = "5230d25d244a51339273b8870f0f77874cd4449fb4f8f629b21188ae10cfc0ba"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-http",
|
||||
@@ -351,9 +351,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-http"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03bcc02d7ed9649d855c8ce4a735e9848d7b8f7568aad0504c158e3baa955df8"
|
||||
checksum = "b60e2133beb9fe6ffe0b70deca57aaeff0a35ad24a9c6fab2fd3b4f45b99fdb5"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
"bytes",
|
||||
@@ -373,9 +373,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-http-tower"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da88b3a860f65505996c29192d800f1aeb9480440f56d63aad33a3c12045017a"
|
||||
checksum = "3a4d94f556c86a0dd916a5d7c39747157ea8cb909ca469703e20fee33e448b67"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
@@ -389,18 +389,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-json"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b0c1e87d75cac889dca2a7f5ba280da2cde8122448e7fec1d614194dfa00c70"
|
||||
checksum = "5ce3d6e6ebb00b2cce379f079ad5ec508f9bcc3a9510d9b9c1840ed1d6f8af39"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-query"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6b50d15f446c19e088009ecb00e2fb2d13133d6fe1db702e9aa67ad135bf6a6"
|
||||
checksum = "d58edfca32ef9bfbc1ca394599e17ea329cb52d6a07359827be74235b64b3298"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
"urlencoding",
|
||||
@@ -408,9 +408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-types"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0afc731fd1417d791f9145a1e0c30e23ae0beaab9b4814017708ead2fc20f1"
|
||||
checksum = "58db46fc1f4f26be01ebdb821751b4e2482cd43aa2b64a0348fb89762defaffa"
|
||||
dependencies = [
|
||||
"base64-simd",
|
||||
"itoa",
|
||||
@@ -421,18 +421,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-xml"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b5398c1c25dfc6f8c282b1552a66aa807c9d6e15e1b3a84b94aa44e7859bec3"
|
||||
checksum = "fb557fe4995bd9ec87fb244bbb254666a971dc902a783e9da8b7711610e9664c"
|
||||
dependencies = [
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-types"
|
||||
version = "0.55.1"
|
||||
version = "0.55.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9b082e329d9a304d39e193ad5c7ab363a0d6507aca6965e0673a746686fb0cc"
|
||||
checksum = "de0869598bfe46ec44ffe17e063ed33336e59df90356ca8ff0e8da6f7c1d994b"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-smithy-async",
|
||||
@@ -473,9 +473,9 @@ dependencies = [
|
||||
"sha1",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.18.0",
|
||||
"tower",
|
||||
"tower-http 0.4.0",
|
||||
"tower-http",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
@@ -497,25 +497,6 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51227033e4d3acad15c879092ac8a228532707b5db5ff2628f638334f63e1b7a"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http 0.3.5",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum_oauth2"
|
||||
version = "0.1.0"
|
||||
@@ -572,6 +553,18 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.3"
|
||||
@@ -632,14 +625,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bson"
|
||||
version = "2.5.0"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8746d07211bb12a7c34d995539b4a2acd4e0b0e757de98ce2ab99bcf17443fad"
|
||||
checksum = "9aeb8bae494e49dbc330dd23cf78f6f7accee22f640ce3ab17841badaa4ce232"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.13.1",
|
||||
"bitvec",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"rand",
|
||||
"serde",
|
||||
@@ -804,14 +799,13 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.3.0"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
"aws-config",
|
||||
"aws-sdk-ec2",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"axum_oauth2",
|
||||
"bcrypt",
|
||||
"db_client",
|
||||
@@ -824,19 +818,19 @@ dependencies = [
|
||||
"jwt",
|
||||
"merge_config_files",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.3.2",
|
||||
"monitor_types 0.3.4",
|
||||
"mungos",
|
||||
"periphery_client",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"slack_client_rs",
|
||||
"termination_signal",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.19.0",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http 0.4.0",
|
||||
"tower-http",
|
||||
"typeshare",
|
||||
]
|
||||
|
||||
@@ -971,15 +965,6 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "daemonize"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.4"
|
||||
@@ -1058,10 +1043,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "db_client"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"monitor_types 0.3.2",
|
||||
"monitor_types 0.3.4",
|
||||
"mungos",
|
||||
]
|
||||
|
||||
@@ -1265,6 +1250,12 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.26"
|
||||
@@ -1282,9 +1273,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.26"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -1292,9 +1283,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.26"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
@@ -1309,38 +1300,38 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.26"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.26"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.26"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.26"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.26"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1884,9 +1875,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mongodb"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a37fe10c1485a0cd603468e284a1a8535b4ecf46808f5f7de3639a1e1252dbf8"
|
||||
checksum = "ebe15399de63ad4294c80069967736cbb87ebe467a8cd0629df9cab88a6fbde6"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.13.1",
|
||||
@@ -1934,14 +1925,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_cli"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"async_timing_util",
|
||||
"clap",
|
||||
"colored",
|
||||
"monitor_types 0.3.2",
|
||||
"monitor_types 0.3.4",
|
||||
"rand",
|
||||
"run_command",
|
||||
"run_command 0.0.5",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"strum",
|
||||
@@ -1951,65 +1942,59 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_client"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.19.0",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_helpers"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"monitor_types 0.3.2",
|
||||
"monitor_types 0.3.4",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_periphery"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
"axum",
|
||||
"bollard",
|
||||
"clap",
|
||||
"daemonize",
|
||||
"dotenv",
|
||||
"envy",
|
||||
"futures",
|
||||
"merge_config_files",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.3.2",
|
||||
"monitor_types 0.3.4",
|
||||
"parse_csl",
|
||||
"run_command",
|
||||
"run_command 0.0.6",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"svi",
|
||||
"sysinfo",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tower",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -2018,7 +2003,6 @@ dependencies = [
|
||||
"derive_builder",
|
||||
"diff-struct",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"typeshare",
|
||||
@@ -2026,9 +2010,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b50f6e811dc59b9f19b13b58bee4636f6f1bee2815d9c05f523b530284db75d"
|
||||
checksum = "24ce7541ee0fad1452ae11d0db73285cfbb9df79ad3e49c0680d0bc27c64db24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -2045,18 +2029,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mungos"
|
||||
version = "0.3.14"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b8fabef8c6e29f25a64c58736ab58b191e28aa3bafc3e84a3b0e78a1ba00665"
|
||||
checksum = "bb7d2b7c11b7b18b1436117633a8ec1d7d037530456c6603d5296499f0e38185"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"envy",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"mongodb",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2275,16 +2257,16 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
"monitor_types 0.3.2",
|
||||
"monitor_types 0.3.4",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tokio-tungstenite",
|
||||
"tokio-tungstenite 0.19.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2355,6 +2337,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -2500,6 +2488,12 @@ name = "run_command"
|
||||
version = "0.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb00d9c9c67906454ba83e62aaa52368814512ac1c5b65b8791bb313a9257b0e"
|
||||
|
||||
[[package]]
|
||||
name = "run_command"
|
||||
version = "0.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388e1106aa4bd809ba57afecb8b3efc60b6599cd06a774d5798a7c5a29675307"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
@@ -2818,6 +2812,16 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
@@ -2827,6 +2831,18 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-tokio"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"libc",
|
||||
"signal-hook",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
@@ -2922,9 +2938,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "svi"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec1ee5e6cf961310f3b4ba037f6a3680fc264f9077e0b9f16a0d7cc8d0ade140"
|
||||
checksum = "78a65bb8a7e77c020a52094de0c5cab44506727339282368418b285fe9bc92ef"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
@@ -2959,9 +2975,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.28.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "727220a596b4ca0af040a07091e49f5c105ec8f2592674339a5bf35be592f76e"
|
||||
checksum = "02f1dc6930a439cc5d154221b5387d153f8183529b07c19aca24ea31e0a167e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
@@ -2978,6 +2994,12 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.4.0"
|
||||
@@ -3000,6 +3022,19 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termination_signal"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c8af403325efec5c1f2ab0f69732a47680c40c5379b088958d053cf946b6bfa"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
"signal-hook",
|
||||
"signal-hook-tokio",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tests"
|
||||
version = "0.1.0"
|
||||
@@ -3155,13 +3190,25 @@ name = "tokio-tungstenite"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tungstenite 0.18.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tungstenite",
|
||||
"tungstenite 0.19.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3236,9 +3283,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.3.5"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
|
||||
checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
@@ -3254,27 +3301,10 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-range-header",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3385,6 +3415,25 @@ dependencies = [
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"data-encoding",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"native-tls",
|
||||
"rand",
|
||||
"sha1",
|
||||
@@ -3850,6 +3899,15 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xmlparser"
|
||||
version = "0.13.5"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_cli"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor cli | tools to setup monitor system"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "core"
|
||||
version = "0.3.0"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -11,17 +11,16 @@ types = { package = "monitor_types", path = "../lib/types" }
|
||||
db = { package = "db_client", path = "../lib/db_client" }
|
||||
periphery = { package = "periphery_client", path = "../lib/periphery_client" }
|
||||
axum_oauth2 = { path = "../lib/axum_oauth2" }
|
||||
tokio = { version = "1.26", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio-util = "0.7"
|
||||
tokio = { version = "1.28", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.19", features=["native-tls"] }
|
||||
tokio-util = { version = "0.7"}
|
||||
axum = { version = "0.6", features = ["ws", "json"] }
|
||||
axum-extra = { version = "0.5.0", features = ["spa"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
tower-http = { version = "0.4.0", features = ["cors"] }
|
||||
tower = { version = "0.4", features = ["timeout"] }
|
||||
tower-http = { version = "0.4", features = ["fs", "cors"] }
|
||||
slack = { package = "slack_client_rs", version = "0.0.8" }
|
||||
mungos = "0.3.14"
|
||||
futures-util = "0.3"
|
||||
mungos = "0.3.19"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
dotenv = "0.15"
|
||||
envy = "0.4"
|
||||
@@ -31,10 +30,10 @@ jwt = "0.16"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
async_timing_util = "0.1.14"
|
||||
futures-util = "0.3"
|
||||
diff-struct = "0.5"
|
||||
typeshare = "1.0.0"
|
||||
typeshare = "1.0.1"
|
||||
hex = "0.4"
|
||||
aws-config = "0.55"
|
||||
aws-sdk-ec2 = "0.26"
|
||||
merge_config_files = "0.1.3"
|
||||
aws-config = "0.55.2"
|
||||
aws-sdk-ec2 = "0.27.0"
|
||||
merge_config_files = "0.1.3"
|
||||
termination_signal = "0.1.2"
|
||||
@@ -5,12 +5,10 @@ use aws_sdk_ec2::Client;
|
||||
use diff::Diff;
|
||||
use futures_util::future::join_all;
|
||||
use helpers::{all_logs_success, to_monitor_name};
|
||||
use mungos::{doc, to_bson};
|
||||
use mungos::mongodb::bson::{doc, to_bson};
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
AwsBuilderBuildConfig, Build, DockerContainerState, Log, Operation, PermissionLevel, Update,
|
||||
UpdateStatus, UpdateTarget, Version,
|
||||
monitor_timestamp, traits::Permissioned, AwsBuilderBuildConfig, Build, DockerContainerState,
|
||||
Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget, Version,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -43,13 +41,6 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn build_busy(&self, id: &str) -> bool {
|
||||
match self.build_action_states.lock().await.get(id) {
|
||||
Some(a) => a.busy(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_build(&self, name: &str, user: &RequestUser) -> anyhow::Result<Build> {
|
||||
if !user.is_admin && !user.create_build_permissions {
|
||||
return Err(anyhow!("user does not have permission to create builds"));
|
||||
@@ -117,7 +108,7 @@ impl State {
|
||||
}
|
||||
|
||||
pub async fn delete_build(&self, build_id: &str, user: &RequestUser) -> anyhow::Result<Build> {
|
||||
if self.build_busy(build_id).await {
|
||||
if self.build_action_states.busy(build_id).await {
|
||||
return Err(anyhow!("build busy"));
|
||||
}
|
||||
let build = self
|
||||
@@ -147,21 +138,23 @@ impl State {
|
||||
new_build: Build,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Build> {
|
||||
if self.build_busy(&new_build.id).await {
|
||||
if self.build_action_states.busy(&new_build.id).await {
|
||||
return Err(anyhow!("build busy"));
|
||||
}
|
||||
let id = new_build.id.clone();
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().await;
|
||||
let entry = lock.entry(id.clone()).or_default();
|
||||
entry.updating = true;
|
||||
}
|
||||
self.build_action_states
|
||||
.update_entry(id.clone(), |entry| {
|
||||
entry.updating = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.update_build_inner(new_build, user).await;
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().await;
|
||||
let entry = lock.entry(id).or_default();
|
||||
entry.updating = false;
|
||||
}
|
||||
|
||||
self.build_action_states
|
||||
.update_entry(id.clone(), |entry| {
|
||||
entry.updating = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -252,20 +245,20 @@ impl State {
|
||||
}
|
||||
|
||||
pub async fn build(&self, build_id: &str, user: &RequestUser) -> anyhow::Result<Update> {
|
||||
if self.build_busy(build_id).await {
|
||||
if self.build_action_states.busy(build_id).await {
|
||||
return Err(anyhow!("build busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().await;
|
||||
let entry = lock.entry(build_id.to_string()).or_default();
|
||||
entry.building = true;
|
||||
}
|
||||
self.build_action_states
|
||||
.update_entry(build_id.to_string(), |entry| {
|
||||
entry.building = true;
|
||||
})
|
||||
.await;
|
||||
let res = self.build_inner(build_id, user).await;
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().await;
|
||||
let entry = lock.entry(build_id.to_string()).or_default();
|
||||
entry.building = false;
|
||||
}
|
||||
self.build_action_states
|
||||
.update_entry(build_id.to_string(), |entry| {
|
||||
entry.building = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
|
||||
238
core/src/actions/command.rs
Normal file
238
core/src/actions/command.rs
Normal file
@@ -0,0 +1,238 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use helpers::all_logs_success;
|
||||
use types::{
|
||||
monitor_timestamp, traits::Permissioned, Log, Operation, PeripheryCommand,
|
||||
PeripheryCommandBuilder, PermissionLevel, Update, UpdateStatus, UpdateTarget,
|
||||
};
|
||||
|
||||
use crate::{auth::RequestUser, state::State};
|
||||
|
||||
impl State {
|
||||
pub async fn get_command_check_permissions(
|
||||
&self,
|
||||
command_id: &str,
|
||||
user: &RequestUser,
|
||||
permission_level: PermissionLevel,
|
||||
) -> anyhow::Result<PeripheryCommand> {
|
||||
let command = self.db.get_command(command_id).await?;
|
||||
let permissions = command.get_user_permissions(&user.id);
|
||||
if user.is_admin || permissions >= permission_level {
|
||||
Ok(command)
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"user does not have required permissions on this command"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_command(
|
||||
&self,
|
||||
name: &str,
|
||||
server_id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<PeripheryCommand> {
|
||||
self.get_server_check_permissions(&server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let command = PeripheryCommandBuilder::default()
|
||||
.name(name.to_string())
|
||||
.server_id(server_id)
|
||||
.build()
|
||||
.context("failed to build command")?;
|
||||
let command_id = self
|
||||
.db
|
||||
.commands
|
||||
.create_one(command)
|
||||
.await
|
||||
.context("failed at adding command to db")?;
|
||||
let command = self.db.get_command(&command_id).await?;
|
||||
let update = Update {
|
||||
target: UpdateTarget::Command(command_id),
|
||||
operation: Operation::CreateCommand,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
..Default::default()
|
||||
};
|
||||
self.add_update(update).await?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub async fn create_full_command(
|
||||
&self,
|
||||
mut command: PeripheryCommand,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<PeripheryCommand> {
|
||||
command.id = self
|
||||
.create_command(&command.name, command.server_id.clone(), user)
|
||||
.await?
|
||||
.id;
|
||||
let command = self.update_command(command, user).await?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub async fn copy_command(
|
||||
&self,
|
||||
target_id: &str,
|
||||
new_name: String,
|
||||
new_server_id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<PeripheryCommand> {
|
||||
let mut command = self
|
||||
.get_command_check_permissions(target_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
command.name = new_name;
|
||||
command.server_id = new_server_id;
|
||||
let command = self.create_full_command(command, user).await?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub async fn delete_command(
|
||||
&self,
|
||||
command_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<PeripheryCommand> {
|
||||
if self.command_action_states.busy(command_id).await {
|
||||
return Err(anyhow!("command busy"));
|
||||
}
|
||||
let command = self
|
||||
.get_command_check_permissions(command_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
self.db.commands.delete_one(command_id).await?;
|
||||
let update = Update {
|
||||
target: UpdateTarget::Command(command_id.to_string()),
|
||||
operation: Operation::DeleteCommand,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
logs: vec![Log::simple(
|
||||
"delete command",
|
||||
format!("deleted command {}", command.name),
|
||||
)],
|
||||
success: true,
|
||||
..Default::default()
|
||||
};
|
||||
self.add_update(update).await?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub async fn update_command(
|
||||
&self,
|
||||
mut new_command: PeripheryCommand,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<PeripheryCommand> {
|
||||
let current_command = self
|
||||
.get_command_check_permissions(&new_command.id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
// none of these should be changed through this method
|
||||
new_command.permissions = current_command.permissions.clone();
|
||||
new_command.server_id = current_command.server_id.clone();
|
||||
new_command.created_at = current_command.created_at.clone();
|
||||
new_command.updated_at = start_ts.clone();
|
||||
|
||||
self.db
|
||||
.commands
|
||||
.update_one(
|
||||
&new_command.id,
|
||||
mungos::Update::Regular(new_command.clone()),
|
||||
)
|
||||
.await
|
||||
.context("failed at update one command")?;
|
||||
|
||||
let diff = current_command.diff(&new_command);
|
||||
|
||||
let update = Update {
|
||||
operation: Operation::UpdateCommand,
|
||||
target: UpdateTarget::Command(new_command.id.clone()),
|
||||
start_ts,
|
||||
status: UpdateStatus::Complete,
|
||||
logs: vec![Log::simple(
|
||||
"command update",
|
||||
serde_json::to_string_pretty(&diff).unwrap(),
|
||||
)],
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.add_update(update.clone()).await?;
|
||||
|
||||
self.update_update(update).await?;
|
||||
|
||||
Ok(new_command)
|
||||
}
|
||||
|
||||
pub async fn run_command(
|
||||
&self,
|
||||
command_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.command_action_states.busy(command_id).await {
|
||||
return Err(anyhow!("command busy"));
|
||||
}
|
||||
self.command_action_states
|
||||
.update_entry(command_id.to_string(), |entry| {
|
||||
entry.running = true;
|
||||
})
|
||||
.await;
|
||||
let res = self.run_command_inner(command_id, user).await;
|
||||
self.command_action_states
|
||||
.update_entry(command_id.to_string(), |entry| {
|
||||
entry.running = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
async fn run_command_inner(
|
||||
&self,
|
||||
command_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
let start_ts = monitor_timestamp();
|
||||
let command = self
|
||||
.get_command_check_permissions(command_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
|
||||
if command.command.path.is_empty() || command.command.command.is_empty() {
|
||||
return Err(anyhow!("command or path is empty, aborting"));
|
||||
}
|
||||
|
||||
let server = self.db.get_server(&command.server_id).await?;
|
||||
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Command(command_id.to_string()),
|
||||
operation: Operation::RunCommand,
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
match self.periphery.run_command(&server, &command.command).await {
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
}
|
||||
Err(e) => {
|
||||
update
|
||||
.logs
|
||||
.push(Log::error("clone repo", format!("{e:#?}")));
|
||||
}
|
||||
}
|
||||
|
||||
update.success = all_logs_success(&update.logs);
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use helpers::{all_logs_success, to_monitor_name};
|
||||
use mungos::doc;
|
||||
use mungos::mongodb::bson::doc;
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Deployment, DeploymentWithContainerState, DockerContainerState, Log, Operation,
|
||||
PermissionLevel, ServerStatus, ServerWithStatus, TerminationSignal, Update, UpdateStatus,
|
||||
UpdateTarget,
|
||||
monitor_timestamp, traits::Permissioned, Deployment, DeploymentBuilder,
|
||||
DeploymentWithContainerState, DockerContainerState, Log, Operation, PermissionLevel,
|
||||
ServerStatus, ServerWithStatus, TerminationSignal, Update, UpdateStatus, UpdateTarget,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -34,13 +32,6 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn deployment_busy(&self, id: &str) -> bool {
|
||||
match self.deployment_action_states.lock().await.get(id) {
|
||||
Some(a) => a.busy(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_deployment(
|
||||
&self,
|
||||
name: &str,
|
||||
@@ -50,16 +41,18 @@ impl State {
|
||||
self.get_server_check_permissions(&server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = Deployment {
|
||||
name: to_monitor_name(name),
|
||||
server_id,
|
||||
permissions: [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
created_at: start_ts.clone(),
|
||||
updated_at: start_ts.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut deployment = DeploymentBuilder::default()
|
||||
.name(to_monitor_name(name))
|
||||
.server_id(server_id)
|
||||
.build()
|
||||
.context("failed to build deployment")?;
|
||||
deployment.permissions = [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
deployment.created_at = start_ts.clone();
|
||||
deployment.updated_at = start_ts.clone();
|
||||
|
||||
let deployment_id = self
|
||||
.db
|
||||
.deployments
|
||||
@@ -116,7 +109,7 @@ impl State {
|
||||
stop_signal: Option<TerminationSignal>,
|
||||
stop_time: Option<i32>,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
if self.deployment_busy(deployment_id).await {
|
||||
if self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
let deployment = self
|
||||
@@ -167,21 +160,25 @@ impl State {
|
||||
new_deployment: Deployment,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
if self.deployment_busy(&new_deployment.id).await {
|
||||
if self.deployment_action_states.busy(&new_deployment.id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
let id = new_deployment.id.clone();
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(id.clone()).or_default();
|
||||
entry.updating = true;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(id.clone(), |entry| {
|
||||
entry.updating = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.update_deployment_inner(new_deployment, user).await;
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(id).or_default();
|
||||
entry.updating = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(id.clone(), |entry| {
|
||||
entry.updating = false;
|
||||
})
|
||||
.await;
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
@@ -285,22 +282,25 @@ impl State {
|
||||
new_name: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(&deployment_id).await {
|
||||
if self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.renaming = true;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.renaming = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self
|
||||
.rename_deployment_inner(deployment_id, new_name, user)
|
||||
.await;
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.renaming = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.renaming = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -451,20 +451,23 @@ impl State {
|
||||
user: &RequestUser,
|
||||
check_deployment_busy: bool,
|
||||
) -> anyhow::Result<Update> {
|
||||
if check_deployment_busy && self.deployment_busy(deployment_id).await {
|
||||
if check_deployment_busy && self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.recloning = true;
|
||||
}
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.recloning = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.reclone_deployment_inner(deployment_id, user).await;
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.recloning = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.recloning = false;
|
||||
})
|
||||
.await;
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
@@ -488,19 +491,18 @@ impl State {
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
update.success = match self.periphery.clone_repo(&server, &deployment).await {
|
||||
match self.periphery.clone_repo(&server, &deployment).await {
|
||||
Ok(clone_logs) => {
|
||||
update.logs.extend(clone_logs);
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
update
|
||||
.logs
|
||||
.push(Log::error("clone repo", format!("{e:#?}")));
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
update.success = all_logs_success(&update.logs);
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
|
||||
@@ -516,22 +518,24 @@ impl State {
|
||||
stop_signal: Option<TerminationSignal>,
|
||||
stop_time: Option<i32>,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id).await {
|
||||
if self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.deploying = true;
|
||||
}
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.deploying = true;
|
||||
})
|
||||
.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 entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.deploying = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.deploying = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -578,6 +582,9 @@ impl State {
|
||||
|
||||
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 deploy_log = match self
|
||||
.periphery
|
||||
.deploy(&server, &deployment, stop_signal, stop_time)
|
||||
@@ -602,20 +609,22 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id).await {
|
||||
if self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.starting = true;
|
||||
}
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.starting = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.start_container_inner(deployment_id, user).await;
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.starting = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.starting = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -674,22 +683,24 @@ impl State {
|
||||
stop_signal: Option<TerminationSignal>,
|
||||
stop_time: Option<i32>,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id).await {
|
||||
if self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.stopping = true;
|
||||
}
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.stopping = true;
|
||||
})
|
||||
.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 entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.stopping = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.stopping = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -716,6 +727,9 @@ impl State {
|
||||
};
|
||||
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
|
||||
.periphery
|
||||
.container_stop(&server, &deployment.name, stop_signal, stop_time)
|
||||
@@ -750,22 +764,24 @@ impl State {
|
||||
stop_signal: Option<TerminationSignal>,
|
||||
stop_time: Option<i32>,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id).await {
|
||||
if self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.removing = true;
|
||||
}
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.removing = true;
|
||||
})
|
||||
.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 entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.removing = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.removing = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -792,6 +808,9 @@ impl State {
|
||||
};
|
||||
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
|
||||
.periphery
|
||||
.container_remove(&server, &deployment.name, stop_signal, stop_time)
|
||||
@@ -824,20 +843,22 @@ impl State {
|
||||
deployment_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.deployment_busy(deployment_id).await {
|
||||
if self.deployment_action_states.busy(deployment_id).await {
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.pulling = true;
|
||||
}
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.pulling = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.pull_deployment_repo_inner(deployment_id, user).await;
|
||||
{
|
||||
let mut lock = self.deployment_action_states.lock().await;
|
||||
let entry = lock.entry(deployment_id.to_string()).or_default();
|
||||
entry.pulling = false;
|
||||
}
|
||||
|
||||
self.deployment_action_states
|
||||
.update_entry(deployment_id.to_string(), |entry| {
|
||||
entry.pulling = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use types::Update;
|
||||
use crate::state::State;
|
||||
|
||||
mod build;
|
||||
mod command;
|
||||
mod deployment;
|
||||
mod group;
|
||||
mod procedure;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use futures_util::future::join_all;
|
||||
use mungos::doc;
|
||||
use mungos::mongodb::bson::doc;
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Log, Operation, PermissionLevel, Server, Update, UpdateStatus, UpdateTarget,
|
||||
monitor_timestamp, traits::Permissioned, Log, Operation, PermissionLevel, Server, Update,
|
||||
UpdateStatus, UpdateTarget,
|
||||
};
|
||||
|
||||
use crate::{auth::RequestUser, state::State};
|
||||
@@ -28,13 +27,6 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn server_busy(&self, id: &str) -> bool {
|
||||
match self.server_action_states.lock().await.get(id) {
|
||||
Some(a) => a.busy(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_server(
|
||||
&self,
|
||||
name: &str,
|
||||
@@ -96,7 +88,7 @@ impl State {
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Server> {
|
||||
if self.server_busy(server_id).await {
|
||||
if self.server_action_states.busy(server_id).await {
|
||||
return Err(anyhow!("server busy"));
|
||||
}
|
||||
let server = self
|
||||
@@ -164,7 +156,7 @@ impl State {
|
||||
mut new_server: Server,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Server> {
|
||||
if self.server_busy(&new_server.id).await {
|
||||
if self.server_action_states.busy(&new_server.id).await {
|
||||
return Err(anyhow!("server busy"));
|
||||
}
|
||||
let current_server = self
|
||||
@@ -208,20 +200,22 @@ impl State {
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.server_busy(server_id).await {
|
||||
if self.server_action_states.busy(server_id).await {
|
||||
return Err(anyhow!("server busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().await;
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_networks = true;
|
||||
}
|
||||
self.server_action_states
|
||||
.update_entry(server_id.to_string(), |entry| {
|
||||
entry.pruning_networks = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.prune_networks_inner(server_id, user).await;
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().await;
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_networks = false;
|
||||
}
|
||||
|
||||
self.server_action_states
|
||||
.update_entry(server_id.to_string(), |entry| {
|
||||
entry.pruning_networks = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -269,20 +263,22 @@ impl State {
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.server_busy(server_id).await {
|
||||
if self.server_action_states.busy(server_id).await {
|
||||
return Err(anyhow!("server busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().await;
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_images = true;
|
||||
}
|
||||
self.server_action_states
|
||||
.update_entry(server_id.to_string(), |entry| {
|
||||
entry.pruning_images = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.prune_images_inner(server_id, user).await;
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().await;
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_images = false;
|
||||
}
|
||||
|
||||
self.server_action_states
|
||||
.update_entry(server_id.to_string(), |entry| {
|
||||
entry.pruning_images = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
@@ -331,20 +327,22 @@ impl State {
|
||||
server_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.server_busy(server_id).await {
|
||||
if self.server_action_states.busy(server_id).await {
|
||||
return Err(anyhow!("server busy"));
|
||||
}
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().await;
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_containers = true;
|
||||
}
|
||||
self.server_action_states
|
||||
.update_entry(server_id.to_string(), |entry| {
|
||||
entry.pruning_containers = true;
|
||||
})
|
||||
.await;
|
||||
|
||||
let res = self.prune_containers_inner(server_id, user).await;
|
||||
{
|
||||
let mut lock = self.server_action_states.lock().await;
|
||||
let entry = lock.entry(server_id.to_string()).or_default();
|
||||
entry.pruning_containers = false;
|
||||
}
|
||||
|
||||
self.server_action_states
|
||||
.update_entry(server_id.to_string(), |entry| {
|
||||
entry.pruning_containers = false;
|
||||
})
|
||||
.await;
|
||||
res
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@ use axum::{
|
||||
};
|
||||
use futures_util::TryStreamExt;
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize, Document, FindOptions, Serialize};
|
||||
use mungos::mongodb::{
|
||||
bson::{doc, Document},
|
||||
options::FindOptions,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{
|
||||
monitor_ts_from_unix, traits::Permissioned, unix_from_monitor_ts, AwsBuilderConfig, Build,
|
||||
BuildActionState, BuildVersionsReponse, Operation, PermissionLevel, UpdateStatus,
|
||||
@@ -293,13 +297,7 @@ impl State {
|
||||
) -> anyhow::Result<BuildActionState> {
|
||||
self.get_build_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
let action_state = self
|
||||
.build_action_states
|
||||
.lock()
|
||||
.await
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.clone();
|
||||
let action_state = self.build_action_states.get_or_default(id).await;
|
||||
Ok(action_state)
|
||||
}
|
||||
|
||||
|
||||
220
core/src/api/command.rs
Normal file
220
core/src/api/command.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
use anyhow::Context;
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
routing::{delete, get, patch, post},
|
||||
Json, Router,
|
||||
};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::mongodb::bson::Document;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{traits::Permissioned, CommandActionState, PeripheryCommand, PermissionLevel};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::{
|
||||
api::spawn_request_action,
|
||||
auth::{RequestUser, RequestUserExtension},
|
||||
response,
|
||||
state::{State, StateExtension},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CommandId {
|
||||
id: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CreateCommandBody {
|
||||
name: String,
|
||||
server_id: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CopyCommandBody {
|
||||
name: String,
|
||||
server_id: String,
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/:id",
|
||||
get(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Path(CommandId { id })| async move {
|
||||
let command = state
|
||||
.get_command_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(command))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/list",
|
||||
get(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Query(query): Query<Document>| async move {
|
||||
let commands = state
|
||||
.list_commands(&user, query)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(commands))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/create",
|
||||
post(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Json(command): Json<CreateCommandBody>| async move {
|
||||
let command = state
|
||||
.create_command(&command.name, command.server_id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(command))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/create_full",
|
||||
post(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Json(command): Json<PeripheryCommand>| async move {
|
||||
let command = spawn_request_action(async move {
|
||||
state
|
||||
.create_full_command(command, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
.await??;
|
||||
response!(Json(command))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/copy",
|
||||
post(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Path(CommandId { id }),
|
||||
Json(command): Json<CopyCommandBody>| async move {
|
||||
let command = spawn_request_action(async move {
|
||||
state
|
||||
.copy_command(&id, command.name, command.server_id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
.await??;
|
||||
response!(Json(command))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/delete",
|
||||
delete(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Path(CommandId { id })| async move {
|
||||
let build = spawn_request_action(async move {
|
||||
state
|
||||
.delete_command(&id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
.await??;
|
||||
response!(Json(build))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/update",
|
||||
patch(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Json(command): Json<PeripheryCommand>| async move {
|
||||
let command = spawn_request_action(async move {
|
||||
state
|
||||
.update_command(command, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
.await??;
|
||||
response!(Json(command))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/action_state",
|
||||
get(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Path(CommandId { id })| async move {
|
||||
let action_state = state
|
||||
.get_command_action_states(id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(action_state))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/run",
|
||||
post(
|
||||
|state: StateExtension,
|
||||
user: RequestUserExtension,
|
||||
Path(CommandId { id })| async move {
|
||||
let update = spawn_request_action(async move {
|
||||
state
|
||||
.run_command(&id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
.await??;
|
||||
response!(Json(update))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
async fn list_commands(
|
||||
&self,
|
||||
user: &RequestUser,
|
||||
query: impl Into<Option<Document>>,
|
||||
) -> anyhow::Result<Vec<PeripheryCommand>> {
|
||||
let commands: Vec<PeripheryCommand> = self
|
||||
.db
|
||||
.commands
|
||||
.get_some(query, None)
|
||||
.await
|
||||
.context("failed at get all commands query")?
|
||||
.into_iter()
|
||||
.filter(|s| {
|
||||
if user.is_admin {
|
||||
true
|
||||
} else {
|
||||
let permissions = s.get_user_permissions(&user.id);
|
||||
permissions != PermissionLevel::None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(commands)
|
||||
}
|
||||
|
||||
async fn get_command_action_states(
|
||||
&self,
|
||||
id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<CommandActionState> {
|
||||
self.get_command_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
let action_state = self.command_action_states.get_or_default(id).await;
|
||||
Ok(action_state)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,11 @@ use axum::{
|
||||
};
|
||||
use futures_util::future::join_all;
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, options::FindOneOptions, Deserialize, Document, Serialize};
|
||||
use mungos::mongodb::{
|
||||
bson::{doc, Document},
|
||||
options::FindOneOptions,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{
|
||||
traits::Permissioned, Deployment, DeploymentActionState, DeploymentWithContainerState,
|
||||
DockerContainerState, DockerContainerStats, Log, Operation, PermissionLevel, Server,
|
||||
@@ -451,13 +455,7 @@ impl State {
|
||||
) -> anyhow::Result<DeploymentActionState> {
|
||||
self.get_deployment_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
let action_state = self
|
||||
.deployment_action_states
|
||||
.lock()
|
||||
.await
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.clone();
|
||||
let action_state = self.deployment_action_states.get_or_default(id).await;
|
||||
Ok(action_state)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use axum_oauth2::random_duration;
|
||||
use helpers::handle_anyhow_error;
|
||||
use hex::ToHex;
|
||||
use hmac::{Hmac, Mac};
|
||||
use mungos::Deserialize;
|
||||
use serde::Deserialize;
|
||||
use sha2::Sha256;
|
||||
use types::GITHUB_WEBHOOK_USER_ID;
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ use axum::{
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use mungos::mongodb::bson::Document;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{traits::Permissioned, Group, PermissionLevel};
|
||||
use typeshare::typeshare;
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ use axum::{
|
||||
};
|
||||
use futures_util::Future;
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize};
|
||||
use mungos::mongodb::bson::doc;
|
||||
use serde::Deserialize;
|
||||
use types::{PermissionLevel, UpdateTarget, User};
|
||||
use typeshare::typeshare;
|
||||
|
||||
@@ -20,15 +21,16 @@ use crate::{
|
||||
ResponseResult,
|
||||
};
|
||||
|
||||
pub mod build;
|
||||
pub mod deployment;
|
||||
mod build;
|
||||
mod command;
|
||||
mod deployment;
|
||||
mod github_listener;
|
||||
pub mod group;
|
||||
pub mod permissions;
|
||||
pub mod procedure;
|
||||
pub mod secret;
|
||||
pub mod server;
|
||||
pub mod update;
|
||||
mod group;
|
||||
mod permissions;
|
||||
mod procedure;
|
||||
mod secret;
|
||||
mod server;
|
||||
mod update;
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Deserialize)]
|
||||
@@ -94,6 +96,7 @@ pub fn router() -> Router {
|
||||
.nest("/build", build::router())
|
||||
.nest("/deployment", deployment::router())
|
||||
.nest("/server", server::router())
|
||||
.nest("/command", command::router())
|
||||
.nest("/procedure", procedure::router())
|
||||
.nest("/group", group::router())
|
||||
.nest("/update", update::router())
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{routing::post, Extension, Json, Router};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize, Document, Serialize};
|
||||
use mungos::mongodb::bson::{doc, Document};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{
|
||||
monitor_timestamp, Build, Deployment, Group, Log, Operation, PermissionLevel,
|
||||
PermissionsTarget, Procedure, Server, Update, UpdateStatus, UpdateTarget,
|
||||
|
||||
@@ -5,7 +5,8 @@ use axum::{
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use mungos::mongodb::bson::Document;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{traits::Permissioned, PermissionLevel, Procedure};
|
||||
use typeshare::typeshare;
|
||||
|
||||
|
||||
@@ -5,7 +5,11 @@ use axum::{
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use helpers::{generate_secret, handle_anyhow_error};
|
||||
use mungos::{doc, to_bson, Deserialize, Document, Serialize, Update};
|
||||
use mungos::{
|
||||
mongodb::bson::{doc, to_bson, Document},
|
||||
Update,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{monitor_timestamp, ApiSecret};
|
||||
use typeshare::typeshare;
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@ use axum::{
|
||||
};
|
||||
use futures_util::{future::join_all, SinkExt, StreamExt};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize, Document, FindOptions};
|
||||
use mungos::mongodb::{
|
||||
bson::{doc, Document},
|
||||
options::FindOptions,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use tokio::select;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -676,13 +680,7 @@ impl State {
|
||||
) -> anyhow::Result<ServerActionState> {
|
||||
self.get_server_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
let action_state = self
|
||||
.server_action_states
|
||||
.lock()
|
||||
.await
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.clone();
|
||||
let action_state = self.server_action_states.get_or_default(id).await;
|
||||
Ok(action_state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Query, routing::get, Extension, Json, Router};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, to_bson};
|
||||
use mungos::mongodb::bson::{doc, to_bson};
|
||||
use serde_json::Value;
|
||||
use types::{PermissionLevel, Update, UpdateTarget};
|
||||
|
||||
@@ -92,6 +92,10 @@ impl State {
|
||||
.get_group_check_permissions(id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.map(|_| ()),
|
||||
UpdateTarget::Command(id) => self
|
||||
.get_command_check_permissions(id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.map(|_| ()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Query, response::Redirect, routing::get, Extension, Router};
|
||||
use axum_oauth2::github::{GithubOauthClient, GithubOauthExtension};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize};
|
||||
use mungos::mongodb::bson::doc;
|
||||
use serde::Deserialize;
|
||||
use types::{monitor_timestamp, CoreConfig, User};
|
||||
|
||||
use crate::{response, state::StateExtension};
|
||||
|
||||
@@ -4,7 +4,8 @@ use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Query, response::Redirect, routing::get, Extension, Router};
|
||||
use axum_oauth2::google::{GoogleOauthClient, GoogleOauthExtension};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize};
|
||||
use mungos::mongodb::bson::doc;
|
||||
use serde::Deserialize;
|
||||
use types::{monitor_timestamp, CoreConfig, User};
|
||||
|
||||
use crate::{response, state::StateExtension};
|
||||
|
||||
@@ -9,7 +9,7 @@ use axum::{body::Body, http::Request, Extension};
|
||||
use axum_oauth2::random_string;
|
||||
use hmac::{Hmac, Mac};
|
||||
use jwt::{SignWithKey, VerifyWithKey};
|
||||
use mungos::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use types::{CoreConfig, User};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Json, routing::post, Extension, Router};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::doc;
|
||||
use mungos::mongodb::bson::doc;
|
||||
use types::{monitor_timestamp, User, UserCredentials};
|
||||
|
||||
use crate::state::StateExtension;
|
||||
|
||||
@@ -9,7 +9,7 @@ use axum::{
|
||||
Extension, Json, Router,
|
||||
};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::CoreConfig;
|
||||
use typeshare::typeshare;
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@ use anyhow::{anyhow, Context};
|
||||
use async_timing_util::unix_timestamp_ms;
|
||||
use axum::{routing::post, Extension, Json, Router};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize, Document, Update};
|
||||
use mungos::{
|
||||
mongodb::bson::{doc, Document},
|
||||
Update,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use types::unix_from_monitor_ts;
|
||||
|
||||
use crate::state::StateExtension;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use axum_extra::routing::SpaRouter;
|
||||
use axum::Router;
|
||||
use dotenv::dotenv;
|
||||
use merge_config_files::parse_config_file;
|
||||
use mungos::Deserialize;
|
||||
use serde::Deserialize;
|
||||
use tower_http::services::{ServeDir, ServeFile};
|
||||
use types::CoreConfig;
|
||||
|
||||
type SpaRouter = Router;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Env {
|
||||
#[serde(default = "default_config_path")]
|
||||
@@ -12,12 +15,17 @@ struct Env {
|
||||
pub frontend_path: String,
|
||||
}
|
||||
|
||||
pub fn load() -> (CoreConfig, SpaRouter) {
|
||||
pub fn load() -> (CoreConfig, SpaRouter, ServeFile) {
|
||||
dotenv().ok();
|
||||
let env: Env = envy::from_env().expect("failed to parse environment variables");
|
||||
let config = parse_config_file(env.config_path).expect("failed to parse config");
|
||||
let spa_router = SpaRouter::new("/assets", env.frontend_path);
|
||||
(config, spa_router)
|
||||
let spa_router = Router::new().nest_service(
|
||||
"/assets",
|
||||
ServeDir::new(&env.frontend_path)
|
||||
.not_found_service(ServeFile::new(format!("{}/index.html", env.frontend_path))),
|
||||
);
|
||||
let index_html_service = ServeFile::new(format!("{}/index.html", env.frontend_path));
|
||||
(config, spa_router, index_html_service)
|
||||
}
|
||||
|
||||
pub fn default_config_path() -> String {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use std::str::FromStr;
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use diff::{Diff, OptionDiff};
|
||||
use helpers::to_monitor_name;
|
||||
use types::Build;
|
||||
use tokio::sync::RwLock;
|
||||
use types::{traits::Busy, Build};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! response {
|
||||
@@ -66,3 +67,37 @@ pub fn empty_or_only_spaces(word: &str) -> bool {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Cache<T: Clone + Default> {
|
||||
cache: RwLock<HashMap<String, T>>,
|
||||
}
|
||||
|
||||
impl<T: Clone + Default> Cache<T> {
|
||||
pub async fn get(&self, key: &str) -> Option<T> {
|
||||
self.cache.read().await.get(key).map(|e| e.clone())
|
||||
}
|
||||
|
||||
pub async fn get_or_default(&self, key: String) -> T {
|
||||
let mut cache = self.cache.write().await;
|
||||
cache.entry(key).or_default().clone()
|
||||
}
|
||||
|
||||
pub async fn update_entry(&self, key: String, handler: impl Fn(&mut T) -> ()) {
|
||||
let mut cache = self.cache.write().await;
|
||||
handler(cache.entry(key).or_default());
|
||||
}
|
||||
|
||||
pub async fn clear(&self) {
|
||||
self.cache.write().await.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Default + Busy> Cache<T> {
|
||||
pub async fn busy(&self, id: &str) -> bool {
|
||||
match self.get(id).await {
|
||||
Some(state) => state.busy(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use ::helpers::get_socket_addr;
|
||||
use auth::JwtClient;
|
||||
use axum::{http::StatusCode, Router};
|
||||
use state::State;
|
||||
use termination_signal::tokio::immediate_term_handle;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
|
||||
mod actions;
|
||||
@@ -20,29 +21,43 @@ type ResponseResult<T> = Result<T, (StatusCode, String)>;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let (config, spa_router) = config::load();
|
||||
println!("version: v{}", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
println!("starting monitor core on port {}...", config.port);
|
||||
let term_signal = immediate_term_handle()?;
|
||||
|
||||
let app = Router::new()
|
||||
.merge(spa_router)
|
||||
.nest("/api", api::router())
|
||||
.nest("/auth", auth::router(&config))
|
||||
.nest("/ws", ws::router())
|
||||
.layer(JwtClient::extension(&config))
|
||||
.layer(State::extension(config.clone()).await)
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods(Any)
|
||||
.allow_headers(Any),
|
||||
);
|
||||
let app = tokio::spawn(async move {
|
||||
let (config, spa_router, index_html_service) = config::load();
|
||||
|
||||
println!("started monitor core on port {}", config.port);
|
||||
println!("starting monitor core on port {}...", config.port);
|
||||
|
||||
axum::Server::bind(&get_socket_addr(config.port))
|
||||
.serve(app.into_make_service())
|
||||
.await?;
|
||||
let app = Router::new()
|
||||
.nest("/api", api::router())
|
||||
.nest("/auth", auth::router(&config))
|
||||
.nest("/ws", ws::router())
|
||||
.layer(JwtClient::extension(&config))
|
||||
.layer(State::extension(config.clone()).await)
|
||||
.merge(spa_router)
|
||||
.fallback_service(index_html_service)
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods(Any)
|
||||
.allow_headers(Any),
|
||||
);
|
||||
|
||||
println!("started monitor core on port {}", config.port);
|
||||
|
||||
axum::Server::bind(&get_socket_addr(config.port))
|
||||
.serve(app.into_make_service())
|
||||
.await?;
|
||||
|
||||
anyhow::Ok(())
|
||||
});
|
||||
|
||||
tokio::select! {
|
||||
res = app => return res?,
|
||||
_ = term_signal => {},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ use async_timing_util::{
|
||||
unix_timestamp_ms, wait_until_timelength, Timelength, ONE_DAY_MS, ONE_HOUR_MS,
|
||||
};
|
||||
use futures_util::future::join_all;
|
||||
use mungos::doc;
|
||||
use mungos::mongodb::bson::doc;
|
||||
use slack::types::Block;
|
||||
use types::{Server, SystemStats, SystemStatsQuery, SystemStatsRecord};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct AlertStatus {
|
||||
cpu_alert: bool,
|
||||
mem_alert: bool,
|
||||
@@ -100,16 +100,16 @@ impl State {
|
||||
}
|
||||
|
||||
async fn check_cpu(&self, server: &Server, stats: &SystemStats) {
|
||||
let server_alert_status = self.server_alert_status.lock().await;
|
||||
if self.slack.is_none()
|
||||
|| server_alert_status
|
||||
|| self
|
||||
.server_alert_status
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| s.cpu_alert)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
drop(server_alert_status);
|
||||
if stats.cpu_perc > server.cpu_alert {
|
||||
let region = if let Some(region) = &server.region {
|
||||
format!(" ({region})")
|
||||
@@ -171,24 +171,26 @@ impl State {
|
||||
server.name, stats.cpu_perc
|
||||
)
|
||||
} else {
|
||||
let mut lock = self.server_alert_status.lock().await;
|
||||
let entry = lock.entry(server.id.clone()).or_default();
|
||||
entry.cpu_alert = true;
|
||||
self.server_alert_status
|
||||
.update_entry(server.id.clone(), |entry| {
|
||||
entry.cpu_alert = true;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_mem(&self, server: &Server, stats: &SystemStats) {
|
||||
let server_alert_status = self.server_alert_status.lock().await;
|
||||
if self.slack.is_none()
|
||||
|| server_alert_status
|
||||
|| self
|
||||
.server_alert_status
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| s.mem_alert)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
drop(server_alert_status);
|
||||
let usage_perc = (stats.mem_used_gb / stats.mem_total_gb) * 100.0;
|
||||
if usage_perc > server.mem_alert {
|
||||
let region = if let Some(region) = &server.region {
|
||||
@@ -254,25 +256,27 @@ impl State {
|
||||
server.name, stats.mem_used_gb, stats.mem_total_gb,
|
||||
)
|
||||
} else {
|
||||
let mut lock = self.server_alert_status.lock().await;
|
||||
let entry = lock.entry(server.id.clone()).or_default();
|
||||
entry.mem_alert = true;
|
||||
self.server_alert_status
|
||||
.update_entry(server.id.clone(), |entry| {
|
||||
entry.mem_alert = true;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_disk(&self, server: &Server, stats: &SystemStats) {
|
||||
for disk in &stats.disk.disks {
|
||||
let server_alert_status = self.server_alert_status.lock().await;
|
||||
if self.slack.is_none()
|
||||
|| server_alert_status
|
||||
|| self
|
||||
.server_alert_status
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| *s.disk_alert.get(&disk.mount).unwrap_or(&false))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
drop(server_alert_status);
|
||||
let usage_perc = (disk.used_gb / disk.total_gb) * 100.0;
|
||||
if usage_perc > server.disk_alert {
|
||||
let region = if let Some(region) = &server.region {
|
||||
@@ -315,25 +319,27 @@ impl State {
|
||||
server.name, stats.disk.used_gb, stats.disk.total_gb,
|
||||
)
|
||||
} else {
|
||||
let mut lock = self.server_alert_status.lock().await;
|
||||
let entry = lock.entry(server.id.clone()).or_default();
|
||||
entry.disk_alert.insert(disk.mount.clone(), true);
|
||||
self.server_alert_status
|
||||
.update_entry(server.id.clone(), |entry| {
|
||||
entry.disk_alert.insert(disk.mount.clone(), true);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_components(&self, server: &Server, stats: &SystemStats) {
|
||||
let lock = self.server_alert_status.lock().await;
|
||||
if self.slack.is_none()
|
||||
|| lock
|
||||
|| self
|
||||
.server_alert_status
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| s.component_alert)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
drop(lock);
|
||||
let info = stats
|
||||
.components
|
||||
.iter()
|
||||
@@ -393,9 +399,11 @@ impl State {
|
||||
info.join(" | "),
|
||||
)
|
||||
} else {
|
||||
let mut lock = self.server_alert_status.lock().await;
|
||||
let entry = lock.entry(server.id.clone()).or_default();
|
||||
entry.component_alert = true;
|
||||
self.server_alert_status
|
||||
.update_entry(server.id.clone(), |entry| {
|
||||
entry.component_alert = true;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -487,7 +495,7 @@ impl State {
|
||||
);
|
||||
}
|
||||
{
|
||||
self.server_alert_status.lock().await.clear();
|
||||
self.server_alert_status.clear().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_timing_util::{unix_timestamp_ms, wait_until_timelength, Timelength, ONE_HOUR_MS};
|
||||
use axum::Extension;
|
||||
use db::DbClient;
|
||||
use futures_util::future::join_all;
|
||||
use mungos::doc;
|
||||
use mungos::mongodb::bson::doc;
|
||||
use periphery::PeripheryClient;
|
||||
use tokio::sync::Mutex;
|
||||
use types::{BuildActionState, CoreConfig, DeploymentActionState, ServerActionState};
|
||||
use types::{
|
||||
BuildActionState, CommandActionState, CoreConfig, DeploymentActionState, ServerActionState,
|
||||
};
|
||||
|
||||
use crate::{monitoring::AlertStatus, ws::update::UpdateWsChannel};
|
||||
use crate::{helpers::Cache, monitoring::AlertStatus, ws::update::UpdateWsChannel};
|
||||
|
||||
pub type StateExtension = Extension<Arc<State>>;
|
||||
|
||||
pub type ActionStateMap<T> = Mutex<HashMap<String, T>>;
|
||||
|
||||
// pub type Cache<T> = RwLock<HashMap<String, T>>;
|
||||
pub struct State {
|
||||
pub config: CoreConfig,
|
||||
pub db: DbClient,
|
||||
pub update: UpdateWsChannel,
|
||||
pub periphery: PeripheryClient,
|
||||
pub slack: Option<slack::Client>,
|
||||
pub build_action_states: ActionStateMap<BuildActionState>,
|
||||
pub deployment_action_states: ActionStateMap<DeploymentActionState>,
|
||||
pub server_action_states: ActionStateMap<ServerActionState>,
|
||||
pub server_alert_status: Mutex<HashMap<String, AlertStatus>>, // (server_id, AlertStatus)
|
||||
pub build_action_states: Cache<BuildActionState>,
|
||||
pub deployment_action_states: Cache<DeploymentActionState>,
|
||||
pub server_action_states: Cache<ServerActionState>,
|
||||
pub command_action_states: Cache<CommandActionState>,
|
||||
pub server_alert_status: Cache<AlertStatus>, // (server_id, AlertStatus)
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -38,6 +39,7 @@ impl State {
|
||||
build_action_states: Default::default(),
|
||||
deployment_action_states: Default::default(),
|
||||
server_action_states: Default::default(),
|
||||
command_action_states: Default::default(),
|
||||
server_alert_status: Default::default(),
|
||||
};
|
||||
let state = Arc::new(state);
|
||||
|
||||
@@ -6,9 +6,9 @@ use axum::{
|
||||
};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::Deserialize;
|
||||
use serde::Deserialize;
|
||||
use tokio::select;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
use tokio_tungstenite::tungstenite::Message as TungsteniteMessage;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use types::{traits::Permissioned, PermissionLevel, SystemStatsQuery};
|
||||
|
||||
@@ -67,7 +67,7 @@ pub async fn ws_handler(
|
||||
},
|
||||
stats = stats_recv.next() => stats,
|
||||
};
|
||||
if let Some(Ok(Message::Text(msg))) = stats {
|
||||
if let Some(Ok(TungsteniteMessage::Text(msg))) = stats {
|
||||
let _ = ws_sender.send(AxumMessage::Text(msg)).await;
|
||||
} else {
|
||||
let _ = stats_recv.close(None).await;
|
||||
|
||||
@@ -140,6 +140,12 @@ async fn user_can_see_update(
|
||||
.await?;
|
||||
(permissions, "group")
|
||||
}
|
||||
UpdateTarget::Command(command_id) => {
|
||||
let permissions = db_client
|
||||
.get_user_permission_on_command(user_id, command_id)
|
||||
.await?;
|
||||
(permissions, "command")
|
||||
}
|
||||
UpdateTarget::System => {
|
||||
return Err(anyhow!("user not admin, can't recieve system updates"))
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"license": "GPL v3.0",
|
||||
"devDependencies": {
|
||||
"@types/sanitize-html": "^2.9.0",
|
||||
"sass": "^1.57.1",
|
||||
"typescript": "^4.9.4",
|
||||
"vite": "^4.0.3",
|
||||
@@ -19,10 +20,12 @@
|
||||
"dependencies": {
|
||||
"@solidjs/router": "^0.6.0",
|
||||
"@tanstack/solid-query": "^4.26.0",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"axios": "^1.2.1",
|
||||
"js-file-download": "^0.4.12",
|
||||
"lightweight-charts": "^3.8.0",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"sanitize-html": "^2.10.0",
|
||||
"solid-js": "^1.6.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ const Build: Component<{}> = (p) => {
|
||||
}
|
||||
});
|
||||
});
|
||||
onCleanup(() => unsub);
|
||||
onCleanup(() => unsub());
|
||||
const userCanUpdate = () =>
|
||||
user().admin ||
|
||||
build()?.permissions![user_id()] === PermissionLevel.Update;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useContext,
|
||||
} from "solid-js";
|
||||
import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client } from "../../..";
|
||||
import { client, pushNotification } from "../../..";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { Build, Operation, PermissionLevel, ServerWithStatus } from "../../../types";
|
||||
@@ -55,10 +55,17 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
client.get_build(params.id).then((build) => {
|
||||
set({
|
||||
...build,
|
||||
_id: { $oid: params.id } as any,
|
||||
repo: build.repo,
|
||||
branch: build.branch,
|
||||
pre_build: build.pre_build,
|
||||
docker_build_args: build.docker_build_args,
|
||||
docker_build_args: {
|
||||
build_path: build.docker_build_args?.build_path!,
|
||||
dockerfile_path: build.docker_build_args?.dockerfile_path,
|
||||
build_args: build.docker_build_args?.build_args,
|
||||
extra_args: build.docker_build_args?.extra_args,
|
||||
use_buildx: build.docker_build_args?.use_buildx,
|
||||
},
|
||||
docker_account: build.docker_account,
|
||||
github_account: build.github_account,
|
||||
aws_config: build.aws_config,
|
||||
@@ -72,7 +79,13 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
|
||||
const save = () => {
|
||||
setBuild("saving", true);
|
||||
client.update_build(build)
|
||||
client
|
||||
.update_build(build)
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
pushNotification("bad", "update build failed");
|
||||
setBuild("saving", false);
|
||||
});
|
||||
};
|
||||
|
||||
let update_unsub = () => {};
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { Component, Match, Setter, Show, Switch, createSignal } from "solid-js";
|
||||
import {
|
||||
Component,
|
||||
Match,
|
||||
Setter,
|
||||
Show,
|
||||
Signal,
|
||||
Switch,
|
||||
createSignal,
|
||||
} from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
@@ -13,10 +21,12 @@ import { combineClasses } from "../../util/helpers";
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import {
|
||||
DockerContainerState,
|
||||
Operation,
|
||||
PermissionLevel,
|
||||
ServerStatus,
|
||||
TerminationSignal,
|
||||
TerminationSignalLabel,
|
||||
UpdateStatus,
|
||||
} from "../../types";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
import Selector from "../shared/menu/Selector";
|
||||
@@ -164,18 +174,12 @@ const Build: Component = () => {
|
||||
};
|
||||
|
||||
const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||
// const { deployments } = useAppState();
|
||||
const params = useParams();
|
||||
// const deployment = () => deployments.get(params.id)!;
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
const name = () => deployment()?.deployment.name;
|
||||
const [termSignalLabel, setTermSignalLabel] =
|
||||
createSignal<TerminationSignalLabel>({
|
||||
signal: "default" as TerminationSignal,
|
||||
label: "",
|
||||
});
|
||||
const [termSignalLabel, setTermSignalLabel] = useTermSignalLabel();
|
||||
return (
|
||||
<Show
|
||||
when={!actions.deploying}
|
||||
@@ -235,11 +239,7 @@ const RemoveContainer = () => {
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
const [termSignalLabel, setTermSignalLabel] =
|
||||
createSignal<TerminationSignalLabel>({
|
||||
signal: "default" as TerminationSignal,
|
||||
label: "",
|
||||
});
|
||||
const [termSignalLabel, setTermSignalLabel] = useTermSignalLabel();
|
||||
return (
|
||||
<Show
|
||||
when={!actions.removing}
|
||||
@@ -316,11 +316,7 @@ const Stop = () => {
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
const [termSignalLabel, setTermSignalLabel] =
|
||||
createSignal<TerminationSignalLabel>({
|
||||
signal: "default" as TerminationSignal,
|
||||
label: "",
|
||||
});
|
||||
const [termSignalLabel, setTermSignalLabel] = useTermSignalLabel();
|
||||
return (
|
||||
<Show
|
||||
when={!actions.stopping}
|
||||
@@ -336,9 +332,7 @@ const Stop = () => {
|
||||
class="orange"
|
||||
onConfirm={() => {
|
||||
client.stop_container(params.id, {
|
||||
stop_signal: ((termSignalLabel().signal as any) === "default"
|
||||
? undefined
|
||||
: termSignalLabel().signal) as TerminationSignal,
|
||||
stop_signal: termSignalLabel().signal,
|
||||
});
|
||||
}}
|
||||
title="stop container"
|
||||
@@ -434,7 +428,7 @@ const TermSignalSelector: Component<{
|
||||
<Show
|
||||
when={
|
||||
deployment()?.state === DockerContainerState.Running &&
|
||||
(deployment()?.deployment.term_signal_labels?.length || 0) > 0
|
||||
(deployment()?.deployment.term_signal_labels?.length || 0) > 1
|
||||
}
|
||||
>
|
||||
<Flex
|
||||
@@ -446,10 +440,7 @@ const TermSignalSelector: Component<{
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={p.termSignalLabel}
|
||||
items={[
|
||||
{ signal: "default", label: "" },
|
||||
...(deployment()?.deployment.term_signal_labels || []),
|
||||
]}
|
||||
items={deployment()?.deployment.term_signal_labels || []}
|
||||
itemMap={({ signal, label }) => (
|
||||
<Flex gap="0.5rem" alignItems="center">
|
||||
<div>{signal}</div>
|
||||
@@ -458,9 +449,7 @@ const TermSignalSelector: Component<{
|
||||
</Show>
|
||||
</Flex>
|
||||
)}
|
||||
onSelect={(signal) =>
|
||||
p.setTermSignalLabel(signal as TerminationSignalLabel)
|
||||
}
|
||||
onSelect={(signal) => p.setTermSignalLabel(signal)}
|
||||
position="bottom right"
|
||||
/>
|
||||
</Flex>
|
||||
@@ -468,4 +457,28 @@ const TermSignalSelector: Component<{
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Component, createResource, createSignal, Show } from "solid-js";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import {
|
||||
combineClasses,
|
||||
deploymentHeaderStateClass,
|
||||
getId,
|
||||
readableVersion,
|
||||
@@ -79,7 +78,7 @@ const Header: Component<{}> = (p) => {
|
||||
<>
|
||||
<Grid
|
||||
gap="0.5rem"
|
||||
class={combineClasses("card shadow")}
|
||||
class="card shadow"
|
||||
style={{
|
||||
position: "relative",
|
||||
cursor: isSemiMobile() ? "pointer" : undefined,
|
||||
@@ -173,7 +172,7 @@ const Header: Component<{}> = (p) => {
|
||||
{server()?.server.name || "unknown"}
|
||||
</A>
|
||||
<div class={deploymentHeaderStateClass(deployment().state)}>
|
||||
{deployment().state}
|
||||
{deployment().state.replaceAll("_", " ")}
|
||||
</div>
|
||||
</Flex>
|
||||
<Show when={status()}>
|
||||
|
||||
@@ -22,7 +22,7 @@ import SimpleTabs from "../../../shared/tabs/SimpleTabs";
|
||||
import ExtraArgs from "./container/ExtraArgs";
|
||||
import WebhookUrl from "./container/WebhookUrl";
|
||||
import RedeployOnBuild from "./container/RedeployOnBuild";
|
||||
import TerminationSignals from "./container/TerminationSignals";
|
||||
import TerminationSignals, { DefaultTerminationSignal, DefaultTerminationTimeout } from "./termination/TerminationSignals";
|
||||
|
||||
const Config: Component<{}> = () => {
|
||||
const { deployment, reset, save, userCanUpdate } = useConfig();
|
||||
@@ -42,7 +42,6 @@ const Config: Component<{}> = () => {
|
||||
<Grid class="config-items scroller" placeItems="start center">
|
||||
<Image />
|
||||
<DockerAccount />
|
||||
<TerminationSignals />
|
||||
<Network />
|
||||
<Restart />
|
||||
<Env />
|
||||
@@ -57,6 +56,19 @@ const Config: Component<{}> = () => {
|
||||
</Grid>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "termination",
|
||||
element: () => (
|
||||
<Grid class="config-items" placeItems="start center" style={{ "margin-bottom": "" }}>
|
||||
<TerminationSignals />
|
||||
<DefaultTerminationSignal />
|
||||
<DefaultTerminationTimeout />
|
||||
<Show when={isMobile()}>
|
||||
<div style={{ height: "1rem" }} />
|
||||
</Show>
|
||||
</Grid>
|
||||
),
|
||||
},
|
||||
(userCanUpdate() || deployment.repo ? true : false) && {
|
||||
title: "frontend",
|
||||
element: () => (
|
||||
|
||||
@@ -62,6 +62,8 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
// console.log("loading deployment");
|
||||
client.get_deployment(params.id).then((deployment) =>
|
||||
set({
|
||||
...deployment,
|
||||
_id: { $oid: params.id } as any,
|
||||
name: deployment.deployment.name,
|
||||
server_id: deployment.deployment.server_id,
|
||||
permissions: deployment.deployment.permissions,
|
||||
@@ -87,6 +89,10 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
repo_mount: deployment.deployment.repo_mount,
|
||||
created_at: deployment.deployment.created_at,
|
||||
updated_at: deployment.deployment.updated_at,
|
||||
redeploy_on_build: deployment.deployment.redeploy_on_build,
|
||||
term_signal_labels: deployment.deployment.term_signal_labels,
|
||||
termination_signal: deployment.deployment.termination_signal,
|
||||
termination_timeout: deployment.deployment.termination_timeout,
|
||||
loaded: true,
|
||||
updated: false,
|
||||
updating: false,
|
||||
@@ -108,11 +114,13 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
|
||||
const save = () => {
|
||||
setDeployment("updating", true);
|
||||
client.update_deployment(deployment).catch((e) => {
|
||||
console.error(e);
|
||||
pushNotification("bad", "update deployment failed");
|
||||
setDeployment("updating", false);
|
||||
});
|
||||
client
|
||||
.update_deployment(deployment)
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
pushNotification("bad", "update deployment failed");
|
||||
setDeployment("updating", false);
|
||||
});
|
||||
};
|
||||
|
||||
let update_unsub = () => {};
|
||||
|
||||
@@ -7,6 +7,8 @@ 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();
|
||||
@@ -33,7 +35,7 @@ const TerminationSignals: Component<{}> = (p) => {
|
||||
<Grid class="config-item shadow">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>termination signals</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<Show when={userCanUpdate() && signals_to_add().length > 0}>
|
||||
<Menu
|
||||
show={menuOpen()}
|
||||
close={() => setMenuOpen(false)}
|
||||
@@ -90,3 +92,65 @@ const TerminationSignals: Component<{}> = (p) => {
|
||||
};
|
||||
|
||||
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 (!isNaN(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>
|
||||
);
|
||||
};
|
||||
@@ -6,22 +6,24 @@ import {
|
||||
onCleanup,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import Convert from "ansi-to-html";
|
||||
import { pushNotification } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { DockerContainerState, Log as LogType } from "../../../../types";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { combineClasses, sanitizeLog } from "../../../../util/helpers";
|
||||
import { useBuffer, useLocalStorageToggle } from "../../../../util/hooks";
|
||||
import Icon from "../../../shared/Icon";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../config/Provider";
|
||||
import s from "./log.module.scss";
|
||||
|
||||
const POLLING_RATE = 5000;
|
||||
|
||||
let interval = -1;
|
||||
|
||||
const convert = new Convert();
|
||||
|
||||
const Log: Component<{
|
||||
log?: LogType;
|
||||
logTail: number;
|
||||
@@ -31,7 +33,6 @@ const Log: Component<{
|
||||
}> = (p) => {
|
||||
const { deployments } = useAppState();
|
||||
const params = useParams();
|
||||
const { userCanUpdate } = useConfig();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
let ref: HTMLDivElement | undefined;
|
||||
let ignore = false;
|
||||
@@ -56,14 +57,15 @@ const Log: Component<{
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
/// the return of this closure is used as the innerHTML of the <pre /> element
|
||||
/// need to make sure this can't be used to inject some script into the page
|
||||
const log = () => {
|
||||
if (deployment()?.state === DockerContainerState.NotDeployed) {
|
||||
return "not deployed";
|
||||
} else {
|
||||
return (
|
||||
(p.error ? p.log?.stderr : p.log?.stdout) ||
|
||||
`no${p.error ? " error" : ""} log`
|
||||
);
|
||||
const log = p.error ? p.log?.stderr : p.log?.stdout;
|
||||
const sanitized = log && sanitizeLog(log);
|
||||
return sanitized ? convert.toHtml(sanitized) : `no${p.error ? " error" : ""} log`;
|
||||
}
|
||||
};
|
||||
const buffer = useBuffer(scrolled, 250);
|
||||
@@ -145,7 +147,7 @@ const Log: Component<{
|
||||
}
|
||||
}}
|
||||
>
|
||||
<pre class={s.Log}>{log()}</pre>
|
||||
<pre class={s.Log} innerHTML={log()} />
|
||||
</div>
|
||||
<Show when={buffer()}>
|
||||
<button
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
}
|
||||
|
||||
.Log {
|
||||
white-space: pre-line;
|
||||
overflow-wrap: anywhere;
|
||||
tab-size: 2;
|
||||
padding: 1rem;
|
||||
margin-bottom: 40vh;
|
||||
}
|
||||
|
||||
58
frontend/src/components/server/tabs/Images.tsx
Normal file
58
frontend/src/components/server/tabs/Images.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Component, For, Show, createEffect, createResource, onCleanup } from "solid-js";
|
||||
import Grid from "../../shared/layout/Grid";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { client } from "../../..";
|
||||
import Flex from "../../shared/layout/Flex";
|
||||
import { readableBytes, readableImageNameTag, readableTimestamp } from "../../../util/helpers";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { Operation } from "../../../types";
|
||||
|
||||
const Images: Component<{}> = (p) => {
|
||||
const params = useParams();
|
||||
const { ws } = useAppState();
|
||||
const [images, { refetch }] = createResource(() => client.get_docker_images(params.id));
|
||||
let unsub = () => {};
|
||||
createEffect(() => {
|
||||
unsub();
|
||||
unsub = ws.subscribe([Operation.PruneImagesServer], (update) => {
|
||||
if (update.target.id === params.id) {
|
||||
refetch()
|
||||
}
|
||||
});
|
||||
});
|
||||
onCleanup(() => unsub());
|
||||
createEffect(() => console.log(images()));
|
||||
return (
|
||||
<Grid class="config">
|
||||
<Grid class="config-items">
|
||||
<Show when={images()}>
|
||||
<For each={images()}>
|
||||
{(image) => {
|
||||
const [name, tag] = readableImageNameTag(image.RepoTags, image.RepoDigests);
|
||||
return (
|
||||
<Flex
|
||||
class="card light hover shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Flex alignItems="center">
|
||||
<h2>{name}</h2>
|
||||
<h2 class="dimmed">{tag}</h2>
|
||||
</Flex>
|
||||
<Flex alignItems="center">
|
||||
<div>{readableBytes(image.Size)}</div>
|
||||
<div class="dimmed">
|
||||
{readableTimestamp(image.Created * 1000)}
|
||||
</div>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</Show>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
export default Images;
|
||||
@@ -9,6 +9,7 @@ import Config from "./config/Config";
|
||||
import { ConfigProvider } from "./config/Provider";
|
||||
import Info from "./Info";
|
||||
import Permissions from "./Permissions";
|
||||
import Images from "./Images";
|
||||
|
||||
const ServerTabs: Component<{}> = (p) => {
|
||||
const { servers } = useAppState();
|
||||
@@ -31,6 +32,10 @@ const ServerTabs: Component<{}> = (p) => {
|
||||
title: "info",
|
||||
element: () => <Info />
|
||||
},
|
||||
server()?.status === ServerStatus.Ok && {
|
||||
title: "images",
|
||||
element: () => <Images />
|
||||
},
|
||||
user().admin && {
|
||||
title: "permissions",
|
||||
element: () => <Permissions />,
|
||||
|
||||
@@ -9,10 +9,15 @@ import {
|
||||
useContext,
|
||||
} from "solid-js";
|
||||
import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client } from "../../../..";
|
||||
import { client, pushNotification } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { useUser } from "../../../../state/UserProvider";
|
||||
import { Server, Operation, PermissionLevel, ServerStatus } from "../../../../types";
|
||||
import {
|
||||
Server,
|
||||
Operation,
|
||||
PermissionLevel,
|
||||
ServerStatus,
|
||||
} from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
|
||||
type ConfigServer = Server & { loaded: boolean; updated: boolean };
|
||||
@@ -49,6 +54,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
client.get_server(params.id).then((server) => {
|
||||
set({
|
||||
...server.server,
|
||||
_id: { $oid: params.id } as any,
|
||||
loaded: true,
|
||||
updated: false,
|
||||
});
|
||||
@@ -66,10 +72,15 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
createEffect(loadNetworks);
|
||||
|
||||
const save = () => {
|
||||
client.update_server(server);
|
||||
client
|
||||
.update_server(server)
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
pushNotification("bad", "update server failed");
|
||||
});
|
||||
};
|
||||
|
||||
let unsub = () => {}
|
||||
let unsub = () => {};
|
||||
createEffect(() => {
|
||||
unsub();
|
||||
unsub = ws.subscribe([Operation.UpdateServer], (update) => {
|
||||
@@ -94,7 +105,8 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
|
||||
const userCanUpdate = () =>
|
||||
user().admin ||
|
||||
servers.get(params.id)!.server.permissions![getId(user())] === PermissionLevel.Update;
|
||||
servers.get(params.id)!.server.permissions![getId(user())] ===
|
||||
PermissionLevel.Update;
|
||||
|
||||
const state = {
|
||||
server,
|
||||
|
||||
@@ -218,6 +218,8 @@ export function useUsernames() {
|
||||
const load = async (userID: string) => {
|
||||
if (userID === "github") {
|
||||
set((s) => ({ ...s, [userID]: "github" }));
|
||||
} else if (userID === "auto redeploy") {
|
||||
set((s) => ({ ...s, [userID]: "auto redeploy" }));
|
||||
} else {
|
||||
const username = await client.get_username(userID);
|
||||
set((s) => ({ ...s, [userID]: username }));
|
||||
|
||||
@@ -26,6 +26,9 @@ code {
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
tab-size: 2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
||||
@@ -106,6 +106,8 @@ export interface Deployment {
|
||||
skip_secret_interp?: boolean;
|
||||
docker_run_args: DockerRunArgs;
|
||||
term_signal_labels?: TerminationSignalLabel[];
|
||||
termination_signal?: TerminationSignal;
|
||||
termination_timeout?: number;
|
||||
build_id?: string;
|
||||
redeploy_on_build?: boolean;
|
||||
build_version?: Version;
|
||||
|
||||
@@ -45,7 +45,6 @@ import {
|
||||
ModifyUserCreateServerBody,
|
||||
ModifyUserEnabledBody,
|
||||
PermissionsUpdateBody,
|
||||
RenameDeploymentBody,
|
||||
StopContainerQuery,
|
||||
UpdateDescriptionBody,
|
||||
} from "./client_types";
|
||||
@@ -220,20 +219,39 @@ export class Client {
|
||||
return this.post(`/api/deployment/${deployment_id}/pull`);
|
||||
}
|
||||
|
||||
deploy_container(deployment_id: string, query?: StopContainerQuery): Promise<Update> {
|
||||
return this.post(`/api/deployment/${deployment_id}/deploy${generateQuery(query as any)}`);
|
||||
deploy_container(
|
||||
deployment_id: string,
|
||||
query?: StopContainerQuery
|
||||
): Promise<Update> {
|
||||
return this.post(
|
||||
`/api/deployment/${deployment_id}/deploy${generateQuery(query as any)}`
|
||||
);
|
||||
}
|
||||
|
||||
start_container(deployment_id: string): Promise<Update> {
|
||||
return this.post(`/api/deployment/${deployment_id}/start_container`);
|
||||
}
|
||||
|
||||
stop_container(deployment_id: string, query?: StopContainerQuery): Promise<Update> {
|
||||
return this.post(`/api/deployment/${deployment_id}/stop_container${generateQuery(query as any)}`);
|
||||
stop_container(
|
||||
deployment_id: string,
|
||||
query?: StopContainerQuery
|
||||
): Promise<Update> {
|
||||
return this.post(
|
||||
`/api/deployment/${deployment_id}/stop_container${generateQuery(
|
||||
query as any
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
remove_container(deployment_id: string, query?: StopContainerQuery): Promise<Update> {
|
||||
return this.post(`/api/deployment/${deployment_id}/remove_container${generateQuery(query as any)}`);
|
||||
remove_container(
|
||||
deployment_id: string,
|
||||
query?: StopContainerQuery
|
||||
): Promise<Update> {
|
||||
return this.post(
|
||||
`/api/deployment/${deployment_id}/remove_container${generateQuery(
|
||||
query as any
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
async download_container_log(
|
||||
@@ -367,7 +385,15 @@ export class Client {
|
||||
return this.post(`/api/server/${server_id}/networks/prune`);
|
||||
}
|
||||
|
||||
get_docker_images(server_id: string): Promise<any[]> {
|
||||
get_docker_images(server_id: string): Promise<
|
||||
{
|
||||
RepoTags: string[];
|
||||
RepoDigests: string[];
|
||||
Size: number;
|
||||
Created: number;
|
||||
Id: string;
|
||||
}[]
|
||||
> {
|
||||
return this.get(`/api/server/${server_id}/images`);
|
||||
}
|
||||
|
||||
|
||||
622
frontend/src/util/client2.ts
Normal file
622
frontend/src/util/client2.ts
Normal file
@@ -0,0 +1,622 @@
|
||||
import axios from "axios";
|
||||
import {
|
||||
BuildStatsQuery,
|
||||
BuildStatsResponse,
|
||||
BuildVersionsQuery,
|
||||
CopyBuildBody,
|
||||
CopyDeploymentBody,
|
||||
CreateBuildBody,
|
||||
CreateDeploymentBody,
|
||||
CreateGroupBody,
|
||||
CreateProcedureBody,
|
||||
CreateSecretBody,
|
||||
CreateServerBody,
|
||||
LoginOptions,
|
||||
ModifyUserCreateBuildBody,
|
||||
ModifyUserCreateServerBody,
|
||||
ModifyUserEnabledBody,
|
||||
PermissionsUpdateBody,
|
||||
StopContainerQuery,
|
||||
UpdateDescriptionBody,
|
||||
} from "./client_types";
|
||||
import { MONITOR_BASE_URL } from "..";
|
||||
import {
|
||||
AwsBuilderConfig,
|
||||
BasicContainerInfo,
|
||||
Build,
|
||||
BuildActionState,
|
||||
BuildVersionsReponse,
|
||||
Deployment,
|
||||
DeploymentActionState,
|
||||
DeploymentWithContainerState,
|
||||
DockerContainerStats,
|
||||
Group,
|
||||
HistoricalStatsQuery,
|
||||
Log,
|
||||
Operation,
|
||||
Procedure,
|
||||
Server,
|
||||
ServerActionState,
|
||||
ServerWithStatus,
|
||||
SystemInformation,
|
||||
SystemStats,
|
||||
SystemStatsQuery,
|
||||
SystemStatsRecord,
|
||||
Update,
|
||||
UpdateTarget,
|
||||
User,
|
||||
UserCredentials,
|
||||
} from "../types";
|
||||
import fileDownload from "js-file-download";
|
||||
import { QueryObject, generateQuery } from "./helpers";
|
||||
|
||||
export function monitor_client(base_url: string, token: string | null) {
|
||||
const state = {
|
||||
base_url,
|
||||
token: token as string | null,
|
||||
login_options: undefined as LoginOptions | undefined,
|
||||
monitor_title: undefined as string | undefined,
|
||||
secrets_cache: {} as Record<string, string[]>,
|
||||
github_accounts_cache: {} as Record<string, string[]>,
|
||||
docker_accounts_cache: {} as Record<string, string[]>,
|
||||
server_version_cache: {} as Record<string, string>,
|
||||
};
|
||||
|
||||
const request = request_client(state);
|
||||
|
||||
const client = {
|
||||
// ===========
|
||||
// AUTH / USER
|
||||
// ===========
|
||||
|
||||
async initialize() {
|
||||
const [loginOptions, monitorTitle] = await Promise.all([
|
||||
client.get_login_options(),
|
||||
client.get_monitor_title(),
|
||||
]);
|
||||
state.login_options = loginOptions;
|
||||
state.monitor_title = monitorTitle;
|
||||
document.title = monitorTitle;
|
||||
const params = new URLSearchParams(location.search);
|
||||
const exchange_token = params.get("token");
|
||||
if (exchange_token) {
|
||||
history.replaceState({}, "", MONITOR_BASE_URL);
|
||||
try {
|
||||
const jwt = await client.exchange_for_jwt(exchange_token);
|
||||
token = jwt;
|
||||
localStorage.setItem("access_token", jwt);
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get_login_options(): Promise<LoginOptions> {
|
||||
return request.get("/auth/options");
|
||||
},
|
||||
|
||||
async login(credentials: UserCredentials) {
|
||||
const jwt: string = await request.post("/auth/local/login", credentials);
|
||||
state.token = jwt;
|
||||
localStorage.setItem("access_token", state.token);
|
||||
return await client.get_user();
|
||||
},
|
||||
|
||||
async signup(credentials: UserCredentials) {
|
||||
const jwt: string = await request.post(
|
||||
"/auth/local/create_user",
|
||||
credentials
|
||||
);
|
||||
state.token = jwt;
|
||||
localStorage.setItem("access_token", state.token);
|
||||
return await client.get_user();
|
||||
},
|
||||
|
||||
logout() {
|
||||
localStorage.removeItem("access_token");
|
||||
state.token = null;
|
||||
},
|
||||
|
||||
async get_user(): Promise<User | false> {
|
||||
if (state.token) {
|
||||
try {
|
||||
return await request.get("/api/user");
|
||||
} catch (error: any) {
|
||||
client.logout();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
get_username(user_id: string): Promise<string> {
|
||||
return request.get(`/api/username/${user_id}`);
|
||||
},
|
||||
|
||||
// admin only
|
||||
list_users(): Promise<User[]> {
|
||||
return request.get("/api/users");
|
||||
},
|
||||
|
||||
// admin only
|
||||
get_user_by_id(user_id: string): Promise<User> {
|
||||
return request.get(`/api/user/${user_id}`);
|
||||
},
|
||||
|
||||
exchange_for_jwt(exchange_token: string): Promise<string> {
|
||||
return request.post("/auth/exchange", { token: exchange_token });
|
||||
},
|
||||
|
||||
create_api_secret(body: CreateSecretBody): Promise<string> {
|
||||
return request.post("/api/secret/create", body);
|
||||
},
|
||||
|
||||
delete_api_secret(name: string): Promise<undefined> {
|
||||
return request.delete(`/api/secret/delete/${name}`);
|
||||
},
|
||||
|
||||
// ====
|
||||
// MISC
|
||||
// ====
|
||||
|
||||
get_github_webhook_base_url(): Promise<string> {
|
||||
return request.get("/api/github_webhook_base_url");
|
||||
},
|
||||
|
||||
update_description(body: UpdateDescriptionBody): Promise<undefined> {
|
||||
return request.post("/api/update_description", body);
|
||||
},
|
||||
|
||||
get_monitor_title(): Promise<string> {
|
||||
return request.get("/api/title");
|
||||
},
|
||||
|
||||
list_updates(
|
||||
offset: number,
|
||||
target?: UpdateTarget,
|
||||
show_builds?: boolean,
|
||||
operations?: Operation[]
|
||||
): Promise<Update[]> {
|
||||
return request.get(
|
||||
`/api/update/list${generateQuery({
|
||||
offset,
|
||||
type: target && target.type,
|
||||
id: target && target.id,
|
||||
show_builds,
|
||||
operations: operations?.join(","),
|
||||
})}`
|
||||
);
|
||||
},
|
||||
|
||||
// ==========
|
||||
// DEPLOYMENT
|
||||
// ==========
|
||||
|
||||
list_deployments(
|
||||
query?: QueryObject
|
||||
): Promise<DeploymentWithContainerState[]> {
|
||||
return request.get("/api/deployment/list" + generateQuery(query));
|
||||
},
|
||||
|
||||
get_deployment(id: string): Promise<DeploymentWithContainerState> {
|
||||
return request.get(`/api/deployment/${id}`);
|
||||
},
|
||||
|
||||
get_deployment_action_state(id: string): Promise<DeploymentActionState> {
|
||||
return request.get(`/api/deployment/${id}/action_state`);
|
||||
},
|
||||
|
||||
get_deployment_container_log(id: string, tail?: number): Promise<Log> {
|
||||
return request.get(`/api/deployment/${id}/log${generateQuery({ tail })}`);
|
||||
},
|
||||
|
||||
get_deployment_container_stats(id: string): Promise<DockerContainerStats> {
|
||||
return request.get(`/api/deployment/${id}/stats`);
|
||||
},
|
||||
|
||||
get_deployment_deployed_version(id: string): Promise<string> {
|
||||
return request.get(`/api/deployment/${id}/deployed_version`);
|
||||
},
|
||||
|
||||
create_deployment(body: CreateDeploymentBody): Promise<Deployment> {
|
||||
return request.post("/api/deployment/create", body);
|
||||
},
|
||||
|
||||
create_full_deployment(deployment: Deployment): Promise<Deployment> {
|
||||
return request.post("/api/deployment/create_full", deployment);
|
||||
},
|
||||
|
||||
copy_deployment(
|
||||
target_id: string,
|
||||
body: CopyDeploymentBody
|
||||
): Promise<Deployment> {
|
||||
return request.post(`/api/deployment/${target_id}/copy`, body);
|
||||
},
|
||||
|
||||
delete_deployment(id: string): Promise<Deployment> {
|
||||
return request.delete(`/api/deployment/${id}/delete`);
|
||||
},
|
||||
|
||||
update_deployment(deployment: Deployment): Promise<Deployment> {
|
||||
return request.patch("/api/deployment/update", deployment);
|
||||
},
|
||||
|
||||
rename_deployment(deployment_id: string, new_name: string) {
|
||||
return request.patch(`/api/deployment/${deployment_id}/rename`, {
|
||||
new_name,
|
||||
});
|
||||
},
|
||||
|
||||
reclone_deployment(deployment_id: string): Promise<Update> {
|
||||
return request.post(`/api/deployment/${deployment_id}/reclone`);
|
||||
},
|
||||
|
||||
pull_deployment(deployment_id: string): Promise<Update> {
|
||||
return request.post(`/api/deployment/${deployment_id}/pull`);
|
||||
},
|
||||
|
||||
deploy_container(
|
||||
deployment_id: string,
|
||||
query?: StopContainerQuery
|
||||
): Promise<Update> {
|
||||
return request.post(
|
||||
`/api/deployment/${deployment_id}/deploy${generateQuery(query as any)}`
|
||||
);
|
||||
},
|
||||
|
||||
start_container(deployment_id: string): Promise<Update> {
|
||||
return request.post(`/api/deployment/${deployment_id}/start_container`);
|
||||
},
|
||||
|
||||
stop_container(
|
||||
deployment_id: string,
|
||||
query?: StopContainerQuery
|
||||
): Promise<Update> {
|
||||
return request.post(
|
||||
`/api/deployment/${deployment_id}/stop_container${generateQuery(
|
||||
query as any
|
||||
)}`
|
||||
);
|
||||
},
|
||||
|
||||
remove_container(
|
||||
deployment_id: string,
|
||||
query?: StopContainerQuery
|
||||
): Promise<Update> {
|
||||
return request.post(
|
||||
`/api/deployment/${deployment_id}/remove_container${generateQuery(
|
||||
query as any
|
||||
)}`
|
||||
);
|
||||
},
|
||||
|
||||
async download_container_log(
|
||||
id: string,
|
||||
name: string,
|
||||
error?: boolean | undefined
|
||||
) {
|
||||
const log = await client.get_deployment_container_log(id, 5000);
|
||||
const date = new Date();
|
||||
fileDownload(
|
||||
(error ? log.stderr : log.stdout) || "no log",
|
||||
`${name}-${error ? "error-" : ""}log-${date
|
||||
.toLocaleDateString()
|
||||
.replaceAll("/", "-")}.txt`
|
||||
);
|
||||
},
|
||||
|
||||
// ======
|
||||
// SERVER
|
||||
// ======
|
||||
|
||||
list_servers(query?: QueryObject): Promise<ServerWithStatus[]> {
|
||||
return request.get("/api/server/list" + generateQuery(query));
|
||||
},
|
||||
|
||||
get_server(server_id: string): Promise<ServerWithStatus> {
|
||||
return request.get(`/api/server/${server_id}`);
|
||||
},
|
||||
|
||||
get_server_action_state(id: string): Promise<ServerActionState> {
|
||||
return request.get(`/api/server/${id}/action_state`);
|
||||
},
|
||||
|
||||
get_server_github_accounts(id: string): Promise<string[]> {
|
||||
return request.get(`/api/server/${id}/github_accounts`);
|
||||
},
|
||||
|
||||
get_server_docker_accounts(id: string): Promise<string[]> {
|
||||
return request.get(`/api/server/${id}/docker_accounts`);
|
||||
},
|
||||
|
||||
get_server_available_secrets(id: string): Promise<string[]> {
|
||||
return request.get(`/api/server/${id}/secrets`);
|
||||
},
|
||||
|
||||
get_server_version(id: string): Promise<string> {
|
||||
return request.get(`/api/server/${id}/version`);
|
||||
},
|
||||
|
||||
get_server_system_info(id: string): Promise<SystemInformation> {
|
||||
return request.get(`/api/server/${id}/system_information`);
|
||||
},
|
||||
|
||||
create_server(body: CreateServerBody): Promise<Server> {
|
||||
return request.post("/api/server/create", body);
|
||||
},
|
||||
|
||||
create_full_server(server: Server): Promise<Server> {
|
||||
return request.post("/api/server/create_full", server);
|
||||
},
|
||||
|
||||
delete_server(id: string): Promise<Server> {
|
||||
return request.delete(`/api/server/${id}/delete`);
|
||||
},
|
||||
|
||||
update_server(server: Server): Promise<Server> {
|
||||
return request.patch("/api/server/update", server);
|
||||
},
|
||||
|
||||
get_server_stats(
|
||||
server_id: string,
|
||||
query?: SystemStatsQuery
|
||||
): Promise<SystemStats> {
|
||||
return request.get(
|
||||
`/api/server/${server_id}/stats${generateQuery(query as any)}`
|
||||
);
|
||||
},
|
||||
|
||||
get_server_stats_history(
|
||||
server_id: string,
|
||||
query?: HistoricalStatsQuery
|
||||
): Promise<SystemStatsRecord[]> {
|
||||
return request.get(
|
||||
`/api/server/${server_id}/stats/history${generateQuery(query as any)}`
|
||||
);
|
||||
},
|
||||
|
||||
get_server_stats_at_ts(
|
||||
server_id: string,
|
||||
ts: number
|
||||
): Promise<SystemStatsRecord> {
|
||||
return request.get(`/api/server/${server_id}/stats/at_ts?ts=${ts}`);
|
||||
},
|
||||
|
||||
get_docker_networks(server_id: string): Promise<any[]> {
|
||||
return request.get(`/api/server/${server_id}/networks`);
|
||||
},
|
||||
|
||||
prune_docker_networks(server_id: string): Promise<Log> {
|
||||
return request.post(`/api/server/${server_id}/networks/prune`);
|
||||
},
|
||||
|
||||
get_docker_images(server_id: string): Promise<
|
||||
{
|
||||
RepoTags: string[];
|
||||
RepoDigests: string[];
|
||||
Size: number;
|
||||
Created: number;
|
||||
Id: string;
|
||||
}[]
|
||||
> {
|
||||
return request.get(`/api/server/${server_id}/images`);
|
||||
},
|
||||
|
||||
prune_docker_images(server_id: string): Promise<Log> {
|
||||
return request.post(`/api/server/${server_id}/images/prune`);
|
||||
},
|
||||
|
||||
get_docker_containers(server_id: string): Promise<BasicContainerInfo[]> {
|
||||
return request.get(`/api/server/${server_id}/containers`);
|
||||
},
|
||||
|
||||
prune_docker_containers(server_id: string): Promise<Log> {
|
||||
return request.post(`/api/server/${server_id}/containers/prune`);
|
||||
},
|
||||
|
||||
// =====
|
||||
// BUILD
|
||||
// =====
|
||||
|
||||
list_builds(query?: QueryObject): Promise<Build[]> {
|
||||
return request.get("/api/build/list" + generateQuery(query));
|
||||
},
|
||||
|
||||
get_build(build_id: string): Promise<Build> {
|
||||
return request.get(`/api/build/${build_id}`);
|
||||
},
|
||||
|
||||
get_build_action_state(id: string): Promise<BuildActionState> {
|
||||
return request.get(`/api/build/${id}/action_state`);
|
||||
},
|
||||
|
||||
get_build_versions(
|
||||
id: string,
|
||||
query?: BuildVersionsQuery
|
||||
): Promise<BuildVersionsReponse[]> {
|
||||
return request.get(
|
||||
`/api/build/${id}/versions${generateQuery(query as any)}`
|
||||
);
|
||||
},
|
||||
|
||||
get_build_stats(query?: BuildStatsQuery): Promise<BuildStatsResponse> {
|
||||
return request.get(`/api/build/stats${generateQuery(query as any)}`);
|
||||
},
|
||||
|
||||
create_build(body: CreateBuildBody): Promise<Build> {
|
||||
return request.post("/api/build/create", body);
|
||||
},
|
||||
|
||||
create_full_build(build: Build): Promise<Build> {
|
||||
return request.post("/api/build/create_full", build);
|
||||
},
|
||||
|
||||
copy_build(target_id: string, body: CopyBuildBody): Promise<Build> {
|
||||
return request.post(`/api/build/${target_id}/copy`, body);
|
||||
},
|
||||
|
||||
delete_build(id: string): Promise<Build> {
|
||||
return request.delete(`/api/build/${id}/delete`);
|
||||
},
|
||||
|
||||
update_build(build: Build): Promise<Build> {
|
||||
return request.patch("/api/build/update", build);
|
||||
},
|
||||
|
||||
build(build_id: string): Promise<Update> {
|
||||
return request.post(`/api/build/${build_id}/build`);
|
||||
},
|
||||
|
||||
reclone_build(id: string): Promise<Update> {
|
||||
return request.post(`/api/build/${id}/reclone`);
|
||||
},
|
||||
|
||||
get_aws_builder_defaults(): Promise<AwsBuilderConfig> {
|
||||
return request.get("/api/build/aws_builder_defaults");
|
||||
},
|
||||
|
||||
get_docker_organizations(): Promise<string[]> {
|
||||
return request.get("/api/build/docker_organizations");
|
||||
},
|
||||
|
||||
// =========
|
||||
// PROCEDURE
|
||||
// =========
|
||||
|
||||
list_procedures(query?: QueryObject): Promise<Procedure[]> {
|
||||
return request.get("/api/procedure/list" + generateQuery(query));
|
||||
},
|
||||
|
||||
get_procedure(procedure_id: string): Promise<Procedure> {
|
||||
return request.get(`/api/procedure/${procedure_id}`);
|
||||
},
|
||||
|
||||
create_procedure(body: CreateProcedureBody): Promise<Procedure> {
|
||||
return request.post("/api/procedure/create", body);
|
||||
},
|
||||
|
||||
create_full_procedure(procedure: Procedure): Promise<Procedure> {
|
||||
return request.post("/api/procedure/create_full", procedure);
|
||||
},
|
||||
|
||||
delete_procedure(id: string): Promise<Procedure> {
|
||||
return request.delete(`/api/procedure/${id}/delete`);
|
||||
},
|
||||
|
||||
update_procedure(procedure: Procedure): Promise<Procedure> {
|
||||
return request.patch("/api/procedure/update", procedure);
|
||||
},
|
||||
|
||||
run_procedure(id: string): Promise<Update> {
|
||||
return request.post(`/api/procedure/${id}/run`);
|
||||
},
|
||||
|
||||
// =====
|
||||
// GROUP
|
||||
// =====
|
||||
|
||||
list_groups(query?: QueryObject): Promise<Group[]> {
|
||||
return request.get("/api/group/list" + generateQuery(query));
|
||||
},
|
||||
|
||||
get_group(group_id: string): Promise<Group> {
|
||||
return request.get(`/api/group/${group_id}`);
|
||||
},
|
||||
|
||||
create_group(body: CreateGroupBody): Promise<Group> {
|
||||
return request.post("/api/group/create", body);
|
||||
},
|
||||
|
||||
create_full_group(group: Group): Promise<Group> {
|
||||
return request.post("/api/group/create_full", group);
|
||||
},
|
||||
|
||||
delete_group(id: string): Promise<Group> {
|
||||
return request.delete(`/api/group/${id}/delete`);
|
||||
},
|
||||
|
||||
update_group(group: Group): Promise<Group> {
|
||||
return request.patch("/api/group/update", group);
|
||||
},
|
||||
|
||||
// ===========
|
||||
// PERMISSIONS
|
||||
// ===========
|
||||
|
||||
update_user_permissions_on_target(
|
||||
body: PermissionsUpdateBody
|
||||
): Promise<Update> {
|
||||
return request.post("/api/permissions/update", body);
|
||||
},
|
||||
|
||||
modify_user_enabled(body: ModifyUserEnabledBody): Promise<Update> {
|
||||
return request.post("/api/permissions/modify_enabled", body);
|
||||
},
|
||||
|
||||
modify_user_create_server_permissions(
|
||||
body: ModifyUserCreateServerBody
|
||||
): Promise<Update> {
|
||||
return request.post("/api/permissions/modify_create_server", body);
|
||||
},
|
||||
|
||||
modify_user_create_build_permissions(
|
||||
body: ModifyUserCreateBuildBody
|
||||
): Promise<Update> {
|
||||
return request.post("/api/permissions/modify_create_build", body);
|
||||
},
|
||||
};
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
function request_client(state: { base_url: string; token: string | null }) {
|
||||
return {
|
||||
async get<R = any>(url: string): Promise<R> {
|
||||
return await axios({
|
||||
method: "get",
|
||||
url: state.base_url + url,
|
||||
headers: {
|
||||
authorization: state.token ? `Bearer ${state.token}` : undefined,
|
||||
},
|
||||
}).then(({ data }) => data);
|
||||
},
|
||||
async post<B = any, R = any>(url: string, body?: B): Promise<R> {
|
||||
return await axios({
|
||||
method: "post",
|
||||
url: state.base_url + url,
|
||||
headers: {
|
||||
authorization: `Bearer ${state.token}`,
|
||||
},
|
||||
data: body,
|
||||
}).then(({ data }) => data);
|
||||
},
|
||||
async patch<B = any, R = any>(url: string, body: B): Promise<R> {
|
||||
return await axios({
|
||||
method: "patch",
|
||||
url: state.base_url + url,
|
||||
headers: {
|
||||
authorization: `Bearer ${state.token}`,
|
||||
},
|
||||
data: body,
|
||||
}).then(({ data }) => data);
|
||||
},
|
||||
async delete<R = any>(url: string): Promise<R> {
|
||||
return await axios({
|
||||
method: "delete",
|
||||
url: state.base_url + url,
|
||||
headers: {
|
||||
authorization: `Bearer ${state.token}`,
|
||||
},
|
||||
}).then(({ data }) => data);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function login_with_github() {
|
||||
location.replace(`${MONITOR_BASE_URL}/auth/github/login`);
|
||||
}
|
||||
|
||||
export function login_with_google() {
|
||||
location.replace(`${MONITOR_BASE_URL}/auth/google/login`);
|
||||
}
|
||||
@@ -1,17 +1,12 @@
|
||||
import {
|
||||
Build,
|
||||
Deployment,
|
||||
DeploymentWithContainerState,
|
||||
DockerContainerState,
|
||||
EnvironmentVar,
|
||||
Server,
|
||||
ServerStatus,
|
||||
ServerWithStatus,
|
||||
Timelength,
|
||||
UpdateTarget,
|
||||
User,
|
||||
Version,
|
||||
} from "../types";
|
||||
import sanitizeHtml from "sanitize-html";
|
||||
|
||||
export function combineClasses(...classes: (string | false | undefined)[]) {
|
||||
return classes.filter((c) => (c ? true : false)).join(" ");
|
||||
@@ -23,6 +18,15 @@ export function inPx(num: number) {
|
||||
|
||||
export type QueryObject = Record<string, string | number | boolean | undefined>;
|
||||
|
||||
export function sanitizeLog(log: string) {
|
||||
return sanitizeHtml(log, {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.filter(
|
||||
(tag) => tag !== "script"
|
||||
),
|
||||
allowedAttributes: sanitizeHtml.defaults.allowedAttributes,
|
||||
});
|
||||
}
|
||||
|
||||
export function generateQuery(query?: QueryObject) {
|
||||
if (query) {
|
||||
const q = Object.keys(query)
|
||||
@@ -251,3 +255,33 @@ export function readableUserType(user: User) {
|
||||
return "local";
|
||||
}
|
||||
}
|
||||
|
||||
const KB = 1024;
|
||||
const MB = 1024 ** 2;
|
||||
const GB = 1024 ** 3;
|
||||
|
||||
export function readableBytes(bytes: number) {
|
||||
if (bytes > GB) {
|
||||
return `${(bytes / GB).toFixed(1)} GiB`;
|
||||
} else if (bytes > MB) {
|
||||
return `${(bytes / MB).toFixed(1)} MiB`;
|
||||
} else {
|
||||
return `${(bytes / KB).toFixed(1)} KiB`;
|
||||
}
|
||||
}
|
||||
|
||||
export function readableImageId(id: string) {
|
||||
return id.replaceAll("sha256:", "").slice(0, 12);
|
||||
}
|
||||
|
||||
export function readableImageNameTag(repoTags?: string[], repoDigests?: string[]): [string, string] {
|
||||
if (repoTags && repoTags.length > 0) {
|
||||
const [name, tag] = repoTags[0].split(":");
|
||||
return [name, tag];
|
||||
} else if (repoDigests && repoDigests.length > 0) {
|
||||
const [name] = repoDigests[0].split("@");
|
||||
return [name, "none"];
|
||||
} else {
|
||||
return ["none", "none"]
|
||||
}
|
||||
}
|
||||
@@ -447,6 +447,13 @@
|
||||
dependencies:
|
||||
"@tanstack/query-core" "4.26.0"
|
||||
|
||||
"@types/sanitize-html@^2.9.0":
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.9.0.tgz#5b609f7592de22ef80a0930c39670329753dca1b"
|
||||
integrity sha512-4fP/kEcKNj2u39IzrxWYuf/FnCCwwQCpif6wwY6ROUS1EPRIfWJjGkY3HIowY1EX/VbX5e86yq8AAE7UPMgATg==
|
||||
dependencies:
|
||||
htmlparser2 "^8.0.0"
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
@@ -454,6 +461,13 @@ ansi-styles@^3.2.1:
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-to-html@^0.7.2:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.7.2.tgz#a92c149e4184b571eb29a0135ca001a8e2d710cb"
|
||||
integrity sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==
|
||||
dependencies:
|
||||
entities "^2.2.0"
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
|
||||
@@ -580,16 +594,61 @@ debug@^4.1.0:
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
deepmerge@^4.2.2:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
|
||||
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
dom-serializer@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
|
||||
integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.2"
|
||||
entities "^4.2.0"
|
||||
|
||||
domelementtype@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
|
||||
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
||||
|
||||
domhandler@^5.0.2, domhandler@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
|
||||
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
|
||||
domutils@^3.0.1:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
|
||||
integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==
|
||||
dependencies:
|
||||
dom-serializer "^2.0.0"
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
|
||||
electron-to-chromium@^1.4.251:
|
||||
version "1.4.284"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
|
||||
integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
|
||||
|
||||
entities@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||
|
||||
entities@^4.2.0, entities@^4.4.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
esbuild@^0.16.3:
|
||||
version "0.16.10"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.10.tgz#d485c28f1626a3f9c1796c952e4cd0561f0031bb"
|
||||
@@ -628,6 +687,11 @@ escape-string-regexp@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
||||
|
||||
escape-string-regexp@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
fancy-canvas@0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/fancy-canvas/-/fancy-canvas-0.2.2.tgz#33fd4976724169a1eda5015f515a2a1302d1ec91"
|
||||
@@ -698,6 +762,16 @@ html-entities@2.3.2:
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488"
|
||||
integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==
|
||||
|
||||
htmlparser2@^8.0.0:
|
||||
version "8.0.2"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21"
|
||||
integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
domutils "^3.0.1"
|
||||
entities "^4.4.0"
|
||||
|
||||
immutable@^4.0.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.2.0.tgz#c91f09108ed7504c2f0faec7222f40178ff97b11"
|
||||
@@ -734,6 +808,11 @@ is-number@^7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
is-plain-object@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
||||
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
|
||||
|
||||
is-what@^4.1.8:
|
||||
version "4.1.8"
|
||||
resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.8.tgz#0e2a8807fda30980ddb2571c79db3d209b14cbe4"
|
||||
@@ -802,6 +881,11 @@ nanoid@^3.3.4:
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
|
||||
|
||||
nanoid@^3.3.6:
|
||||
version "3.3.6"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
|
||||
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
|
||||
|
||||
node-releases@^2.0.6:
|
||||
version "2.0.8"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae"
|
||||
@@ -812,6 +896,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
parse-srcset@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
|
||||
integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==
|
||||
|
||||
path-parse@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
@@ -827,6 +916,15 @@ picomatch@^2.0.4, picomatch@^2.2.1:
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
postcss@^8.3.11:
|
||||
version "8.4.23"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
|
||||
integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
|
||||
dependencies:
|
||||
nanoid "^3.3.6"
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
postcss@^8.4.20:
|
||||
version "8.4.20"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56"
|
||||
@@ -869,6 +967,18 @@ rollup@^3.7.0:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
sanitize-html@^2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.10.0.tgz#74d28848dfcf72c39693139131895c78900ab452"
|
||||
integrity sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==
|
||||
dependencies:
|
||||
deepmerge "^4.2.2"
|
||||
escape-string-regexp "^4.0.0"
|
||||
htmlparser2 "^8.0.0"
|
||||
is-plain-object "^5.0.0"
|
||||
parse-srcset "^1.0.2"
|
||||
postcss "^8.3.11"
|
||||
|
||||
sass@^1.57.1:
|
||||
version "1.57.1"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.57.1.tgz#dfafd46eb3ab94817145e8825208ecf7281119b5"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "db_client"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { package = "monitor_types", path = "../types" }
|
||||
mungos = "0.3.8"
|
||||
mungos = "0.3.19"
|
||||
anyhow = "1.0"
|
||||
@@ -1,6 +1,9 @@
|
||||
use anyhow::Context;
|
||||
use mungos::{Collection, Mungos};
|
||||
use types::{Action, Build, Deployment, Group, Procedure, Server, SystemStatsRecord, Update, User};
|
||||
use types::{
|
||||
Action, Build, Deployment, Group, PeripheryCommand, Procedure, Server, SystemStatsRecord,
|
||||
Update, User,
|
||||
};
|
||||
|
||||
pub async fn users_collection(mungos: &Mungos, db_name: &str) -> anyhow::Result<Collection<User>> {
|
||||
let coll = mungos.collection(db_name, "users");
|
||||
@@ -133,3 +136,17 @@ pub async fn server_stats_collection(
|
||||
.context("failed at creating ts index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
pub async fn commands_collection(
|
||||
mungos: &Mungos,
|
||||
db_name: &str,
|
||||
) -> anyhow::Result<Collection<PeripheryCommand>> {
|
||||
let coll = mungos.collection(db_name, "commands");
|
||||
coll.create_unique_index("name")
|
||||
.await
|
||||
.context("failed at creating name index")?;
|
||||
coll.create_index("server_id")
|
||||
.await
|
||||
.context("failed at creating server_id index")?;
|
||||
Ok(coll)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use collections::{
|
||||
actions_collection, builds_collection, deployments_collection, groups_collection,
|
||||
procedures_collection, server_stats_collection, servers_collection, updates_collection,
|
||||
users_collection,
|
||||
actions_collection, builds_collection, commands_collection, deployments_collection,
|
||||
groups_collection, procedures_collection, server_stats_collection, servers_collection,
|
||||
updates_collection, users_collection,
|
||||
};
|
||||
use mungos::{Collection, Mungos};
|
||||
use types::{
|
||||
Action, Build, Deployment, Group, MongoConfig, PermissionLevel, Procedure, Server,
|
||||
SystemStatsRecord, Update, User,
|
||||
Action, Build, Deployment, Group, MongoConfig, PeripheryCommand, PermissionLevel, Procedure,
|
||||
Server, SystemStatsRecord, Update, User,
|
||||
};
|
||||
|
||||
mod collections;
|
||||
@@ -20,6 +20,7 @@ pub struct DbClient {
|
||||
pub procedures: Collection<Procedure>,
|
||||
pub actions: Collection<Action>,
|
||||
pub groups: Collection<Group>,
|
||||
pub commands: Collection<PeripheryCommand>,
|
||||
pub updates: Collection<Update>,
|
||||
pub stats: Collection<SystemStatsRecord>,
|
||||
}
|
||||
@@ -61,6 +62,9 @@ impl DbClient {
|
||||
stats: server_stats_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make stats collection"),
|
||||
commands: commands_collection(&mungos, db_name)
|
||||
.await
|
||||
.expect("failed to make commands collection"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,4 +201,28 @@ impl DbClient {
|
||||
.unwrap_or_default();
|
||||
Ok(permissions)
|
||||
}
|
||||
|
||||
pub async fn get_command(&self, command_id: &str) -> anyhow::Result<PeripheryCommand> {
|
||||
let command = self
|
||||
.commands
|
||||
.find_one_by_id(command_id)
|
||||
.await
|
||||
.context(format!("failed at mongo query for command {command_id}"))?
|
||||
.ok_or(anyhow!("command at {command_id} doesn't exist"))?;
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
pub async fn get_user_permission_on_command(
|
||||
&self,
|
||||
user_id: &str,
|
||||
command_id: &str,
|
||||
) -> anyhow::Result<PermissionLevel> {
|
||||
let permissions = *self
|
||||
.get_command(command_id)
|
||||
.await?
|
||||
.permissions
|
||||
.get(user_id)
|
||||
.unwrap_or_default();
|
||||
Ok(permissions)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_helpers"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "helpers used as dependency for mogh tech monitor"
|
||||
@@ -12,7 +12,4 @@ license = "GPL-3.0-or-later"
|
||||
types = { package = "monitor_types", path = "../types" }
|
||||
anyhow = "1.0"
|
||||
axum = "0.6"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
toml = "0.7"
|
||||
rand = "0.8"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_client"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "a client to interact with the monitor system"
|
||||
@@ -9,10 +9,10 @@ license = "GPL-3.0-or-later"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
monitor_types = "0.3.2"
|
||||
monitor_types = "0.3.4"
|
||||
# monitor_types = { path = "../types" }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio-tungstenite = { version = "0.19", features=["native-tls"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
tokio-util = "0.7"
|
||||
anyhow = "1.0"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "periphery_client"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { package = "monitor_types", path = "../types" }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio-tungstenite = { version = "0.19", features=["native-tls"] }
|
||||
tokio = "1.25"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = "1.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_types"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "types for the mogh tech monitor"
|
||||
@@ -10,13 +10,12 @@ license = "GPL-3.0-or-later"
|
||||
|
||||
[dependencies]
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
bson = "2.4"
|
||||
bson = "2.6"
|
||||
strum = "0.24"
|
||||
strum_macros = "0.24"
|
||||
diff-struct = "0.5"
|
||||
bollard = "0.14.0"
|
||||
derive_builder = "0.12"
|
||||
typeshare = "1.0.0"
|
||||
typeshare = "1.0.1"
|
||||
chrono = "0.4"
|
||||
anyhow = "1.0"
|
||||
@@ -46,11 +46,21 @@ pub struct Deployment {
|
||||
#[diff(attr(#[serde(skip_serializing_if = "docker_run_args_diff_no_change")]))]
|
||||
pub docker_run_args: DockerRunArgs,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[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)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub build_id: Option<String>,
|
||||
@@ -99,6 +109,14 @@ pub struct Deployment {
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
fn default_termination_timeout() -> i32 {
|
||||
10
|
||||
}
|
||||
|
||||
fn default_term_signal_labels() -> Vec<TerminationSignalLabel> {
|
||||
vec![TerminationSignalLabel::default()]
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct DeploymentWithContainerState {
|
||||
|
||||
@@ -2,7 +2,7 @@ use diff::{Diff, OptionDiff, VecDiff};
|
||||
|
||||
use crate::{
|
||||
deployment::{DockerRunArgsDiff, RestartModeDiff, TerminationSignalDiff},
|
||||
TimelengthDiff,
|
||||
CommandDiff, TimelengthDiff,
|
||||
};
|
||||
|
||||
pub fn f64_diff_no_change(f64_diff: &f64) -> bool {
|
||||
@@ -13,6 +13,10 @@ pub fn f32_diff_no_change(f32_diff: &f32) -> bool {
|
||||
*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
|
||||
where
|
||||
<T as Diff>::Repr: PartialEq,
|
||||
@@ -52,3 +56,7 @@ pub fn timelength_diff_no_change(timelength: &TimelengthDiff) -> bool {
|
||||
pub fn termination_signal_diff_no_change(term_signal: &TerminationSignalDiff) -> bool {
|
||||
term_signal == &TerminationSignalDiff::NoChange
|
||||
}
|
||||
|
||||
pub fn command_diff_no_change(command: &CommandDiff) -> bool {
|
||||
command.command.is_none() && command.path.is_none()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ mod config;
|
||||
mod deployment;
|
||||
mod diff;
|
||||
mod group;
|
||||
mod periphery_command;
|
||||
mod procedure;
|
||||
mod server;
|
||||
mod update;
|
||||
@@ -27,6 +28,7 @@ pub use build::*;
|
||||
pub use config::*;
|
||||
pub use deployment::*;
|
||||
pub use group::*;
|
||||
pub use periphery_command::*;
|
||||
pub use procedure::*;
|
||||
pub use server::*;
|
||||
pub use update::*;
|
||||
@@ -49,7 +51,7 @@ pub struct CloneArgs {
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Diff)]
|
||||
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Diff)]
|
||||
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||
pub struct Command {
|
||||
#[serde(default)]
|
||||
@@ -125,6 +127,12 @@ pub enum Operation {
|
||||
UpdateProcedure,
|
||||
DeleteProcedure,
|
||||
|
||||
// command
|
||||
CreateCommand,
|
||||
UpdateCommand,
|
||||
DeleteCommand,
|
||||
RunCommand,
|
||||
|
||||
// group
|
||||
CreateGroup,
|
||||
UpdateGroup,
|
||||
|
||||
58
lib/types/src/periphery_command.rs
Normal file
58
lib/types/src/periphery_command.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use bson::serde_helpers::hex_string_as_object_id;
|
||||
use derive_builder::Builder;
|
||||
use diff::Diff;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::{diff::command_diff_no_change, Command, PermissionsMap};
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, Diff, Builder)]
|
||||
#[diff(attr(#[derive(Debug, Serialize)]))]
|
||||
pub struct PeripheryCommand {
|
||||
#[serde(
|
||||
default,
|
||||
rename = "_id",
|
||||
skip_serializing_if = "String::is_empty",
|
||||
with = "hex_string_as_object_id"
|
||||
)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
#[builder(setter(skip))]
|
||||
pub id: String,
|
||||
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub name: String, // must be formatted to be compat with docker
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub description: String,
|
||||
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub server_id: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[diff(attr(#[serde(skip_serializing)]))]
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "command_diff_no_change")]))]
|
||||
pub command: Command,
|
||||
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
#[diff(attr(#[serde(skip)]))]
|
||||
#[builder(setter(skip))]
|
||||
pub created_at: String,
|
||||
#[serde(default)]
|
||||
#[diff(attr(#[serde(skip)]))]
|
||||
#[builder(setter(skip))]
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct CommandActionState {
|
||||
pub running: bool,
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Build, BuildActionState, CloneArgs, Deployment, DeploymentActionState, Group, PermissionLevel,
|
||||
PermissionsMap, Procedure, Server, ServerActionState,
|
||||
Build, BuildActionState, CloneArgs, CommandActionState, Deployment, DeploymentActionState,
|
||||
Group, PeripheryCommand, PermissionLevel, PermissionsMap, Procedure, Server, ServerActionState,
|
||||
};
|
||||
|
||||
pub trait Permissioned {
|
||||
@@ -41,6 +41,12 @@ impl Permissioned for Group {
|
||||
}
|
||||
}
|
||||
|
||||
impl Permissioned for PeripheryCommand {
|
||||
fn permissions_map(&self) -> &PermissionsMap {
|
||||
&self.permissions
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Busy {
|
||||
fn busy(&self) -> bool;
|
||||
}
|
||||
@@ -70,6 +76,12 @@ impl Busy for BuildActionState {
|
||||
}
|
||||
}
|
||||
|
||||
impl Busy for CommandActionState {
|
||||
fn busy(&self) -> bool {
|
||||
self.running
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Deployment> for CloneArgs {
|
||||
fn from(d: &Deployment) -> Self {
|
||||
CloneArgs {
|
||||
|
||||
@@ -75,6 +75,7 @@ pub enum UpdateTarget {
|
||||
Server(String),
|
||||
Procedure(String),
|
||||
Group(String),
|
||||
Command(String),
|
||||
}
|
||||
|
||||
impl Default for UpdateTarget {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "monitor_periphery"
|
||||
version = "0.3.2"
|
||||
version = "0.3.4"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor periphery binary"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
description = "monitor periphery client"
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[[bin]]
|
||||
@@ -15,23 +15,21 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
||||
types = { package = "monitor_types", path = "../lib/types" }
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
async_timing_util = "0.1.14"
|
||||
tokio = { version = "1.26", features = ["full"] }
|
||||
run_command = { version = "0.0.6", features = ["async_tokio"] }
|
||||
|
||||
tokio = { version = "1.28", features = ["full"] }
|
||||
axum = { version = "0.6", features = ["ws"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
clap = { version = "4.2", features = ["derive"] }
|
||||
futures = "0.3"
|
||||
dotenv = "0.15"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
bollard = "0.14.0"
|
||||
anyhow = "1.0"
|
||||
envy = "0.4"
|
||||
sysinfo = "0.28"
|
||||
toml = "0.7"
|
||||
daemonize = "0.5.0"
|
||||
clap = { version = "4.2", features = ["derive"] }
|
||||
svi = "0.1.3"
|
||||
sysinfo = "0.29"
|
||||
async_timing_util = "0.1.14"
|
||||
svi = "0.1.4"
|
||||
merge_config_files = "0.1.3"
|
||||
parse_csl = "0.1.0"
|
||||
|
||||
@@ -11,21 +11,8 @@ use types::PeripheryConfig;
|
||||
use crate::{HomeDirExtension, PeripheryConfigExtension};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author = "mbecker20 <becker.maxh@gmail.com>")]
|
||||
#[command(about = "monitor periphery client")]
|
||||
#[command(author, about, version)]
|
||||
pub struct Args {
|
||||
/// Run this program as a system daemon
|
||||
#[arg(short, long)]
|
||||
pub daemon: bool,
|
||||
|
||||
/// Sets destination file of periphery stdout logs
|
||||
#[arg(long, default_value = "~/.monitor/periphery.log.out")]
|
||||
pub stdout: String,
|
||||
|
||||
/// Sets destination file of periphery stderr logs
|
||||
#[arg(long, default_value = "~/.monitor/periphery.log.err")]
|
||||
pub stderr: String,
|
||||
|
||||
/// Sets the path of a config file or directory to use. can use multiple times
|
||||
#[arg(short, long)]
|
||||
pub config_path: Option<Vec<String>>,
|
||||
@@ -34,14 +21,18 @@ pub struct Args {
|
||||
#[arg(long)]
|
||||
pub config_keyword: Option<Vec<String>>,
|
||||
|
||||
#[arg(short, long)]
|
||||
/// Merges nested configs, eg. secrets, docker_accounts, github_accounts
|
||||
#[arg(long)]
|
||||
pub merge_nested_config: bool,
|
||||
|
||||
/// Extends config arrays, eg. allowed_ips, passkeys
|
||||
#[arg(long)]
|
||||
pub extend_config_arrays: bool,
|
||||
|
||||
/// Sets the periphery home directory to use instead of $HOME
|
||||
/// ~ will be replaced with this directory
|
||||
#[arg(long)]
|
||||
pub home_dir: Option<String>,
|
||||
|
||||
#[arg(short, long)]
|
||||
version: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -52,14 +43,10 @@ struct Env {
|
||||
config_keywords: String,
|
||||
}
|
||||
|
||||
pub fn load() -> (Args, u16, PeripheryConfigExtension, HomeDirExtension) {
|
||||
pub fn load() -> (u16, PeripheryConfigExtension, HomeDirExtension) {
|
||||
dotenv().ok();
|
||||
let env: Env = envy::from_env().expect("failed to parse env");
|
||||
let args = Args::parse();
|
||||
if args.version {
|
||||
println!("v{}", env!("CARGO_PKG_VERSION"));
|
||||
std::process::exit(0)
|
||||
}
|
||||
let home_dir = get_home_dir(&args.home_dir);
|
||||
let config_paths = args
|
||||
.config_path
|
||||
@@ -83,20 +70,19 @@ pub fn load() -> (Args, u16, PeripheryConfigExtension, HomeDirExtension) {
|
||||
config_paths.clone(),
|
||||
match_keywords,
|
||||
args.merge_nested_config,
|
||||
args.merge_nested_config,
|
||||
args.extend_config_arrays,
|
||||
)
|
||||
.expect("failed at parsing config");
|
||||
let _ = std::fs::create_dir(&config.repo_dir);
|
||||
print_startup_log(config_paths, &args, &config);
|
||||
print_startup_log(config_paths, &config);
|
||||
(
|
||||
args,
|
||||
config.port,
|
||||
Extension(Arc::new(config)),
|
||||
Extension(Arc::new(home_dir)),
|
||||
)
|
||||
}
|
||||
|
||||
fn print_startup_log(config_paths: Vec<String>, args: &Args, config: &PeripheryConfig) {
|
||||
fn print_startup_log(config_paths: Vec<String>, config: &PeripheryConfig) {
|
||||
println!("\nconfig paths: {config_paths:?}");
|
||||
let mut config_for_print = config.clone();
|
||||
config_for_print.github_accounts = config_for_print
|
||||
@@ -120,9 +106,6 @@ fn print_startup_log(config_paths: Vec<String>, args: &Args, config: &PeripheryC
|
||||
.map(|_| "<SECRET>".to_string())
|
||||
.collect();
|
||||
println!("{config_for_print:#?}");
|
||||
if args.daemon {
|
||||
println!("daemon mode enabled");
|
||||
}
|
||||
println!("starting montior periphery on port {}\n", config.port);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,22 @@ pub async fn stop_container(
|
||||
time: Option<i32>,
|
||||
) -> Log {
|
||||
let command = stop_container_command(container_name, signal, time);
|
||||
run_monitor_command("docker stop", command).await
|
||||
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(
|
||||
@@ -74,7 +89,23 @@ pub async fn stop_and_remove_container(
|
||||
) -> Log {
|
||||
let stop_command = stop_container_command(container_name, signal, time);
|
||||
let command = format!("{stop_command} && docker container rm {container_name}");
|
||||
run_monitor_command("docker stop and remove", command).await
|
||||
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(
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
// #![allow(unused)]
|
||||
|
||||
use std::{fs::File, net::SocketAddr, sync::Arc};
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use ::helpers::get_socket_addr;
|
||||
use axum::Extension;
|
||||
use daemonize::Daemonize;
|
||||
use types::PeripheryConfig;
|
||||
|
||||
mod api;
|
||||
@@ -15,19 +14,7 @@ type PeripheryConfigExtension = Extension<Arc<PeripheryConfig>>;
|
||||
type HomeDirExtension = Extension<Arc<String>>;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let (args, port, config, home_dir) = config::load();
|
||||
|
||||
if args.daemon {
|
||||
let stdout = File::create(args.stdout.replace("~", &home_dir))
|
||||
.expect("failed to create stdout log file");
|
||||
let stderr = File::create(args.stderr.replace("~", &home_dir))
|
||||
.expect("failed to create stderr log file");
|
||||
let daemon = Daemonize::new().stdout(stdout).stderr(stderr);
|
||||
match daemon.start() {
|
||||
Ok(_) => println!("monitor periphery"),
|
||||
Err(e) => eprintln!("Error, {}", e),
|
||||
}
|
||||
}
|
||||
let (port, config, home_dir) = config::load();
|
||||
|
||||
run_periphery_server(port, config, home_dir)?;
|
||||
|
||||
|
||||
@@ -17,74 +17,81 @@ use tests::*;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let monitor = config::load().await;
|
||||
|
||||
println!("\nstarting tests\n");
|
||||
|
||||
let start_ts = unix_timestamp_ms();
|
||||
|
||||
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(),
|
||||
)
|
||||
let d = DeploymentBuilder::default()
|
||||
.name(String::from("test"))
|
||||
.server_id(String::from("any"))
|
||||
.build()?;
|
||||
|
||||
let build = monitor.create_full_build(&build).await?;
|
||||
println!("{d:#?}");
|
||||
|
||||
println!("{build:#?}");
|
||||
// let monitor = config::load().await;
|
||||
|
||||
let build_update = monitor.build(&build.id).await?;
|
||||
// println!("\nstarting tests\n");
|
||||
|
||||
println!("{build_update:#?}");
|
||||
// let start_ts = unix_timestamp_ms();
|
||||
|
||||
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 server = monitor
|
||||
// .list_servers(None)
|
||||
// .await?
|
||||
// .pop()
|
||||
// .ok_or(anyhow!("no servers"))?;
|
||||
|
||||
let deployment = monitor.create_full_deployment(&deployment).await?;
|
||||
// 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()?;
|
||||
|
||||
println!("{deployment:#?}");
|
||||
// let build = monitor.create_full_build(&build).await?;
|
||||
|
||||
let deploy_update = monitor.deploy_container(&deployment.id).await?;
|
||||
// println!("{build:#?}");
|
||||
|
||||
println!("{deploy_update:#?}");
|
||||
// let build_update = monitor.build(&build.id).await?;
|
||||
|
||||
let update = test_aws_build(&monitor).await?;
|
||||
// println!("{build_update:#?}");
|
||||
|
||||
let end_ts = unix_timestamp_ms();
|
||||
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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user