forked from github-starred/komodo
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
575aa62625 | ||
|
|
ac88a2c4ed | ||
|
|
f1dcb71a8a | ||
|
|
30d04bc201 | ||
|
|
33a00bb1a2 | ||
|
|
ccca44ea89 | ||
|
|
ae5f36fe51 | ||
|
|
69ce1e4f36 | ||
|
|
6e444b9032 | ||
|
|
73eff72da4 | ||
|
|
698e3c214b | ||
|
|
9da77667dc | ||
|
|
c30793fb8f | ||
|
|
84fdaab24d | ||
|
|
cbd67bb609 | ||
|
|
00f58e9008 | ||
|
|
7738fab351 | ||
|
|
06e8f6589b | ||
|
|
57d9287724 | ||
|
|
2cc65595ee | ||
|
|
3dd2b97873 | ||
|
|
3c805ebbf7 | ||
|
|
a854160018 | ||
|
|
a99d9e5969 | ||
|
|
813b6c1182 | ||
|
|
2958f9589b | ||
|
|
69b4e26176 | ||
|
|
78b00f139d | ||
|
|
dc1e8de851 | ||
|
|
3187b335a3 | ||
|
|
54b5a2b420 | ||
|
|
14c6bd00a8 | ||
|
|
e9c3646450 | ||
|
|
4f20257479 | ||
|
|
65749991de | ||
|
|
237a1d802d | ||
|
|
e4336f19f3 | ||
|
|
c895e5e67f | ||
|
|
4e4e210736 | ||
|
|
09dfc8faa3 |
622
Cargo.lock
generated
622
Cargo.lock
generated
@@ -19,6 +19,15 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@@ -75,6 +84,322 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "0.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3d1e2a1f1ab3ac6c4b884e37413eaa03eb9d901e4fc68ee8f5c1d49721680e"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-http",
|
||||
"aws-sdk-sso",
|
||||
"aws-sdk-sts",
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-client",
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-http-tower",
|
||||
"aws-smithy-json",
|
||||
"aws-smithy-types",
|
||||
"aws-types",
|
||||
"bytes",
|
||||
"hex",
|
||||
"http",
|
||||
"hyper",
|
||||
"ring",
|
||||
"time 0.3.17",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tracing",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-credential-types"
|
||||
version = "0.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0696a0523a39a19087747e4dafda0362dc867531e3d72a3f195564c84e5e08"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-types",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-endpoint"
|
||||
version = "0.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80a4f935ab6a1919fbfd6102a80c4fccd9ff5f47f94ba154074afe1051903261"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
"aws-types",
|
||||
"http",
|
||||
"regex",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-http"
|
||||
version = "0.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82976ca4e426ee9ca3ffcf919d9b2c8d14d0cd80d43cc02173737a8f07f28d4d"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
"aws-types",
|
||||
"bytes",
|
||||
"http",
|
||||
"http-body",
|
||||
"lazy_static",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ec2"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b40ee2d853d8300a49513778beb79b1574ff9e9c94b30b1531bc0171d730ad64"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
"aws-http",
|
||||
"aws-sig-auth",
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-client",
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-http-tower",
|
||||
"aws-smithy-json",
|
||||
"aws-smithy-query",
|
||||
"aws-smithy-types",
|
||||
"aws-smithy-xml",
|
||||
"aws-types",
|
||||
"bytes",
|
||||
"fastrand",
|
||||
"http",
|
||||
"regex",
|
||||
"tokio-stream",
|
||||
"tower",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca0119bacf0c42f587506769390983223ba834e605f049babe514b2bd646dbb2"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
"aws-http",
|
||||
"aws-sig-auth",
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-client",
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-http-tower",
|
||||
"aws-smithy-json",
|
||||
"aws-smithy-types",
|
||||
"aws-types",
|
||||
"bytes",
|
||||
"http",
|
||||
"regex",
|
||||
"tokio-stream",
|
||||
"tower",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "270b6a33969ebfcb193512fbd5e8ee5306888ad6c6d5d775cdbfb2d50d94de26"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-endpoint",
|
||||
"aws-http",
|
||||
"aws-sig-auth",
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-client",
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-http-tower",
|
||||
"aws-smithy-json",
|
||||
"aws-smithy-query",
|
||||
"aws-smithy-types",
|
||||
"aws-smithy-xml",
|
||||
"aws-types",
|
||||
"bytes",
|
||||
"http",
|
||||
"regex",
|
||||
"tower",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sig-auth"
|
||||
version = "0.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "660a02a98ab1af83bd8d714afbab2d502ba9b18c49e7e4cddd6bf8837ff778cb"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-sigv4",
|
||||
"aws-smithy-http",
|
||||
"aws-types",
|
||||
"http",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-sigv4"
|
||||
version = "0.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdaf11005b7444e6cd66f600d09861a3aeb6eb89a0f003c7c9820dbab2d15297"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"form_urlencoded",
|
||||
"hex",
|
||||
"hmac",
|
||||
"http",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"regex",
|
||||
"sha2",
|
||||
"time 0.3.17",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-async"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00e8615bf58d144dec3fcdb5110941b84e904c68054cb74ed240b9588fc337a5"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-client"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8c1df4c1d03e1ce299ae4e24c19d0f4cd8bebceac60828530e579977d70289a"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-http-tower",
|
||||
"aws-smithy-types",
|
||||
"bytes",
|
||||
"fastrand",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"lazy_static",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-http"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78abf16f8667b9176737cfffd1dd4ad07d350ef5dba01d01fdec5f31265f7134"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
"bytes",
|
||||
"bytes-utils",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-http-tower"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d517ac2476efc1820228c2fdfdcb17d3bea8695558bd67584a62a47c12b41918"
|
||||
dependencies = [
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
"bytes",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
"tower",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-json"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a23cc091168c5d969b150d7cc11a5a202bfa88dc36494e6659d2499b0cf227b"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-query"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92a1218021362bc1faa56648397b8cc4ac7631a3944e087d314d0187ef88d782"
|
||||
dependencies = [
|
||||
"aws-smithy-types",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-types"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee8d2056dc5f10094d5e753ac5c649e8996869f0649b641e470950151596db73"
|
||||
dependencies = [
|
||||
"base64-simd",
|
||||
"itoa",
|
||||
"num-integer",
|
||||
"ryu",
|
||||
"time 0.3.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-xml"
|
||||
version = "0.54.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a3029cbb4a49656456b3f2b34daee7f68dd93c61cc5d03fa90788cb1d25d5b4"
|
||||
dependencies = [
|
||||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-types"
|
||||
version = "0.54.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8f15b34253b68cde08e39b0627cc6101bcca64351229484b4743392c035d057"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-client",
|
||||
"aws-smithy-http",
|
||||
"aws-smithy-types",
|
||||
"http",
|
||||
"rustc_version 0.4.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.6.4"
|
||||
@@ -130,9 +455,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a320103719de37b7b4da4c8eb629d4573f6bcfd3dfe80d3208806895ccf81d"
|
||||
checksum = "51227033e4d3acad15c879092ac8a228532707b5db5ff2628f638334f63e1b7a"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"bytes",
|
||||
@@ -181,14 +506,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.13.0"
|
||||
name = "base64-simd"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e7c93a3fb23b2fdde989b2c9ec4dd153063ec81f408507f84c090cd91c6641"
|
||||
checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"simd-abstraction",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9df288bec72232f78c1ec5fe4e8f1d108aa0265476e93097593c803c8c02062a"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"blowfish",
|
||||
"getrandom",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -299,10 +634,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
name = "bytes-utils"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
@@ -398,7 +743,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -414,8 +759,8 @@ dependencies = [
|
||||
"hex",
|
||||
"hmac",
|
||||
"jwt",
|
||||
"monitor_helpers 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.1.16",
|
||||
"mungos",
|
||||
"periphery_client",
|
||||
"serde",
|
||||
@@ -520,9 +865,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e"
|
||||
checksum = "322296e2f2e5af4270b54df9e85a02ff037e271af20ba3e7fe1575515dc840b8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
@@ -532,9 +877,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200"
|
||||
checksum = "017a1385b05d631e7875b1f151c9f012d37b53491e2a87f65bff5c262b2111d8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
@@ -547,15 +892,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea"
|
||||
checksum = "c26bbb078acf09bc1ecda02d4223f03bdd28bd4874edcb0379138efc499ce971"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.87"
|
||||
version = "1.0.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e"
|
||||
checksum = "357f40d1f06a24b60ae1fe122542c1fb05d28d32acb2aed064e84bc2ad1e252e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -650,10 +995,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "db_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.16",
|
||||
"mungos",
|
||||
]
|
||||
|
||||
@@ -740,9 +1085,9 @@ checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
@@ -846,9 +1191,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -861,9 +1206,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -871,15 +1216,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@@ -888,15 +1233,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -905,21 +1250,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@@ -1103,6 +1448,21 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
"log",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
@@ -1453,7 +1813,7 @@ dependencies = [
|
||||
"rand",
|
||||
"rustc_version_runtime",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"rustls-pemfile 0.3.0",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_with",
|
||||
@@ -1478,7 +1838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_cli"
|
||||
version = "0.1.18"
|
||||
version = "0.1.22"
|
||||
dependencies = [
|
||||
"async_timing_util",
|
||||
"clap",
|
||||
@@ -1494,11 +1854,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1510,47 +1871,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_helpers"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
"aws-config",
|
||||
"aws-sdk-ec2",
|
||||
"axum",
|
||||
"bollard",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_helpers"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63b1ff7bac3697e1fc06044b669b029964b11182483ce3066e3996c5c0f66b57"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
"axum",
|
||||
"bollard",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.16",
|
||||
"periphery_client",
|
||||
"rand",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_periphery"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -1561,8 +1905,8 @@ dependencies = [
|
||||
"dotenv",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_helpers 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.1.16",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1576,7 +1920,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1593,9 +1937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31a6a8d9bf88d5f6f0ede8d35efa7b7018ebf4b62cf7b9d052c18eed185ca805"
|
||||
checksum = "e4d1444216d1585f466d0591cedbdf84d1c810a4f45586362108219e7c943267"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1814,6 +2158,12 @@ version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "outref"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
@@ -1854,12 +2204,11 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
"monitor_helpers 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.16",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2020,6 +2369,23 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
@@ -2106,7 +2472,16 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver 1.0.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2115,8 +2490,8 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d31b7153270ebf48bf91c65ae5b0c00e749c4cfad505f66530ac74950249582f"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
"semver",
|
||||
"rustc_version 0.2.3",
|
||||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2145,6 +2520,18 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 1.0.2",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "0.3.0"
|
||||
@@ -2154,6 +2541,15 @@ dependencies = [
|
||||
"base64 0.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.11"
|
||||
@@ -2199,9 +2595,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.8.1"
|
||||
version = "2.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721"
|
||||
checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@@ -2229,6 +2625,12 @@ dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
@@ -2370,6 +2772,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-abstraction"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987"
|
||||
dependencies = [
|
||||
"outref",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
@@ -2614,9 +3025,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.24.2"
|
||||
version = "1.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@@ -2664,6 +3075,17 @@ dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.18.0"
|
||||
@@ -2694,9 +3116,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
|
||||
checksum = "2f560bc7fb3eb31f5eee1340c68a2160cad39605b7b9c9ec32045ddbdee13b85"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@@ -2706,18 +3128,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
|
||||
checksum = "886f31a9b85b6182cabd4d8b07df3b451afcc216563748201490940d2a28ed36"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.18.0"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581"
|
||||
checksum = "233d8716cdc5d20ec88a18a839edaf545edc71efa4a5ff700ef4a102c26cd8fa"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nom8",
|
||||
@@ -2793,9 +3215,21 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.30"
|
||||
@@ -3231,6 +3665,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xmlparser"
|
||||
version = "0.13.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.5.7"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_cli"
|
||||
version = "0.1.18"
|
||||
version = "0.1.22"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor cli | tools to setup monitor system"
|
||||
@@ -18,7 +18,7 @@ async_timing_util = "0.1.14"
|
||||
rand = "0.8"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
toml = "0.6"
|
||||
toml = "0.7"
|
||||
run_command = "0.0.5"
|
||||
colored = "2"
|
||||
strum = "0.24"
|
||||
|
||||
@@ -74,6 +74,7 @@ pub fn gen_core_config(sub_matches: &ArgMatches) {
|
||||
local_auth: true,
|
||||
github_oauth: Default::default(),
|
||||
google_oauth: Default::default(),
|
||||
aws: Default::default(),
|
||||
mongo: MongoConfig {
|
||||
uri: mongo_uri,
|
||||
db_name: mongo_db_name,
|
||||
@@ -81,6 +82,7 @@ pub fn gen_core_config(sub_matches: &ArgMatches) {
|
||||
},
|
||||
jwt_secret: generate_secret(40),
|
||||
github_webhook_secret: generate_secret(30),
|
||||
passkey: generate_secret(30),
|
||||
};
|
||||
|
||||
write_to_toml(&path, &config);
|
||||
@@ -176,7 +178,7 @@ pub fn start_mongo(sub_matches: &ArgMatches) {
|
||||
}
|
||||
}
|
||||
|
||||
let command = format!("docker stop {name} && docker container rm {name} && docker run -d --name {name} -p {port}:27017 --network {network} -v {mount}:/data/db{env} --restart {restart} mongo --quiet");
|
||||
let command = format!("docker stop {name} && docker container rm {name} && docker run -d --name {name} -p {port}:27017 --network {network} -v {mount}:/data/db{env} --restart {restart} --log-opt max-size=15m --log-opt max-file=3 mongo --quiet");
|
||||
|
||||
let output = run_command_pipe_to_terminal(&command);
|
||||
|
||||
@@ -318,9 +320,10 @@ pub fn gen_periphery_config(sub_matches: &ArgMatches) {
|
||||
|
||||
let config = PeripheryConfig {
|
||||
port,
|
||||
repo_dir,
|
||||
stats_polling_rate,
|
||||
allowed_ips,
|
||||
repo_dir,
|
||||
passkeys: vec![],
|
||||
secrets: Default::default(),
|
||||
github_accounts: Default::default(),
|
||||
docker_accounts: Default::default(),
|
||||
@@ -588,6 +591,7 @@ Description=agent to connect with monitor core
|
||||
|
||||
[Service]
|
||||
ExecStart={home}/.monitor/bin/periphery --config-path {config_path} --home-dir {home}
|
||||
Restart=on-failure
|
||||
TimeoutStartSec=0
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -21,7 +21,6 @@ pub struct CoreConfig {
|
||||
#[serde(default)]
|
||||
pub keep_stats_for_days: u64, // 0 means never prune
|
||||
|
||||
// jwt config
|
||||
pub jwt_secret: String,
|
||||
#[serde(default = "default_jwt_valid_for")]
|
||||
pub jwt_valid_for: Timelength,
|
||||
@@ -32,20 +31,25 @@ pub struct CoreConfig {
|
||||
// used to verify validity from github webhooks
|
||||
pub github_webhook_secret: String,
|
||||
|
||||
// sent in auth header with req to periphery
|
||||
pub passkey: String,
|
||||
|
||||
// integration with slack app
|
||||
pub slack_url: Option<String>,
|
||||
|
||||
// enable login with local auth
|
||||
pub local_auth: bool,
|
||||
|
||||
// github integration
|
||||
pub mongo: MongoConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub github_oauth: OauthCredentials,
|
||||
|
||||
// google integration
|
||||
#[serde(default)]
|
||||
pub google_oauth: OauthCredentials,
|
||||
|
||||
// mongo config
|
||||
pub mongo: MongoConfig,
|
||||
#[serde(default)]
|
||||
pub aws: AwsBuilderConfig,
|
||||
}
|
||||
|
||||
fn default_core_port() -> u16 {
|
||||
@@ -83,6 +87,37 @@ fn default_core_mongo_db_name() -> String {
|
||||
"monitor".to_string()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AwsBuilderConfig {
|
||||
pub access_key_id: String,
|
||||
pub secret_access_key: String,
|
||||
pub default_ami_id: String,
|
||||
pub default_subnet_id: String,
|
||||
pub default_key_pair_name: String,
|
||||
#[serde(default = "default_aws_region")]
|
||||
pub default_region: String,
|
||||
#[serde(default = "default_volume_gb")]
|
||||
pub default_volume_gb: i32,
|
||||
#[serde(default = "default_instance_type")]
|
||||
pub default_instance_type: String,
|
||||
#[serde(default)]
|
||||
pub default_security_group_ids: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub default_assign_public_ip: bool,
|
||||
}
|
||||
|
||||
fn default_aws_region() -> String {
|
||||
String::from("us-east-1")
|
||||
}
|
||||
|
||||
fn default_volume_gb() -> i32 {
|
||||
8
|
||||
}
|
||||
|
||||
fn default_instance_type() -> String {
|
||||
String::from("m5.2xlarge")
|
||||
}
|
||||
|
||||
pub type GithubUsername = String;
|
||||
pub type GithubToken = String;
|
||||
pub type GithubAccounts = HashMap<GithubUsername, GithubToken>;
|
||||
@@ -104,6 +139,8 @@ pub struct PeripheryConfig {
|
||||
#[serde(default)]
|
||||
pub allowed_ips: Vec<IpAddr>,
|
||||
#[serde(default)]
|
||||
pub passkeys: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub secrets: SecretsMap,
|
||||
#[serde(default)]
|
||||
pub github_accounts: GithubAccounts,
|
||||
|
||||
@@ -22,17 +22,34 @@ slack_url = "your_slack_app_webhook_url"
|
||||
# token that has to be given to github during webhook config as the Secret
|
||||
github_webhook_secret = "your_random_webhook_secret"
|
||||
|
||||
# token used to authenticate core requests to periphery
|
||||
passkey = "your_random_passkey"
|
||||
|
||||
# can be 30-sec, 1-min, 2-min, 5-min
|
||||
monitoring_interval = "1-min"
|
||||
|
||||
# allow or deny user login with username / password
|
||||
local_auth = true
|
||||
|
||||
[aws]
|
||||
access_key_id = "your_aws_key_id"
|
||||
secret_access_key = "your_aws_secret_key"
|
||||
default_region = "us-east-1"
|
||||
default_ami_id = "your_periphery_ami"
|
||||
default_key_pair_name = "your_default_key_pair_name"
|
||||
default_instance_type = "m5.2xlarge"
|
||||
default_volume_gb = 8
|
||||
default_subnet_id = "your_default_subnet_id"
|
||||
default_security_group_ids = ["sg_id_1", "sg_id_2"]
|
||||
default_assign_public_ip = false
|
||||
|
||||
[github_oauth]
|
||||
enabled = true
|
||||
id = "your_github_client_id"
|
||||
secret = "your_github_client_secret"
|
||||
|
||||
[google_oauth]
|
||||
enabled = true
|
||||
id = "your_google_client_id"
|
||||
secret = "your_google_client_secret"
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
port = 9001 # optional. 9001 is default
|
||||
port = 8000 # optional. 8000 is default
|
||||
repo_dir = "/repos" # optional. /repos is default. no reason to change if running the docker container, just mount your desired repo dir to /repos in the container
|
||||
stats_polling_rate = "5-sec" # optional. 5-sec is default. can use 1-sec, 5-sec, 10-sec, 30-sec, 1-min. controls granularity of system stats recorded
|
||||
allowed_ips = ["127.0.0.1"] # optional. default is empty, which will not block any request by ip.
|
||||
passkeys = ["abcdefghijk"] # optional. default is empty, which will not require any passkey to be passed by core.
|
||||
|
||||
[secrets] # optional. can inject these values into your deployments configuration.
|
||||
secret_variable = "secret_value"
|
||||
@@ -12,4 +13,4 @@ github_username2 = "github_token2"
|
||||
|
||||
[docker_accounts] # optional
|
||||
docker_username1 = "docker_token1"
|
||||
docker_username2 = "docker_token2"
|
||||
docker_username2 = "docker_token2"
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
[package]
|
||||
name = "core"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
||||
# types = { package = "monitor_types", path = "../lib/types" }
|
||||
helpers = { package = "monitor_helpers", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
||||
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.24", features = ["full"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio-util = "0.7"
|
||||
axum = { version = "0.6", features = ["ws", "json"] }
|
||||
axum-extra = { version = "0.4", features = ["spa"] }
|
||||
axum-extra = { version = "0.5", features = ["spa"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
tower-http = { version = "0.3", features = ["cors"] }
|
||||
slack = { package = "slack_client_rs", version = "0.0.8" }
|
||||
@@ -28,7 +26,7 @@ serde_json = "1.0"
|
||||
dotenv = "0.15"
|
||||
envy = "0.4"
|
||||
anyhow = "1.0"
|
||||
bcrypt = "0.13"
|
||||
bcrypt = "0.14"
|
||||
jwt = "0.16"
|
||||
hmac = "0.12"
|
||||
sha2 = "0.10"
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use helpers::{all_logs_success, to_monitor_name};
|
||||
use helpers::{
|
||||
all_logs_success,
|
||||
aws::{self, create_ec2_client, create_instance_with_ami, terminate_ec2_instance, Ec2Instance},
|
||||
to_monitor_name,
|
||||
};
|
||||
use mungos::{doc, to_bson};
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
Build, Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget,
|
||||
Build, Log, Operation, PermissionLevel, Update, UpdateStatus, UpdateTarget, Version,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
auth::RequestUser,
|
||||
helpers::{any_option_diff_is_some, option_diff_is_some},
|
||||
state::State,
|
||||
};
|
||||
use crate::{auth::RequestUser, state::State};
|
||||
|
||||
const BUILDER_POLL_RATE_SECS: u64 = 2;
|
||||
const BUILDER_POLL_MAX_TRIES: usize = 30;
|
||||
|
||||
impl State {
|
||||
pub async fn get_build_check_permissions(
|
||||
@@ -39,18 +44,13 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_build(
|
||||
&self,
|
||||
name: &str,
|
||||
server_id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Build> {
|
||||
self.get_server_check_permissions(&server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
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"));
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let build = Build {
|
||||
name: to_monitor_name(name),
|
||||
server_id,
|
||||
permissions: [(user.id.clone(), PermissionLevel::Update)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
@@ -84,10 +84,7 @@ impl State {
|
||||
mut build: Build,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Build> {
|
||||
build.id = self
|
||||
.create_build(&build.name, build.server_id.clone(), user)
|
||||
.await?
|
||||
.id;
|
||||
build.id = self.create_build(&build.name, user).await?.id;
|
||||
let build = self.update_build(build, user).await?;
|
||||
Ok(build)
|
||||
}
|
||||
@@ -96,14 +93,13 @@ impl State {
|
||||
&self,
|
||||
target_id: &str,
|
||||
new_name: String,
|
||||
new_server_id: String,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Build> {
|
||||
let mut build = self
|
||||
.get_build_check_permissions(target_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
build.name = new_name;
|
||||
build.server_id = new_server_id;
|
||||
build.version = Version::default();
|
||||
let build = self.create_full_build(build, user).await?;
|
||||
Ok(build)
|
||||
}
|
||||
@@ -116,12 +112,6 @@ impl State {
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = self.db.get_server(&build.server_id).await?;
|
||||
let delete_repo_log = self
|
||||
.periphery
|
||||
.delete_repo(&server, &build.name)
|
||||
.await
|
||||
.context("failed at deleting repo")?;
|
||||
self.db.builds.delete_one(build_id).await?;
|
||||
let update = Update {
|
||||
target: UpdateTarget::Build(build_id.to_string()),
|
||||
@@ -129,13 +119,10 @@ impl State {
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
logs: vec![
|
||||
delete_repo_log,
|
||||
Log::simple(
|
||||
"delete build",
|
||||
format!("deleted build {} on server {}", build.name, server.name),
|
||||
),
|
||||
],
|
||||
logs: vec![Log::simple(
|
||||
"delete build",
|
||||
format!("deleted build {}", build.name),
|
||||
)],
|
||||
success: true,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -179,7 +166,6 @@ impl State {
|
||||
// none of these should be changed through this method
|
||||
new_build.name = current_build.name.clone();
|
||||
new_build.permissions = current_build.permissions.clone();
|
||||
new_build.server_id = current_build.server_id.clone();
|
||||
new_build.last_built_at = String::new();
|
||||
new_build.created_at = current_build.created_at.clone();
|
||||
new_build.updated_at = start_ts.clone();
|
||||
@@ -192,41 +178,42 @@ impl State {
|
||||
|
||||
let diff = current_build.diff(&new_build);
|
||||
|
||||
let mut update = Update {
|
||||
let update = Update {
|
||||
operation: Operation::UpdateBuild,
|
||||
target: UpdateTarget::Build(new_build.id.clone()),
|
||||
start_ts,
|
||||
status: UpdateStatus::InProgress,
|
||||
status: UpdateStatus::Complete,
|
||||
logs: vec![Log::simple(
|
||||
"build update",
|
||||
serde_json::to_string_pretty(&diff).unwrap(),
|
||||
)],
|
||||
operator: user.id.clone(),
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
success: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
// update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
if any_option_diff_is_some(&[&diff.repo, &diff.branch, &diff.github_account])
|
||||
|| option_diff_is_some(&diff.on_clone)
|
||||
{
|
||||
let server = self.db.get_server(¤t_build.server_id).await?;
|
||||
match self.periphery.clone_repo(&server, &new_build).await {
|
||||
Ok(clone_logs) => {
|
||||
update.logs.extend(clone_logs);
|
||||
}
|
||||
Err(e) => update
|
||||
.logs
|
||||
.push(Log::error("cloning repo", format!("{e:#?}"))),
|
||||
}
|
||||
}
|
||||
// if any_option_diff_is_some(&[&diff.repo, &diff.branch, &diff.github_account])
|
||||
// || option_diff_is_some(&diff.on_clone)
|
||||
// {
|
||||
// let server = self.db.get_server(¤t_build.server_id).await?;
|
||||
// match self.periphery.clone_repo(&server, &new_build).await {
|
||||
// Ok(clone_logs) => {
|
||||
// update.logs.extend(clone_logs);
|
||||
// }
|
||||
// Err(e) => update
|
||||
// .logs
|
||||
// .push(Log::error("cloning repo", format!("{e:#?}"))),
|
||||
// }
|
||||
// }
|
||||
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.success = all_logs_success(&update.logs);
|
||||
update.status = UpdateStatus::Complete;
|
||||
// update.end_ts = Some(monitor_timestamp());
|
||||
// update.success = all_logs_success(&update.logs);
|
||||
// update.status = UpdateStatus::Complete;
|
||||
|
||||
self.update_update(update).await?;
|
||||
self.add_update(update).await?;
|
||||
|
||||
Ok(new_build)
|
||||
}
|
||||
@@ -253,10 +240,7 @@ impl State {
|
||||
let mut build = self
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let server = self.db.get_server(&build.server_id).await?;
|
||||
|
||||
build.version.increment();
|
||||
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Build(build_id.to_string()),
|
||||
operation: Operation::BuildBuild,
|
||||
@@ -267,12 +251,95 @@ impl State {
|
||||
version: build.version.clone().into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let (server, aws_client) = if let Some(server_id) = &build.server_id {
|
||||
let server = self.db.get_server(server_id).await;
|
||||
if let Err(e) = server {
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.success = false;
|
||||
update
|
||||
.logs
|
||||
.push(Log::error("get build server", format!("{e:#?}")));
|
||||
self.update_update(update.clone()).await?;
|
||||
return Err(e);
|
||||
}
|
||||
let server = Ec2Instance {
|
||||
instance_id: String::new(),
|
||||
server: server.unwrap(),
|
||||
};
|
||||
(server, None)
|
||||
} else if build.aws_config.is_some() {
|
||||
let start_ts = monitor_timestamp();
|
||||
let res = self.create_ec2_instance_for_build(&build).await;
|
||||
if let Err(e) = res {
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.success = false;
|
||||
update.logs.push(Log {
|
||||
stage: "start build server".to_string(),
|
||||
stderr: format!("{e:#?}"),
|
||||
success: false,
|
||||
start_ts,
|
||||
end_ts: monitor_timestamp(),
|
||||
..Default::default()
|
||||
});
|
||||
self.update_update(update).await?;
|
||||
return Err(e);
|
||||
}
|
||||
let (server, aws_client, log) = res.unwrap();
|
||||
update.logs.push(log);
|
||||
self.update_update(update.clone()).await?;
|
||||
(server, aws_client)
|
||||
} else {
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.success = false;
|
||||
update.logs.push(Log::error(
|
||||
"start build",
|
||||
"build has neither server_id nor aws_config attached".to_string(),
|
||||
));
|
||||
self.update_update(update).await?;
|
||||
return Err(anyhow!(
|
||||
"build has neither server_id or aws_config attached"
|
||||
));
|
||||
};
|
||||
|
||||
let clone_success = match self.periphery.clone_repo(&server.server, &build).await {
|
||||
Ok(clone_logs) => {
|
||||
update.logs.extend(clone_logs);
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
update
|
||||
.logs
|
||||
.push(Log::error("clone repo", format!("{e:#?}")));
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !clone_success {
|
||||
let _ = self
|
||||
.periphery
|
||||
.delete_repo(&server.server, &build.name)
|
||||
.await;
|
||||
if let Some(aws_client) = aws_client {
|
||||
self.terminate_ec2_instance(aws_client, &server, &mut update)
|
||||
.await;
|
||||
}
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.success = false;
|
||||
self.update_update(update.clone()).await?;
|
||||
return Ok(update);
|
||||
}
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
let build_logs = match self
|
||||
.periphery
|
||||
.build(&server, &build)
|
||||
.build(&server.server, &build)
|
||||
.await
|
||||
.context("failed at call to periphery to build")
|
||||
{
|
||||
@@ -282,9 +349,9 @@ impl State {
|
||||
|
||||
match build_logs {
|
||||
Some(logs) => {
|
||||
let success = all_logs_success(&logs);
|
||||
update.logs.extend(logs);
|
||||
update.success = all_logs_success(&update.logs);
|
||||
if update.success {
|
||||
if success {
|
||||
let _ = self
|
||||
.db
|
||||
.builds
|
||||
@@ -305,68 +372,18 @@ impl State {
|
||||
.push(Log::error("build", "builder busy".to_string()));
|
||||
}
|
||||
}
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
let _ = self
|
||||
.periphery
|
||||
.delete_repo(&server.server, &build.name)
|
||||
.await;
|
||||
|
||||
pub async fn reclone_build(
|
||||
&self,
|
||||
build_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.build_busy(build_id).await {
|
||||
return Err(anyhow!("build busy"));
|
||||
if let Some(aws_client) = aws_client {
|
||||
self.terminate_ec2_instance(aws_client, &server, &mut update)
|
||||
.await;
|
||||
}
|
||||
{
|
||||
let mut lock = self.build_action_states.lock().await;
|
||||
let entry = lock.entry(build_id.to_string()).or_default();
|
||||
entry.recloning = true;
|
||||
}
|
||||
let res = self.reclone_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.recloning = false;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
async fn reclone_build_inner(
|
||||
&self,
|
||||
build_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
let build = self
|
||||
.get_build_check_permissions(build_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let server = self.db.get_server(&build.server_id).await?;
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Build(build_id.to_string()),
|
||||
operation: Operation::RecloneBuild,
|
||||
start_ts: monitor_timestamp(),
|
||||
status: UpdateStatus::InProgress,
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
..Default::default()
|
||||
};
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
update.success = match self.periphery.clone_repo(&server, &build).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());
|
||||
|
||||
@@ -374,4 +391,170 @@ impl State {
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
async fn create_ec2_instance_for_build(
|
||||
&self,
|
||||
build: &Build,
|
||||
) -> anyhow::Result<(Ec2Instance, Option<aws::Client>, Log)> {
|
||||
if build.aws_config.is_none() {
|
||||
return Err(anyhow!("build has no aws_config attached"));
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let aws_config = build.aws_config.as_ref().unwrap();
|
||||
let region = aws_config
|
||||
.region
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_region)
|
||||
.to_string();
|
||||
let aws_client = create_ec2_client(
|
||||
region,
|
||||
&self.config.aws.access_key_id,
|
||||
self.config.aws.secret_access_key.clone(),
|
||||
)
|
||||
.await;
|
||||
let ami_id = aws_config
|
||||
.ami_id
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_ami_id);
|
||||
let instance_type = aws_config
|
||||
.instance_type
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_instance_type);
|
||||
let subnet_id = aws_config
|
||||
.subnet_id
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_subnet_id);
|
||||
let security_group_ids = aws_config
|
||||
.security_group_ids
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_security_group_ids)
|
||||
.to_owned();
|
||||
let readable_sec_group_ids = security_group_ids.join(", ");
|
||||
let volume_size_gb = *aws_config
|
||||
.volume_gb
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_volume_gb);
|
||||
let key_pair_name = aws_config
|
||||
.key_pair_name
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_key_pair_name);
|
||||
let assign_public_ip = *aws_config
|
||||
.assign_public_ip
|
||||
.as_ref()
|
||||
.unwrap_or(&self.config.aws.default_assign_public_ip);
|
||||
let instance = create_instance_with_ami(
|
||||
&aws_client,
|
||||
&format!("BUILDER-{}-v{}", build.name, build.version.to_string()),
|
||||
ami_id,
|
||||
instance_type,
|
||||
subnet_id,
|
||||
security_group_ids,
|
||||
volume_size_gb,
|
||||
key_pair_name,
|
||||
assign_public_ip,
|
||||
)
|
||||
.await?;
|
||||
let mut res = Ok(String::new());
|
||||
for _ in 0..BUILDER_POLL_MAX_TRIES {
|
||||
let status = self.periphery.health_check(&instance.server).await;
|
||||
if let Ok(_) = status {
|
||||
let instance_id = &instance.instance_id;
|
||||
let log = Log {
|
||||
stage: "start build instance".to_string(),
|
||||
success: true,
|
||||
stdout: format!("instance id: {instance_id}\nami id: {ami_id}\ninstance type: {instance_type}\nvolume size: {volume_size_gb} GB\nsubnet id: {subnet_id}\nsecurity groups: {readable_sec_group_ids}"),
|
||||
start_ts,
|
||||
end_ts: monitor_timestamp(),
|
||||
..Default::default()
|
||||
};
|
||||
return Ok((instance, Some(aws_client), log));
|
||||
}
|
||||
res = status;
|
||||
tokio::time::sleep(Duration::from_secs(BUILDER_POLL_RATE_SECS)).await;
|
||||
}
|
||||
let _ = terminate_ec2_instance(&aws_client, &instance.instance_id).await;
|
||||
Err(anyhow!("unable to reach periphery agent on build server\n{res:#?}"))
|
||||
}
|
||||
|
||||
async fn terminate_ec2_instance(
|
||||
&self,
|
||||
aws_client: aws::Client,
|
||||
server: &Ec2Instance,
|
||||
update: &mut Update,
|
||||
) {
|
||||
let res = terminate_ec2_instance(&aws_client, &server.instance_id).await;
|
||||
if let Err(e) = res {
|
||||
update
|
||||
.logs
|
||||
.push(Log::error("terminate instance", format!("{e:#?}")))
|
||||
} else {
|
||||
update.logs.push(Log::simple(
|
||||
"terminate instance",
|
||||
format!("terminate instance id {}", server.instance_id),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// pub async fn reclone_build(
|
||||
// &self,
|
||||
// build_id: &str,
|
||||
// user: &RequestUser,
|
||||
// ) -> anyhow::Result<Update> {
|
||||
// if self.build_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.recloning = true;
|
||||
// }
|
||||
// let res = self.reclone_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.recloning = false;
|
||||
// }
|
||||
// res
|
||||
// }
|
||||
|
||||
// async fn reclone_build_inner(
|
||||
// &self,
|
||||
// build_id: &str,
|
||||
// user: &RequestUser,
|
||||
// ) -> anyhow::Result<Update> {
|
||||
// let build = self
|
||||
// .get_build_check_permissions(build_id, user, PermissionLevel::Update)
|
||||
// .await?;
|
||||
// let server = self.db.get_server(&build.server_id).await?;
|
||||
// let mut update = Update {
|
||||
// target: UpdateTarget::Build(build_id.to_string()),
|
||||
// operation: Operation::RecloneBuild,
|
||||
// start_ts: monitor_timestamp(),
|
||||
// status: UpdateStatus::InProgress,
|
||||
// operator: user.id.clone(),
|
||||
// success: true,
|
||||
// ..Default::default()
|
||||
// };
|
||||
// update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
// update.success = match self.periphery.clone_repo(&server, &build).await {
|
||||
// Ok(clone_logs) => {
|
||||
// update.logs.extend(clone_logs);
|
||||
// true
|
||||
// }
|
||||
// Err(e) => {
|
||||
// update
|
||||
// .logs
|
||||
// .push(Log::error("clone repo", format!("{e:#?}")));
|
||||
// false
|
||||
// }
|
||||
// };
|
||||
|
||||
// update.status = UpdateStatus::Complete;
|
||||
// update.end_ts = Some(monitor_timestamp());
|
||||
|
||||
// self.update_update(update.clone()).await?;
|
||||
|
||||
// Ok(update)
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -119,10 +119,14 @@ impl State {
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = self.db.get_server(&deployment.server_id).await?;
|
||||
let log = self
|
||||
let log = match self
|
||||
.periphery
|
||||
.container_remove(&server, &deployment.name)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("destroy container", format!("{e:#?}")),
|
||||
};
|
||||
self.db
|
||||
.deployments
|
||||
.delete_one(deployment_id)
|
||||
|
||||
@@ -125,7 +125,7 @@ impl State {
|
||||
} in &new_procedure.stages
|
||||
{
|
||||
match operation {
|
||||
BuildBuild | RecloneBuild => {
|
||||
BuildBuild => {
|
||||
self.get_build_check_permissions(&target_id, user, PermissionLevel::Execute)
|
||||
.await?;
|
||||
}
|
||||
@@ -253,13 +253,6 @@ impl State {
|
||||
.context(format!("failed at build (id: {target_id})"))?;
|
||||
updates.push(update);
|
||||
}
|
||||
RecloneBuild => {
|
||||
let update = self
|
||||
.reclone_build(&target_id, user)
|
||||
.await
|
||||
.context(format!("failed at reclone build (id: {target_id})"))?;
|
||||
updates.push(update);
|
||||
}
|
||||
// server
|
||||
PruneImagesServer => {
|
||||
let update = self.prune_images(&target_id, user).await.context(format!(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use diff::Diff;
|
||||
use futures_util::future::join_all;
|
||||
use helpers::to_monitor_name;
|
||||
use mungos::doc;
|
||||
use types::{
|
||||
monitor_timestamp,
|
||||
traits::{Busy, Permissioned},
|
||||
@@ -102,21 +104,59 @@ impl State {
|
||||
.get_server_check_permissions(server_id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
self.db.servers.delete_one(&server_id).await?;
|
||||
let update = Update {
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::Server(server_id.to_string()),
|
||||
operation: Operation::DeleteServer,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
logs: vec![Log::simple(
|
||||
"delete server",
|
||||
format!("deleted server {}", server.name),
|
||||
)],
|
||||
success: true,
|
||||
status: UpdateStatus::InProgress,
|
||||
..Default::default()
|
||||
};
|
||||
self.add_update(update).await?;
|
||||
update.id = self.add_update(update.clone()).await?;
|
||||
|
||||
let res = {
|
||||
let delete_deployments = self
|
||||
.db
|
||||
.deployments
|
||||
.get_some(doc! { "server_id": server_id }, None)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|d| async move { self.delete_deployment(&d.id, user).await });
|
||||
let delete_builds = self
|
||||
.db
|
||||
.builds
|
||||
.get_some(doc! { "server_id": server_id }, None)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|d| async move { self.delete_deployment(&d.id, user).await });
|
||||
let update_groups = self
|
||||
.db
|
||||
.groups
|
||||
.update_many(doc! {}, doc! { "$pull": { "servers": server_id } });
|
||||
let (dep_res, build_res, group_res) = tokio::join!(
|
||||
join_all(delete_deployments),
|
||||
join_all(delete_builds),
|
||||
update_groups
|
||||
);
|
||||
dep_res.into_iter().collect::<anyhow::Result<Vec<_>>>()?;
|
||||
build_res.into_iter().collect::<anyhow::Result<Vec<_>>>()?;
|
||||
group_res?;
|
||||
self.db.servers.delete_one(&server_id).await?;
|
||||
anyhow::Ok(())
|
||||
};
|
||||
|
||||
let log = match res {
|
||||
Ok(_) => Log::simple("delete server", format!("deleted server {}", server.name)),
|
||||
Err(e) => Log::error("delete server", format!("failed to delete server\n{e:#?}")),
|
||||
};
|
||||
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.success = log.success;
|
||||
update.logs.push(log);
|
||||
|
||||
self.update_update(update).await?;
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,14 +31,12 @@ struct BuildId {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CreateBuildBody {
|
||||
name: String,
|
||||
server_id: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CopyBuildBody {
|
||||
name: String,
|
||||
server_id: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
@@ -88,7 +86,7 @@ pub fn router() -> Router {
|
||||
Extension(user): RequestUserExtension,
|
||||
Json(build): Json<CreateBuildBody>| async move {
|
||||
let build = state
|
||||
.create_build(&build.name, build.server_id, &user)
|
||||
.create_build(&build.name, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(build))
|
||||
@@ -121,7 +119,7 @@ pub fn router() -> Router {
|
||||
Json(build): Json<CopyBuildBody>| async move {
|
||||
let build = spawn_request_action(async move {
|
||||
state
|
||||
.copy_build(&id, build.name, build.server_id, &user)
|
||||
.copy_build(&id, build.name, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
@@ -181,23 +179,6 @@ pub fn router() -> Router {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/reclone",
|
||||
post(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Path(build_id): Path<BuildId>| async move {
|
||||
let update = spawn_request_action(async move {
|
||||
state
|
||||
.reclone_build(&build_id.id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)
|
||||
})
|
||||
.await??;
|
||||
response!(Json(update))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/action_state",
|
||||
get(
|
||||
|
||||
@@ -74,6 +74,7 @@ impl State {
|
||||
id: String::from(GITHUB_WEBHOOK_USER_ID),
|
||||
is_admin: true,
|
||||
create_server_permissions: false,
|
||||
create_build_permissions: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
@@ -103,6 +104,7 @@ impl State {
|
||||
id: String::from(GITHUB_WEBHOOK_USER_ID),
|
||||
is_admin: true,
|
||||
create_server_permissions: false,
|
||||
create_build_permissions: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
@@ -127,6 +129,7 @@ impl State {
|
||||
id: String::from(GITHUB_WEBHOOK_USER_ID),
|
||||
is_admin: true,
|
||||
create_server_permissions: false,
|
||||
create_build_permissions: false,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -24,6 +24,7 @@ pub struct RequestUser {
|
||||
pub id: String,
|
||||
pub is_admin: bool,
|
||||
pub create_server_permissions: bool,
|
||||
pub create_build_permissions: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -109,6 +110,7 @@ impl JwtClient {
|
||||
id: claims.id,
|
||||
is_admin: user.admin,
|
||||
create_server_permissions: user.create_server_permissions,
|
||||
create_build_permissions: user.create_build_permissions,
|
||||
};
|
||||
Ok(user)
|
||||
} else {
|
||||
|
||||
@@ -32,9 +32,9 @@ impl State {
|
||||
let state = State {
|
||||
db: DbClient::new(config.mongo.clone()).await,
|
||||
slack: config.slack_url.clone().map(|url| slack::Client::new(&url)),
|
||||
periphery: PeripheryClient::new(config.passkey.clone()),
|
||||
config,
|
||||
update: UpdateWsChannel::new(),
|
||||
periphery: PeripheryClient::default(),
|
||||
build_action_states: Default::default(),
|
||||
deployment_action_states: Default::default(),
|
||||
server_action_states: Default::default(),
|
||||
|
||||
28
docs/builds.md
Normal file
28
docs/builds.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# building images
|
||||
|
||||
Monitor builds docker images by cloning the source repository from Github and running ```docker build``` on the configured Dockerfile, which should be present in the source repository.
|
||||
|
||||
## repo configuration
|
||||
Setting related to the github repo are under the *repo* tab on respective build's page.
|
||||
|
||||
To specify the github repo to build, just give it the name of the repo and the branch under *github config*. The name is given like ```mbecker20/monitor```, it includes the username / organization that owns the repo.
|
||||
|
||||
Many repos are private, in this case a Github access token is required in the periphery.config.toml of the building server. these are specified in the config like ```username = "access_token"```. An account which has access to the repo and is available on the periphery server can be selected to use via the *github account* dropdown menu.
|
||||
|
||||
Sometimes a command needs to be run when the repo is cloned, you can configure this in the *on clone* section.
|
||||
|
||||
There are two fields to pass for *on clone*. the first is *path*, which changes to working directory. To run the command in the root of the repo, just pass ".". The second field is *command*, this is the shell command to be executed after the repo is cloned.
|
||||
|
||||
For example, say your repo had a folder in it called "scripts" with a shell script "on-clone.sh". You would give *path* as "scripts" and command as "sh on-clone.sh". Or you could make *path* just "." and then command would be "sh scripts/on-clone.sh". Either way works fine.
|
||||
|
||||
## build configuration
|
||||
|
||||
|
||||
## versioning
|
||||
|
||||
Monitor uses a major.minor.patch versioning scheme. Every build will auto increment the patch number, and push the image to docker hub with the version tag as well as the "latest" tag.
|
||||
|
||||
|
||||
[next: deploying](https://github.com/mbecker20/monitor/blob/main/docs/deployments.md)
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
3
docs/deployments.md
Normal file
3
docs/deployments.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## deploying applications
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
46
docs/introduction.md
Normal file
46
docs/introduction.md
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
# introduction
|
||||
|
||||
If you have many servers running many applications, it can be a challenge to keep things organized and easily accessible. Without structure, things can become messy quickly, which means operational issues are more likely to arise and they can take longer to resolve. Ultimately these issues hinder productivity and waste valuable time. Monitor is a web app to provide this structure for how applications are built, deployed, and managed across many servers.
|
||||
|
||||
## docker
|
||||
|
||||
Monitor is opinionated by design, and [docker](https://docs.docker.com/) is the tool of choice. Docker provides the ability to package applications and their runtime dependencies into a standalone bundle, called an *image*. This makes them easy to "ship" to any server and run without the hassle of setting up the runtime environment. Docker uses the image as a sort of template to create *containers*. Containers are kind of like virtual machines but with different performance characteristics, namely that processes contained still run natively on the system kernel. The file system is seperate though, and like virtual machines, they can be created, started, stopped, and destroyed.
|
||||
|
||||
## monitor
|
||||
|
||||
Monitor is a solution for handling for the following:
|
||||
|
||||
1. Build application source into auto-versioned images.
|
||||
2. Create, start, stop, and restart Docker containers, and view their status and logs.
|
||||
3. Keep a record of all the actions that are performed and by whom.
|
||||
4. View realtime and historical system resource usage.
|
||||
5. Alerting for server health, like high cpu, memory, disk, etc.
|
||||
|
||||
## architecture and components
|
||||
|
||||
Monitor is composed of a single core and any amount of connected servers running the periphery application.
|
||||
|
||||
### monitor core
|
||||
The core is a web server that hosts the core API and serves the frontend to be accessed in a web browser. All user interaction with the connected servers flow through the core. It is the stateful part of the system, with the application state stored on an instance of MongoDB.
|
||||
|
||||
### monitor periphery
|
||||
The periphery is a stateless web server that exposes API called by the core. The core calls this API to get system usage and container status / logs, clone git repos, and perform docker actions. It is only intended to be reached from the core, and has an address whitelist to limit the IPs allowed to call this API.
|
||||
|
||||
### monitor cli
|
||||
This is a simple standalone cli that helps perform some actions required to setup monitor core and periphery, like generating config files.
|
||||
|
||||
## core API
|
||||
|
||||
Monitor exposes powerful functionality over the core's REST API, enabling infrastructure engineers to manage deployments programmatically in addition to with the GUI. There is a [rust crate](https://crates.io/crates/monitor_client) to simplify programmatic interaction with the API, but in general this can be accomplished using any programming language that can make REST requests.
|
||||
|
||||
## permissioning
|
||||
|
||||
Monitor is a system designed to be used by many users, whether they are developers, operations personnel, or administrators. The ability to affect an applications state is very powerful, so monitor has a granular permissioning system to only provide this functionality to the intended users. The permissioning system is explained in detail in the [permissioning](https://github.com/mbecker20/monitor/blob/main/docs/permissions.md) section.
|
||||
|
||||
User sign-on is possible using username / password, or with Oauth (Github and Google). Allowed login methods can be configured from the [core config](https://github.com/mbecker20/monitor/blob/main/config_example/core.config.example.toml).
|
||||
|
||||
[next: connecting servers](https://github.com/mbecker20/monitor/blob/main/docs/servers.md)
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
1
docs/permissions.md
Normal file
1
docs/permissions.md
Normal file
@@ -0,0 +1 @@
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
30
docs/servers.md
Normal file
30
docs/servers.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# connecting servers
|
||||
|
||||
Integrating a device into the monitor system has 2 steps:
|
||||
|
||||
1. Setup and start the periphery agent on the server
|
||||
2. Adding the server to monitor via the core API
|
||||
|
||||
## setup monitor periphery
|
||||
|
||||
The easiest way to do this is to follow the [monitor guide](https://github.com/mbecker20/monitor-guide). This is a repo containing directions and scripts enabling command line installation via ssh or remotely.
|
||||
|
||||
### manual install steps
|
||||
|
||||
1. Download the periphery binary from the latest [release](https://github.com/mbecker20/monitor/releases) or install it using [cargo](https://crates.io/crates/monitor_periphery). If the monitor cli.
|
||||
2. Create and edit ~/.monitor/periphery.config.toml, following the [config example](https://github.com/mbecker20/monitor/blob/main/config_example/periphery.config.example.toml). The file can be anywhere, it can be passed to periphery via the --config-path flag or with the CONFIG_PATH environment variable. The monitor cli can also be used: ```monitor periphery gen-config```
|
||||
3. Ensure that inbound connectivity is allowed on the port specified in periphery.config.toml (default 8000).
|
||||
4. Install docker. Make sure whatever user periphery is run as has access to the docker group without sudo.
|
||||
5. Start the periphery binary with your preferred process manager, like systemd. The config read from the file is printed on startup, ensure that it is as expected.
|
||||
|
||||
## adding the server to monitor
|
||||
|
||||
The easiest way to add the server is with the GUI. On the home page, click the + button to the right of the server search bar, configure the name and address of the server. The address is the full http/s url to the periphery server, eg http://12.34.56.78:8000.
|
||||
|
||||
Once it is added, you can use access the GUI to modify some config, like the alerting thresholds for cpu, memory and disk usage. A server can also be temporarily disabled, this will prevent alerting if it goes offline.
|
||||
|
||||
Since no state is stored on the periphery servers, you can easily redirect all builds / deployments to be hosted on a different server. Just update the address to point to the new server.
|
||||
|
||||
[next: building](https://github.com/mbecker20/monitor/blob/main/docs/builds.md)
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
5
docs/setup.md
Normal file
5
docs/setup.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# setting up monitor core
|
||||
|
||||
|
||||
|
||||
[back to table of contents](https://github.com/mbecker20/monitor/blob/main/readme.md)
|
||||
12
frontend/public/icons/duplicate.svg
Normal file
12
frontend/public/icons/duplicate.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Rounded_Rectangle_2_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
|
||||
<g id="Rounded_Rectangle_2">
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#fceade" d="M15,4H1C0.45,4,0,4.45,0,5v14c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1V5
|
||||
C16,4.45,15.55,4,15,4z M14,18H2V6h12V18z M19,0H5C4.45,0,4,0.45,4,1v2h2V2h12v12h-1v2h2c0.55,0,1-0.45,1-1V1
|
||||
C20,0.45,19.55,0,19,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 679 B |
105
frontend/src/components/CopyMenu.tsx
Normal file
105
frontend/src/components/CopyMenu.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { Component, createSignal } from "solid-js";
|
||||
import { client, pushNotification } from "..";
|
||||
import { useAppState } from "../state/StateProvider";
|
||||
import { Build, Deployment } from "../types";
|
||||
import { getId } from "../util/helpers";
|
||||
import { useToggle } from "../util/hooks";
|
||||
import ConfirmButton from "./shared/ConfirmButton";
|
||||
import Icon from "./shared/Icon";
|
||||
import Input from "./shared/Input";
|
||||
import Flex from "./shared/layout/Flex";
|
||||
import Grid from "./shared/layout/Grid";
|
||||
import CenterMenu from "./shared/menu/CenterMenu";
|
||||
import Selector from "./shared/menu/Selector";
|
||||
|
||||
const CopyMenu: Component<{
|
||||
type: "deployment" | "build";
|
||||
id: string;
|
||||
}> = (p) => {
|
||||
const navigate = useNavigate();
|
||||
const [show, toggleShow] = useToggle();
|
||||
const [newName, setNewName] = createSignal("");
|
||||
const { builds, deployments, servers } = useAppState();
|
||||
const curr_server = () => {
|
||||
if (p.type === "build") {
|
||||
return builds.get(p.id)!.server_id;
|
||||
} else {
|
||||
return deployments.get(p.id)!.deployment.server_id;
|
||||
}
|
||||
}
|
||||
const [selectedId, setSelected] = createSignal(curr_server());
|
||||
const name = () => {
|
||||
if (p.type === "build") {
|
||||
return builds.get(p.id)?.name;
|
||||
} else if (p.type === "deployment") {
|
||||
return deployments.get(p.id)?.deployment.name;
|
||||
}
|
||||
};
|
||||
const copy = () => {
|
||||
if (newName().length !== 0) {
|
||||
let promise: Promise<Build | Deployment>;
|
||||
if (p.type === "build") {
|
||||
promise = client.copy_build(p.id, {
|
||||
name: newName(),
|
||||
server_id: selectedId(),
|
||||
});
|
||||
} else {
|
||||
promise = client.copy_deployment(p.id, {
|
||||
name: newName(),
|
||||
server_id: selectedId(),
|
||||
});
|
||||
}
|
||||
toggleShow();
|
||||
promise.then((val) => {
|
||||
navigate(`/${p.type}/${getId(val)}`);
|
||||
});
|
||||
} else {
|
||||
pushNotification("bad", "copy name cannot be empty");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<CenterMenu
|
||||
show={show}
|
||||
toggleShow={toggleShow}
|
||||
title={`copy ${p.type} | ${name()}`}
|
||||
target={<Icon type="duplicate" />}
|
||||
targetClass="blue"
|
||||
content={() => (
|
||||
<Grid placeItems="center">
|
||||
<Flex alignItems="center">
|
||||
<Input
|
||||
placeholder="copy name"
|
||||
class="card dark"
|
||||
style={{ padding: "0.5rem" }}
|
||||
value={newName()}
|
||||
onEdit={setNewName}
|
||||
/>
|
||||
<Selector
|
||||
label="target: "
|
||||
selected={selectedId()}
|
||||
items={servers.ids()!}
|
||||
onSelect={setSelected}
|
||||
itemMap={(id) => servers.get(id)!.server.name}
|
||||
targetClass="blue"
|
||||
targetStyle={{ display: "flex", gap: "0.5rem" }}
|
||||
searchStyle={{ width: "100%" }}
|
||||
position="bottom right"
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
style={{ width: "100%" }}
|
||||
onConfirm={copy}
|
||||
>
|
||||
copy {p.type}
|
||||
</ConfirmButton>
|
||||
</Grid>
|
||||
)}
|
||||
position="center"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CopyMenu;
|
||||
@@ -10,20 +10,21 @@ import { useActionStates } from "./ActionStateProvider";
|
||||
import { client } from "../..";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { PermissionLevel } from "../../types";
|
||||
import { PermissionLevel, ServerStatus } from "../../types";
|
||||
|
||||
const Actions: Component<{}> = (p) => {
|
||||
const { user } = useUser();
|
||||
const params = useParams() as { id: string };
|
||||
const { builds } = useAppState();
|
||||
const { builds, servers } = useAppState();
|
||||
const build = () => builds.get(params.id)!;
|
||||
const server = () => build() && servers.get(build()!.server_id);
|
||||
const actions = useActionStates();
|
||||
const userCanExecute = () =>
|
||||
user().admin ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Execute ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Update;
|
||||
return (
|
||||
<Show when={userCanExecute()}>
|
||||
<Show when={userCanExecute() && server()?.status === ServerStatus.Ok}>
|
||||
<Grid class={combineClasses("card shadow")} gridTemplateRows="auto 1fr">
|
||||
<h1>actions</h1>
|
||||
<Grid style={{ height: "fit-content" }}>
|
||||
|
||||
@@ -13,6 +13,8 @@ import { A, useParams } from "@solidjs/router";
|
||||
import { PermissionLevel } from "../../types";
|
||||
import { client } from "../..";
|
||||
import HoverMenu from "../shared/menu/HoverMenu";
|
||||
import CopyMenu from "../CopyMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { builds, servers } = useAppState();
|
||||
@@ -43,21 +45,26 @@ const Header: Component<{}> = (p) => {
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{build().name}</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
onConfirm={() => {
|
||||
client.delete_build(params.id);
|
||||
}}
|
||||
class="red"
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
}
|
||||
content="delete build"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
<Flex alignItems="center">
|
||||
<CopyMenu type="build" id={params.id} />
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmMenuButton
|
||||
onConfirm={() => {
|
||||
client.delete_build(params.id);
|
||||
}}
|
||||
class="red"
|
||||
title={`delete build | ${build().name}`}
|
||||
match={build().name}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete build"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
|
||||
@@ -10,7 +10,7 @@ import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client } from "../../..";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { Build, Operation, PermissionLevel } from "../../../types";
|
||||
import { Build, Operation, PermissionLevel, ServerWithStatus } from "../../../types";
|
||||
import { getId } from "../../../util/helpers";
|
||||
|
||||
type ConfigBuild = Build & {
|
||||
@@ -22,6 +22,7 @@ type ConfigBuild = Build & {
|
||||
type State = {
|
||||
build: ConfigBuild;
|
||||
setBuild: SetStoreFunction<ConfigBuild>;
|
||||
server: () => ServerWithStatus | undefined
|
||||
reset: () => void;
|
||||
save: () => void;
|
||||
userCanUpdate: () => boolean;
|
||||
@@ -30,7 +31,7 @@ type State = {
|
||||
const context = createContext<State>();
|
||||
|
||||
export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const { ws, builds } = useAppState();
|
||||
const { ws, builds, servers } = useAppState();
|
||||
const params = useParams();
|
||||
const { user } = useUser();
|
||||
const [build, set] = createStore({
|
||||
@@ -44,6 +45,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
set(...args);
|
||||
set("updated", true);
|
||||
};
|
||||
const server = () => servers.get(builds.get(params.id)!.server_id);
|
||||
|
||||
const load = () => {
|
||||
// console.log("load build");
|
||||
@@ -105,6 +107,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const state = {
|
||||
build,
|
||||
setBuild,
|
||||
server,
|
||||
reset: load,
|
||||
save,
|
||||
userCanUpdate,
|
||||
|
||||
@@ -74,7 +74,7 @@ const EditBuildArgs: Component<{}> = (p) => {
|
||||
value={buildArgs()}
|
||||
onEdit={setBuildArgs}
|
||||
style={{
|
||||
width: "700px",
|
||||
width: "1000px",
|
||||
"max-width": "90vw",
|
||||
height: "80vh",
|
||||
padding: "1rem",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { client } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import Input from "../../../shared/Input";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
@@ -9,10 +10,14 @@ import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Docker: Component<{}> = (p) => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [dockerAccounts, setDockerAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client.get_server_docker_accounts(build.server_id).then(setDockerAccounts);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(build.server_id)
|
||||
.then(setDockerAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
@@ -6,12 +6,15 @@ import Input from "../../../shared/Input";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { client } from "../../../..";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
|
||||
const Git: Component<{}> = (p) => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [githubAccounts, setGithubAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client.get_server_github_accounts(build.server_id).then(setGithubAccounts)
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client.get_server_github_accounts(build.server_id).then(setGithubAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Match, Show, Switch } from "solid-js";
|
||||
import { client, pushNotification } from "../..";
|
||||
import { client } from "../..";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import ConfirmButton from "../shared/ConfirmButton";
|
||||
@@ -9,24 +9,33 @@ import Grid from "../shared/layout/Grid";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import HoverMenu from "../shared/menu/HoverMenu";
|
||||
import { useActionStates } from "./ActionStateProvider";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { combineClasses } from "../../util/helpers";
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import { DockerContainerState, PermissionLevel } from "../../types";
|
||||
import {
|
||||
DockerContainerState,
|
||||
PermissionLevel,
|
||||
ServerStatus,
|
||||
} from "../../types";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Actions: Component<{}> = (p) => {
|
||||
const { deployments, builds, getPermissionOnDeployment } = useAppState();
|
||||
const { deployments, builds, servers, getPermissionOnDeployment } =
|
||||
useAppState();
|
||||
const params = useParams();
|
||||
const { user, user_id } = useUser();
|
||||
const deployment = () => deployments.get(params.id)!;
|
||||
const server = () =>
|
||||
deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
const show = () => {
|
||||
const permissions = getPermissionOnDeployment(params.id);
|
||||
return (
|
||||
server()?.status === ServerStatus.Ok &&
|
||||
deployment() &&
|
||||
(user().admin ||
|
||||
permissions === PermissionLevel.Execute ||
|
||||
permissions === PermissionLevel.Update)
|
||||
);
|
||||
};
|
||||
const deployment = () => deployments.get(params.id)!;
|
||||
const showBuild = () => {
|
||||
const build = deployment().deployment.build_id
|
||||
? builds.get(deployment().deployment.build_id!)
|
||||
@@ -117,13 +126,17 @@ const Actions: Component<{}> = (p) => {
|
||||
};
|
||||
|
||||
const Build: Component = () => {
|
||||
const { ws, deployments } = useAppState();
|
||||
const { deployments } = useAppState();
|
||||
const params = useParams();
|
||||
const actions = useActionStates();
|
||||
const buildID = () => deployments.get(params.id)!.deployment.build_id!;
|
||||
return (
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
<A href={`/build/${buildID()}`} class="pointer">
|
||||
<A
|
||||
href={`/build/${buildID()}`}
|
||||
class="pointer"
|
||||
style={{ padding: 0, "font-size": "16px" }}
|
||||
>
|
||||
build
|
||||
</A>
|
||||
<Show
|
||||
@@ -152,6 +165,8 @@ const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||
const params = useParams();
|
||||
// const deployment = () => deployments.get(params.id)!;
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
return (
|
||||
<Show
|
||||
when={!actions.deploying}
|
||||
@@ -163,14 +178,30 @@ const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||
>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.deploy_container(params.id);
|
||||
}}
|
||||
<Show
|
||||
when={p.redeploy}
|
||||
fallback={
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.deploy_container(params.id);
|
||||
}}
|
||||
>
|
||||
<Icon type={"play"} />
|
||||
</ConfirmButton>
|
||||
}
|
||||
>
|
||||
<Icon type={p.redeploy ? "reset" : "play"} />
|
||||
</ConfirmButton>
|
||||
<ConfirmMenuButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.deploy_container(params.id);
|
||||
}}
|
||||
title={`redeploy container | ${name()}`}
|
||||
match={name()!}
|
||||
>
|
||||
<Icon type={"reset"} />
|
||||
</ConfirmMenuButton>
|
||||
</Show>
|
||||
}
|
||||
content={p.redeploy ? "redeploy container" : "deploy container"}
|
||||
position="bottom center"
|
||||
@@ -183,6 +214,8 @@ const Deploy: Component<{ redeploy?: boolean }> = (p) => {
|
||||
const RemoveContainer = () => {
|
||||
const params = useParams();
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
return (
|
||||
<Show
|
||||
when={!actions.removing}
|
||||
@@ -194,14 +227,16 @@ const RemoveContainer = () => {
|
||||
>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
<ConfirmMenuButton
|
||||
class="red"
|
||||
onConfirm={() => {
|
||||
client.remove_container(params.id);
|
||||
}}
|
||||
title={`destroy container | ${name()}`}
|
||||
match={name()!}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete container"
|
||||
position="bottom center"
|
||||
@@ -245,6 +280,8 @@ const Start = () => {
|
||||
const Stop = () => {
|
||||
const params = useParams();
|
||||
const actions = useActionStates();
|
||||
const { deployments } = useAppState();
|
||||
const name = () => deployments.get(params.id)?.deployment.name;
|
||||
return (
|
||||
<Show
|
||||
when={!actions.stopping}
|
||||
@@ -256,14 +293,16 @@ const Stop = () => {
|
||||
>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
<ConfirmMenuButton
|
||||
class="orange"
|
||||
onConfirm={() => {
|
||||
client.stop_container(params.id);
|
||||
}}
|
||||
title={`stop container | ${name()}`}
|
||||
match={name()!}
|
||||
>
|
||||
<Icon type="pause" />
|
||||
</ConfirmButton>
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="stop container"
|
||||
position="bottom center"
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { MAX_PAGE_WIDTH } from "../..";
|
||||
import { Component, onCleanup, Show } from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import { PermissionLevel } from "../../types";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { ServerStatus } from "../../types";
|
||||
import NotFound from "../NotFound";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Actions from "./Actions";
|
||||
import { ActionStateProvider } from "./ActionStateProvider";
|
||||
import Header from "./Header";
|
||||
import { ConfigProvider } from "./tabs/config/Provider";
|
||||
import DeploymentTabs from "./tabs/Tabs";
|
||||
import Updates from "./Updates";
|
||||
|
||||
const Deployment2: Component<{}> = (p) => {
|
||||
const POLLING_RATE = 10000;
|
||||
// let interval = -1;
|
||||
|
||||
const Deployment: Component<{}> = (p) => {
|
||||
const { servers, deployments } = useAppState();
|
||||
const { isSemiMobile } = useAppDimensions();
|
||||
const params = useParams();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
const server = () =>
|
||||
deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
// clearInterval(interval);
|
||||
// interval = setInterval(async () => {
|
||||
// if (server()?.status === ServerStatus.Ok) {
|
||||
// const deployment = await client.get_deployment(params.id);
|
||||
// deployments.update(deployment);
|
||||
// }
|
||||
// }, POLLING_RATE);
|
||||
// onCleanup(() => clearInterval(interval));
|
||||
return (
|
||||
<Show
|
||||
when={deployment() && server()}
|
||||
@@ -53,46 +61,4 @@ const Deployment2: Component<{}> = (p) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Deployment: Component<{}> = (p) => {
|
||||
const { servers, deployments } = useAppState();
|
||||
const params = useParams();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
const server = () => deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
const { isSemiMobile } = useAppDimensions();
|
||||
const { user } = useUser();
|
||||
const userCanUpdate = () => user().admin || deployment()?.deployment.permissions![getId(user())] === PermissionLevel.Update;
|
||||
return (
|
||||
<Show
|
||||
when={deployment() && server()}
|
||||
fallback={<NotFound type="deployment" />}
|
||||
>
|
||||
<ActionStateProvider>
|
||||
<Grid class={combineClasses("content")}>
|
||||
{/* left / actions */}
|
||||
<Grid class="left-content">
|
||||
<Header />
|
||||
<Actions />
|
||||
<Show when={!isSemiMobile() && userCanUpdate()}>
|
||||
<Updates />
|
||||
</Show>
|
||||
</Grid>
|
||||
{/* right / tabs */}
|
||||
<Show
|
||||
when={userCanUpdate()}
|
||||
fallback={
|
||||
<h2 class={combineClasses("card tabs shadow")}>
|
||||
you do not have permission to view this deployment
|
||||
</h2>
|
||||
}
|
||||
>
|
||||
<ConfigProvider>
|
||||
<DeploymentTabs />
|
||||
</ConfigProvider>
|
||||
</Show>
|
||||
</Grid>
|
||||
</ActionStateProvider>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export default Deployment2;
|
||||
export default Deployment;
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
deploymentHeaderStateClass,
|
||||
getId,
|
||||
} from "../../util/helpers";
|
||||
import ConfirmButton from "../shared/ConfirmButton";
|
||||
import Icon from "../shared/Icon";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
@@ -17,6 +16,8 @@ import Updates from "./Updates";
|
||||
import { DockerContainerState, PermissionLevel } from "../../types";
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import { client } from "../..";
|
||||
import CopyMenu from "../CopyMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { deployments, servers } = useAppState();
|
||||
@@ -53,21 +54,35 @@ const Header: Component<{}> = (p) => {
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{deployment()!.deployment.name}</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmButton
|
||||
onConfirm={() => {
|
||||
client.delete_deployment(params.id);
|
||||
}}
|
||||
class="red"
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
}
|
||||
content="delete deployment"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
<Flex alignItems="center">
|
||||
<CopyMenu type="deployment" id={params.id} />
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmMenuButton
|
||||
onConfirm={() => {
|
||||
client.delete_deployment(params.id);
|
||||
}}
|
||||
class="red"
|
||||
title={`delete deployment | ${
|
||||
deployment().deployment.name
|
||||
}`}
|
||||
match={deployment().deployment.name}
|
||||
info={
|
||||
<Show when={deployment().container}>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
warning! this will destroy this deployments container
|
||||
</div>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete deployment"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
DockerContainerState,
|
||||
Log as LogType,
|
||||
Operation,
|
||||
ServerStatus,
|
||||
} from "../../../types";
|
||||
import { client } from "../../..";
|
||||
import SimpleTabs from "../../shared/tabs/SimpleTabs";
|
||||
@@ -26,18 +27,23 @@ import { useUser } from "../../../state/UserProvider";
|
||||
|
||||
const DeploymentTabs: Component<{}> = () => {
|
||||
const { user } = useUser();
|
||||
const { deployments, ws } = useAppState();
|
||||
const { deployments, ws, servers } = useAppState();
|
||||
const params = useParams();
|
||||
const deployment = () => deployments.get(params.id);
|
||||
const server = () =>
|
||||
deployment() && servers.get(deployment()!.deployment.server_id);
|
||||
const [logTail, setLogTail] = createSignal(50);
|
||||
const [log, setLog] = createSignal<LogType>();
|
||||
const status = () =>
|
||||
deployment()!.state === DockerContainerState.NotDeployed
|
||||
? "not deployed"
|
||||
: deployment()!.container?.state;
|
||||
// const status = () =>
|
||||
// deployment()!.state === DockerContainerState.NotDeployed
|
||||
// ? "not deployed"
|
||||
// : deployment()!.container?.state;
|
||||
const log_available = () =>
|
||||
server()?.status === ServerStatus.Ok &&
|
||||
deployment()?.state !== DockerContainerState.NotDeployed;
|
||||
const loadLog = async () => {
|
||||
console.log("load log");
|
||||
if (deployment()?.state !== DockerContainerState.NotDeployed) {
|
||||
if (log_available()) {
|
||||
console.log("load log");
|
||||
const log = await client.get_deployment_container_log(
|
||||
params.id,
|
||||
logTail()
|
||||
@@ -78,7 +84,7 @@ const DeploymentTabs: Component<{}> = () => {
|
||||
title: "config",
|
||||
element: () => <Config />,
|
||||
},
|
||||
status() !== "not deployed" && [
|
||||
log_available() && [
|
||||
{
|
||||
title: "log",
|
||||
element: () => (
|
||||
@@ -90,7 +96,7 @@ const DeploymentTabs: Component<{}> = () => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
status() !== "not deployed" && {
|
||||
{
|
||||
title: "error log",
|
||||
titleElement: () => (
|
||||
<Flex gap="0.5rem" alignItems="center">
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client, pushNotification } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { useUser } from "../../../../state/UserProvider";
|
||||
import { Deployment, Operation, PermissionLevel } from "../../../../types";
|
||||
import { Deployment, Operation, PermissionLevel, ServerStatus, ServerWithStatus } from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
|
||||
type ConfigDeployment = Deployment & {
|
||||
@@ -25,6 +25,7 @@ type State = {
|
||||
editing: Accessor<boolean>;
|
||||
deployment: ConfigDeployment;
|
||||
setDeployment: SetStoreFunction<ConfigDeployment>;
|
||||
server: () => ServerWithStatus | undefined;
|
||||
reset: () => void;
|
||||
save: () => void;
|
||||
networks: Accessor<any[]>;
|
||||
@@ -34,7 +35,7 @@ type State = {
|
||||
const context = createContext<State>();
|
||||
|
||||
export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const { ws, deployments } = useAppState();
|
||||
const { ws, deployments, servers } = useAppState();
|
||||
const params = useParams();
|
||||
const { user } = useUser();
|
||||
const [editing] = createSignal(false);
|
||||
@@ -87,11 +88,13 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
createEffect(load);
|
||||
|
||||
const [networks, setNetworks] = createSignal<any[]>([]);
|
||||
const server = () => servers.get(deployments.get(params.id)!.deployment.server_id);
|
||||
createEffect(() => {
|
||||
console.log("load networks");
|
||||
client
|
||||
.get_docker_networks(deployments.get(params.id)!.deployment.server_id)
|
||||
.then(setNetworks);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_docker_networks(deployments.get(params.id)!.deployment.server_id)
|
||||
.then(setNetworks);
|
||||
}
|
||||
});
|
||||
|
||||
const save = () => {
|
||||
@@ -141,6 +144,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
editing,
|
||||
deployment,
|
||||
setDeployment,
|
||||
server,
|
||||
reset: load,
|
||||
save,
|
||||
networks,
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { useAppState } from "../../../../../state/StateProvider";
|
||||
import { ServerStatus } from "../../../../../types";
|
||||
import { combineClasses } from "../../../../../util/helpers";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
import Selector from "../../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const DockerAccount: Component<{}> = (p) => {
|
||||
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||
const { deployment, setDeployment, server, userCanUpdate } = useConfig();
|
||||
const [dockerAccounts, setDockerAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client
|
||||
.get_server_docker_accounts(deployment.server_id)
|
||||
.then(setDockerAccounts);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(deployment.server_id)
|
||||
.then(setDockerAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Flex
|
||||
|
||||
@@ -74,7 +74,7 @@ const EditDotEnv: Component<{}> = (p) => {
|
||||
value={dotenv()}
|
||||
onEdit={setDotEnv}
|
||||
style={{
|
||||
width: "700px",
|
||||
width: "1000px",
|
||||
"max-width": "90vw",
|
||||
height: "80vh",
|
||||
padding: "1rem",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import { client } from "../../../../..";
|
||||
import { ServerStatus } from "../../../../../types";
|
||||
import { combineClasses } from "../../../../../util/helpers";
|
||||
import Input from "../../../../shared/Input";
|
||||
import Flex from "../../../../shared/layout/Flex";
|
||||
@@ -8,10 +9,14 @@ import Selector from "../../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Git: Component<{}> = (p) => {
|
||||
const { deployment, setDeployment, userCanUpdate } = useConfig();
|
||||
const { deployment, server, setDeployment, userCanUpdate } = useConfig();
|
||||
const [githubAccounts, setGithubAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
client.get_server_github_accounts(deployment.server_id).then(setGithubAccounts);
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_github_accounts(deployment.server_id)
|
||||
.then(setGithubAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createMemo,
|
||||
createSignal,
|
||||
onCleanup,
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client, pushNotification } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { DockerContainerState, Log as LogType } from "../../../../types";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { useBuffer } from "../../../../util/hooks";
|
||||
import { useBuffer, useLocalStorageToggle } from "../../../../util/hooks";
|
||||
import Icon from "../../../shared/Icon";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
@@ -12,6 +19,10 @@ 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 Log: Component<{
|
||||
log?: LogType;
|
||||
logTail: number;
|
||||
@@ -57,9 +68,23 @@ const Log: Component<{
|
||||
}
|
||||
};
|
||||
const buffer = useBuffer(scrolled, 250);
|
||||
const [poll, togglePoll] = useLocalStorageToggle(
|
||||
"deployment-log-polling",
|
||||
true
|
||||
);
|
||||
clearInterval(interval);
|
||||
interval = setInterval(() => {
|
||||
if (poll() && deployment()?.state === DockerContainerState.Running) {
|
||||
p.reload();
|
||||
}
|
||||
}, POLLING_RATE);
|
||||
onCleanup(() => clearInterval(interval));
|
||||
return (
|
||||
<Show when={p.log}>
|
||||
<Grid gap="0.5rem" style={{ height: "100%", "grid-template-rows": "auto 1fr" }}>
|
||||
<Grid
|
||||
gap="0.5rem"
|
||||
style={{ height: "100%", "grid-template-rows": "auto 1fr" }}
|
||||
>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
@@ -100,6 +125,9 @@ const Log: Component<{
|
||||
>
|
||||
<Icon type="refresh" />
|
||||
</button>
|
||||
<button class={poll() ? "green" : "red"} onClick={togglePoll}>
|
||||
{poll() ? "" : "don't "}poll
|
||||
</button>
|
||||
</Flex>
|
||||
<div style={{ position: "relative", height: "100%" }}>
|
||||
<div
|
||||
|
||||
@@ -25,25 +25,27 @@ const Actions: Component<{}> = (p) => {
|
||||
<Show
|
||||
when={server() && server().status === ServerStatus.Ok && userCanExecute()}
|
||||
>
|
||||
<Grid class={combineClasses("card shadow")}>
|
||||
<Grid class={combineClasses("card shadow")} gridTemplateRows="auto 1fr">
|
||||
<h1>actions</h1>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune images <PruneImages />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune containers <PruneContainers />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune networks{" "}
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.prune_docker_networks(params.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="cut" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
<Grid style={{ height: "fit-content" }}>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune images <PruneImages />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune containers <PruneContainers />
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
prune networks{" "}
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.prune_docker_networks(params.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="cut" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
|
||||
@@ -9,10 +9,12 @@ import Grid from "../shared/layout/Grid";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useLocalStorageToggle } from "../../util/hooks";
|
||||
import Updates from "./Updates";
|
||||
import { PermissionLevel, Server } from "../../types";
|
||||
import { PermissionLevel, ServerStatus } from "../../types";
|
||||
import { A, useParams } from "@solidjs/router";
|
||||
import { client } from "../..";
|
||||
import Loading from "../shared/loading/Loading";
|
||||
import HoverMenu from "../shared/menu/HoverMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { servers } = useAppState();
|
||||
@@ -26,25 +28,73 @@ const Header: Component<{}> = (p) => {
|
||||
const userCanUpdate = () =>
|
||||
user().admin ||
|
||||
server().server.permissions![getId(user())] === PermissionLevel.Update;
|
||||
const [version] = createResource(async () => {
|
||||
return await client.get_server_version(params.id).catch();
|
||||
});
|
||||
const [version] = createResource(
|
||||
() => server() && server().status === ServerStatus.Ok,
|
||||
async (do_it?: boolean) => {
|
||||
if (!do_it) return;
|
||||
return await client.get_server_version(params.id).catch();
|
||||
}
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
<Grid
|
||||
gap="0.5rem"
|
||||
class={combineClasses("card shadow")}
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
style={{
|
||||
position: "relative",
|
||||
cursor: isSemiMobile() ? "pointer" : undefined,
|
||||
height: "fit-content",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isSemiMobile()) toggleShowUpdates();
|
||||
}}
|
||||
>
|
||||
<Grid gap="0.1rem">
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{server().server.name}</h1>
|
||||
<Show when={userCanUpdate()}>
|
||||
<Flex alignItems="center">
|
||||
<div class={serverStatusClass(server().status)}>{status()}</div>
|
||||
<HoverMenu
|
||||
target={
|
||||
<A
|
||||
href={`/server/${params.id}/stats`}
|
||||
class="blue"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Icon type="timeline-line-chart" />
|
||||
</A>
|
||||
}
|
||||
content="server stats"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
<HoverMenu
|
||||
target={
|
||||
<ConfirmMenuButton
|
||||
onConfirm={() => {
|
||||
client.delete_server(params.id);
|
||||
}}
|
||||
class="red"
|
||||
title={`delete server | ${server().server.name}`}
|
||||
match={server().server.name}
|
||||
info={
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
warning! this will also delete all builds and
|
||||
deployments on this server
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmMenuButton>
|
||||
}
|
||||
content="delete server"
|
||||
position="bottom center"
|
||||
padding="0.5rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Flex gap="0.2rem" alignItems="center" style={{ opacity: 0.8 }}>
|
||||
<div>server</div>
|
||||
<Show when={server().server.region}>
|
||||
@@ -52,42 +102,13 @@ const Header: Component<{}> = (p) => {
|
||||
{server().server.region}
|
||||
</Show>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<Flex alignItems="center">
|
||||
<Show when={!isMobile()}>
|
||||
<Show when={version()} fallback={<Loading type="three-dot" />}>
|
||||
<Show when={version()}>
|
||||
<div style={{ opacity: 0.7 }}>periphery v{version()}</div>
|
||||
</Show>
|
||||
</Show>
|
||||
<div class={serverStatusClass(server().status)}>{status()}</div>
|
||||
<A
|
||||
href={`/server/${params.id}/stats`}
|
||||
class="blue"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Icon type="timeline-line-chart" />
|
||||
</A>
|
||||
<Show when={userCanUpdate()}>
|
||||
<ConfirmButton
|
||||
onConfirm={() => {
|
||||
client.delete_server(params.id);
|
||||
}}
|
||||
class="red"
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Show when={isSemiMobile()}>
|
||||
<Flex gap="0.5rem" alignItems="center" class="show-updates-indicator">
|
||||
updates{" "}
|
||||
<Icon
|
||||
type={showUpdates() ? "chevron-up" : "chevron-down"}
|
||||
width="0.9rem"
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<Show when={isSemiMobile() && showUpdates()}>
|
||||
<Updates />
|
||||
</Show>
|
||||
|
||||
@@ -32,7 +32,7 @@ const Server: Component<{}> = (p) => {
|
||||
style={{ width: "100%" }}
|
||||
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||
>
|
||||
<Grid>
|
||||
<Grid style={{ "flex-grow": 1, "grid-auto-rows": "auto 1fr" }}>
|
||||
<Header />
|
||||
<Actions />
|
||||
</Grid>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useParams } from "@solidjs/router";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { ServerStatus } from "../../../types";
|
||||
import SimpleTabs from "../../shared/tabs/SimpleTabs";
|
||||
import { Tab } from "../../shared/tabs/Tabs";
|
||||
import Config from "./config/Config";
|
||||
@@ -26,7 +27,7 @@ const ServerTabs: Component<{}> = (p) => {
|
||||
title: "config",
|
||||
element: () => <Config />,
|
||||
},
|
||||
{
|
||||
server()?.status === ServerStatus.Ok && {
|
||||
title: "info",
|
||||
element: () => <Info />
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@ import { createStore, SetStoreFunction } from "solid-js/store";
|
||||
import { client } from "../../../..";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { useUser } from "../../../../state/UserProvider";
|
||||
import { Server, Operation, PermissionLevel } from "../../../../types";
|
||||
import { Server, Operation, PermissionLevel, ServerStatus } from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
|
||||
type ConfigServer = Server & { loaded: boolean; updated: boolean };
|
||||
@@ -59,7 +59,9 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
const [networks, setNetworks] = createSignal<any[]>([]);
|
||||
const loadNetworks = () => {
|
||||
// console.log("load networks");
|
||||
client.get_docker_networks(params.id).then(setNetworks);
|
||||
if (servers.get(params.id)?.status === ServerStatus.Ok) {
|
||||
client.get_docker_networks(params.id).then(setNetworks);
|
||||
}
|
||||
};
|
||||
createEffect(loadNetworks);
|
||||
|
||||
|
||||
83
frontend/src/components/shared/ConfirmMenuButton.tsx
Normal file
83
frontend/src/components/shared/ConfirmMenuButton.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import { Component, createSignal, JSX } from "solid-js";
|
||||
import { pushNotification } from "../..";
|
||||
import { useToggle } from "../../util/hooks";
|
||||
import ConfirmButton from "./ConfirmButton";
|
||||
import Input from "./Input";
|
||||
import Grid from "./layout/Grid";
|
||||
import CenterMenu from "./menu/CenterMenu";
|
||||
|
||||
const ConfirmMenuButton: Component<{
|
||||
onConfirm?: () => void;
|
||||
class?: string;
|
||||
style?: JSX.CSSProperties;
|
||||
title: string;
|
||||
match: string;
|
||||
info?: JSX.Element;
|
||||
children: JSX.Element;
|
||||
}> = (p) => {
|
||||
const [show, toggleShow] = useToggle();
|
||||
|
||||
return (
|
||||
<CenterMenu
|
||||
show={show}
|
||||
toggleShow={toggleShow}
|
||||
title={p.title}
|
||||
targetClass={p.class}
|
||||
target={p.children}
|
||||
content={() => (
|
||||
<ConfirmMenuContent
|
||||
class={p.class}
|
||||
title={p.title}
|
||||
match={p.match}
|
||||
info={p.info}
|
||||
onConfirm={p.onConfirm}
|
||||
/>
|
||||
)}
|
||||
position="center"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const ConfirmMenuContent: Component<{
|
||||
class?: string;
|
||||
title: string;
|
||||
match: string;
|
||||
onConfirm?: () => void;
|
||||
info?: JSX.Element;
|
||||
}> = (p) => {
|
||||
const [input, setInput] = createSignal("");
|
||||
return (
|
||||
<Grid placeItems="center">
|
||||
{p.info}
|
||||
<Input
|
||||
class="darkgrey"
|
||||
style={{
|
||||
padding: "0.5rem",
|
||||
width: "100%",
|
||||
"border-style": "solid",
|
||||
"border-width": "1px",
|
||||
"border-color": input() === p.match ? "#41764c" : "#952E23",
|
||||
}}
|
||||
placeholder={`enter '${p.match}'`}
|
||||
onEdit={setInput}
|
||||
value={input()}
|
||||
autofocus
|
||||
/>
|
||||
<ConfirmButton
|
||||
class={p.class}
|
||||
style={{ width: "100%" }}
|
||||
onConfirm={() => {
|
||||
if (input() === p.match) {
|
||||
p.onConfirm && p.onConfirm();
|
||||
} else {
|
||||
pushNotification("bad", "must enter value to confirm");
|
||||
}
|
||||
}}
|
||||
>
|
||||
{p.title}
|
||||
</ConfirmButton>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmMenuButton;
|
||||
@@ -45,7 +45,8 @@ export type IconType =
|
||||
| "cog"
|
||||
| "home"
|
||||
| "timeline-line-chart"
|
||||
| "arrow-right";
|
||||
| "arrow-right"
|
||||
| "duplicate";
|
||||
|
||||
const ICON_DIR = import.meta.env.VITE_ICON_DIR || "/assets/icons"
|
||||
|
||||
|
||||
@@ -33,10 +33,12 @@ const Selector: Component<{
|
||||
itemClass?: string;
|
||||
itemStyle?: JSX.CSSProperties;
|
||||
label?: JSXElement;
|
||||
itemMap?: (item: string) => string;
|
||||
}> = (p) => {
|
||||
const [show, toggle] = useToggle();
|
||||
const [search, setSearch] = createSignal("");
|
||||
let ref: HTMLInputElement | undefined;
|
||||
const current = () => (p.itemMap ? p.itemMap(p.selected) : p.selected);
|
||||
createEffect(() => {
|
||||
if (show()) setTimeout(() => ref?.focus(), 200);
|
||||
});
|
||||
@@ -46,7 +48,7 @@ const Selector: Component<{
|
||||
fallback={
|
||||
<div class={p.disabledClass} style={p.disabledStyle}>
|
||||
<Show when={p.label}>{p.label}</Show>
|
||||
{p.selected}
|
||||
{current()}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
@@ -60,7 +62,7 @@ const Selector: Component<{
|
||||
target={
|
||||
<button class={p.targetClass} onClick={toggle} style={p.targetStyle}>
|
||||
<Show when={p.label}>{p.label}</Show>
|
||||
{p.selected}
|
||||
{current()}
|
||||
<Icon type="chevron-down" />
|
||||
</button>
|
||||
}
|
||||
@@ -84,7 +86,11 @@ const Selector: Component<{
|
||||
<For
|
||||
each={
|
||||
p.useSearch
|
||||
? p.items.filter((item) => item.includes(search()))
|
||||
? p.items.filter((item) =>
|
||||
p.itemMap
|
||||
? p.itemMap(item).includes(search())
|
||||
: item.includes(search())
|
||||
)
|
||||
: p.items
|
||||
}
|
||||
>
|
||||
@@ -101,7 +107,7 @@ const Selector: Component<{
|
||||
}}
|
||||
class={combineClasses(p.itemClass, s.SelectorItem)}
|
||||
>
|
||||
{item}
|
||||
{p.itemMap ? p.itemMap(item) : item}
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
|
||||
@@ -52,7 +52,7 @@ export default UpdateMenu;
|
||||
|
||||
const UpdateMenuContent: Component<{ update: UpdateType }> = (p) => {
|
||||
return (
|
||||
<Grid class={s.LogContainer}>
|
||||
<Grid class={combineClasses(s.LogContainer, "scroller")}>
|
||||
<UpdateSummary update={p.update} />
|
||||
<UpdateLogs update={p.update} />
|
||||
</Grid>
|
||||
@@ -66,7 +66,7 @@ const UpdateSummary: Component<{ update: UpdateType }> = (p) => {
|
||||
gap="0.5rem"
|
||||
class="card light shadow"
|
||||
gridTemplateColumns="1fr 1fr"
|
||||
style={{ width: "40rem", "max-width": "90vw" }}
|
||||
style={{ width: "100%", "box-sizing": "border-box" }}
|
||||
>
|
||||
<Flex gap="0.5rem" alignItems="center">
|
||||
status: <h2>{p.update.status.replaceAll("_", " ")}</h2>
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
|
||||
.LogContainer {
|
||||
max-height: 80vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.Log {
|
||||
@@ -49,8 +48,8 @@
|
||||
overflow-wrap: anywhere;
|
||||
/* word-wrap: break-word; */
|
||||
tab-size: 2;
|
||||
width: 40rem;
|
||||
max-width: 90vw;
|
||||
width: 1000px;
|
||||
max-width: 80vw;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(c.$darkgrey, 0.6);
|
||||
padding: 1rem;
|
||||
|
||||
@@ -102,8 +102,8 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
return PermissionLevel.None;
|
||||
}
|
||||
},
|
||||
serverStats: useServerStats(),
|
||||
serverInfo: useServerInfo(),
|
||||
serverStats: useServerStats(servers),
|
||||
serverInfo: useServerInfo(servers),
|
||||
groups,
|
||||
getPermissionOnGroup: (id: string) => {
|
||||
const group = groups.get(id)!;
|
||||
|
||||
@@ -47,13 +47,19 @@ export function useServers() {
|
||||
);
|
||||
}
|
||||
|
||||
export function useServerStats() {
|
||||
export function useServerStats(servers: ReturnType<typeof useServers>) {
|
||||
const [stats, set] = createSignal<Record<string, SystemStats | undefined>>(
|
||||
{}
|
||||
);
|
||||
const load = async (serverID: string) => {
|
||||
const stats = await client.get_server_stats(serverID);
|
||||
set((s) => ({ ...s, [serverID]: stats }));
|
||||
if (servers.get(serverID)?.status === ServerStatus.Ok) {
|
||||
try {
|
||||
const stats = await client.get_server_stats(serverID);
|
||||
set((s) => ({ ...s, [serverID]: stats }));
|
||||
} catch (error) {
|
||||
console.log("error getting server stats");
|
||||
}
|
||||
}
|
||||
};
|
||||
const loading: Record<string, boolean> = {};
|
||||
setTimeout(() => Object.keys(stats()).forEach(load), 30000);
|
||||
@@ -74,13 +80,19 @@ export function useServerStats() {
|
||||
};
|
||||
}
|
||||
|
||||
export function useServerInfo() {
|
||||
export function useServerInfo(servers: ReturnType<typeof useServers>) {
|
||||
const [info, set] = createSignal<
|
||||
Record<string, SystemInformation | undefined>
|
||||
>({});
|
||||
const load = async (serverID: string) => {
|
||||
const info = await client.get_server_system_info(serverID);
|
||||
set((s) => ({ ...s, [serverID]: info }));
|
||||
if (servers.get(serverID)?.status === ServerStatus.Ok) {
|
||||
try {
|
||||
const info = await client.get_server_system_info(serverID);
|
||||
set((s) => ({ ...s, [serverID]: info }));
|
||||
} catch (error) {
|
||||
console.log("error getting server info", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const loading: Record<string, boolean> = {};
|
||||
return {
|
||||
|
||||
@@ -11,9 +11,12 @@ body {
|
||||
background-color: c.$darkgrey;
|
||||
color: c.$app-color;
|
||||
|
||||
scrollbar-color: rgba(252, 234, 222, 0.3) transparent;
|
||||
scrollbar-width: thin;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
place-items: center;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
code {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
[package]
|
||||
name = "db_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
# types = { package = "monitor_types", path = "../types" }
|
||||
types = { package = "monitor_types", path = "../types" }
|
||||
mungos = "0.3.0"
|
||||
anyhow = "1.0"
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_helpers"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "helpers used as dependency for mogh tech monitor"
|
||||
@@ -9,8 +9,9 @@ license = "GPL-3.0-or-later"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# types = { package = "monitor_types", path = "../types" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
tokio = "1.25"
|
||||
types = { package = "monitor_types", path = "../types" }
|
||||
periphery_client = { path = "../periphery_client" }
|
||||
async_timing_util = "0.1.14"
|
||||
bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
@@ -18,8 +19,10 @@ axum = { version = "0.6", features = ["ws", "json"] }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
toml = "0.6"
|
||||
toml = "0.7"
|
||||
run_command = { version = "0.0.5", features = ["async_tokio"] }
|
||||
rand = "0.8"
|
||||
futures = "0.3"
|
||||
futures-util = "0.3.25"
|
||||
aws-config = "0.54"
|
||||
aws-sdk-ec2 = "0.24"
|
||||
|
||||
199
lib/helpers/src/aws/mod.rs
Normal file
199
lib/helpers/src/aws/mod.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use aws_sdk_ec2::model::{
|
||||
BlockDeviceMapping, EbsBlockDevice, InstanceNetworkInterfaceSpecification, InstanceStateChange,
|
||||
InstanceStateName, InstanceStatus, ResourceType, Tag, TagSpecification,
|
||||
};
|
||||
pub use aws_sdk_ec2::{
|
||||
model::InstanceType,
|
||||
output::{DescribeInstanceStatusOutput, TerminateInstancesOutput},
|
||||
Client, Region,
|
||||
};
|
||||
use types::Server;
|
||||
|
||||
pub async fn create_ec2_client(
|
||||
region: String,
|
||||
access_key_id: &str,
|
||||
secret_access_key: String,
|
||||
) -> Client {
|
||||
// There may be a better way to pass these keys to client
|
||||
std::env::set_var("AWS_ACCESS_KEY_ID", access_key_id);
|
||||
std::env::set_var("AWS_SECRET_ACCESS_KEY", secret_access_key);
|
||||
let region = Region::new(region);
|
||||
let config = aws_config::from_env().region(region).load().await;
|
||||
let client = Client::new(&config);
|
||||
client
|
||||
}
|
||||
|
||||
pub struct Ec2Instance {
|
||||
pub instance_id: String,
|
||||
pub server: Server,
|
||||
}
|
||||
|
||||
const POLL_RATE_SECS: u64 = 2;
|
||||
const MAX_POLL_TRIES: usize = 30;
|
||||
|
||||
/// this will only resolve after the instance is running
|
||||
/// should still poll the periphery agent after creation
|
||||
pub async fn create_instance_with_ami(
|
||||
client: &Client,
|
||||
instance_name: &str,
|
||||
ami_id: &str,
|
||||
instance_type: &str,
|
||||
subnet_id: &str,
|
||||
security_group_ids: Vec<String>,
|
||||
volume_size_gb: i32,
|
||||
key_pair_name: &str,
|
||||
assign_public_ip: bool,
|
||||
) -> anyhow::Result<Ec2Instance> {
|
||||
let instance_type = InstanceType::from(instance_type);
|
||||
if let InstanceType::Unknown(t) = instance_type {
|
||||
return Err(anyhow!("unknown instance type {t:?}"));
|
||||
}
|
||||
let res = client
|
||||
.run_instances()
|
||||
.image_id(ami_id)
|
||||
.instance_type(instance_type)
|
||||
.block_device_mappings(
|
||||
BlockDeviceMapping::builder()
|
||||
.set_device_name(String::from("/dev/sda1").into())
|
||||
.set_ebs(
|
||||
EbsBlockDevice::builder()
|
||||
.volume_size(volume_size_gb)
|
||||
.build()
|
||||
.into(),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.network_interfaces(
|
||||
InstanceNetworkInterfaceSpecification::builder()
|
||||
.subnet_id(subnet_id)
|
||||
.associate_public_ip_address(assign_public_ip)
|
||||
.set_groups(security_group_ids.into())
|
||||
.device_index(0)
|
||||
.build(),
|
||||
)
|
||||
.key_name(key_pair_name)
|
||||
.tag_specifications(
|
||||
TagSpecification::builder()
|
||||
.tags(Tag::builder().key("Name").value(instance_name).build())
|
||||
.resource_type(ResourceType::Instance)
|
||||
.build(),
|
||||
)
|
||||
.min_count(1)
|
||||
.max_count(1)
|
||||
.send()
|
||||
.await
|
||||
.context("failed to start builder ec2 instance")?;
|
||||
let instance = res
|
||||
.instances()
|
||||
.ok_or(anyhow!("got None for created instances"))?
|
||||
.get(0)
|
||||
.ok_or(anyhow!("instances array is empty"))?;
|
||||
let instance_id = instance
|
||||
.instance_id()
|
||||
.ok_or(anyhow!("instance does not have instance_id"))?
|
||||
.to_string();
|
||||
for _ in 0..MAX_POLL_TRIES {
|
||||
let state_name = get_ec2_instance_state_name(&client, &instance_id).await?;
|
||||
if state_name == Some(InstanceStateName::Running) {
|
||||
let ip = if assign_public_ip {
|
||||
get_ec2_instance_public_ip(client, &instance_id).await?
|
||||
} else {
|
||||
instance
|
||||
.private_ip_address()
|
||||
.ok_or(anyhow!("instance does not have private ip"))?
|
||||
.to_string()
|
||||
};
|
||||
let server = Server {
|
||||
address: format!("http://{ip}:8000"),
|
||||
..Default::default()
|
||||
};
|
||||
return Ok(Ec2Instance {
|
||||
instance_id,
|
||||
server,
|
||||
});
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(POLL_RATE_SECS)).await;
|
||||
}
|
||||
Err(anyhow!("instance not running after polling"))
|
||||
}
|
||||
|
||||
pub async fn get_ec2_instance_status(
|
||||
client: &Client,
|
||||
instance_id: &str,
|
||||
) -> anyhow::Result<Option<InstanceStatus>> {
|
||||
let status = client
|
||||
.describe_instance_status()
|
||||
.instance_ids(instance_id)
|
||||
.send()
|
||||
.await
|
||||
.context("failed to get instance status from aws")?
|
||||
.instance_statuses()
|
||||
.ok_or(anyhow!("instance statuses is None"))?
|
||||
.get(0)
|
||||
.map(|s| s.to_owned());
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
pub async fn get_ec2_instance_state_name(
|
||||
client: &Client,
|
||||
instance_id: &str,
|
||||
) -> anyhow::Result<Option<InstanceStateName>> {
|
||||
let status = get_ec2_instance_status(client, instance_id).await?;
|
||||
if status.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
let state = status
|
||||
.unwrap()
|
||||
.instance_state()
|
||||
.ok_or(anyhow!("instance state is None"))?
|
||||
.name()
|
||||
.ok_or(anyhow!("instance state name is None"))?
|
||||
.to_owned();
|
||||
Ok(Some(state))
|
||||
}
|
||||
|
||||
pub async fn get_ec2_instance_public_ip(
|
||||
client: &Client,
|
||||
instance_id: &str,
|
||||
) -> anyhow::Result<String> {
|
||||
let ip = client
|
||||
.describe_instances()
|
||||
.instance_ids(instance_id)
|
||||
.send()
|
||||
.await
|
||||
.context("failed to get instance status from aws")?
|
||||
.reservations()
|
||||
.ok_or(anyhow!("instance reservations is None"))?
|
||||
.get(0)
|
||||
.ok_or(anyhow!("instance reservations is empty"))?
|
||||
.instances()
|
||||
.ok_or(anyhow!("instances is None"))?
|
||||
.get(0)
|
||||
.ok_or(anyhow!("instances is empty"))?
|
||||
.public_ip_address()
|
||||
.ok_or(anyhow!("instance has no public ip"))?
|
||||
.to_string();
|
||||
|
||||
Ok(ip)
|
||||
}
|
||||
|
||||
pub async fn terminate_ec2_instance(
|
||||
client: &Client,
|
||||
instance_id: &str,
|
||||
) -> anyhow::Result<InstanceStateChange> {
|
||||
let res = client
|
||||
.terminate_instances()
|
||||
.instance_ids(instance_id)
|
||||
.send()
|
||||
.await
|
||||
.context("failed to terminate instance from aws")?
|
||||
.terminating_instances()
|
||||
.ok_or(anyhow!("terminating instances is None"))?
|
||||
.get(0)
|
||||
.ok_or(anyhow!("terminating instances is empty"))?
|
||||
.to_owned();
|
||||
Ok(res)
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use std::path::PathBuf;
|
||||
use anyhow::{anyhow, Context};
|
||||
use types::{Build, DockerBuildArgs, EnvironmentVar, Log, Version};
|
||||
|
||||
use crate::{all_logs_success, git, run_monitor_command, to_monitor_name};
|
||||
use crate::{run_monitor_command, to_monitor_name};
|
||||
|
||||
use super::docker_login;
|
||||
|
||||
@@ -17,9 +17,7 @@ pub async fn build(
|
||||
name,
|
||||
version,
|
||||
docker_build_args,
|
||||
branch,
|
||||
docker_account,
|
||||
pre_build,
|
||||
..
|
||||
}: &Build,
|
||||
mut repo_dir: PathBuf,
|
||||
@@ -38,25 +36,25 @@ pub async fn build(
|
||||
.await
|
||||
.context("failed to login to docker")?;
|
||||
repo_dir.push(&name);
|
||||
let pull_logs = git::pull(repo_dir.clone(), branch, &None).await;
|
||||
if !all_logs_success(&pull_logs) {
|
||||
logs.extend(pull_logs);
|
||||
return Ok(logs);
|
||||
}
|
||||
logs.extend(pull_logs);
|
||||
if let Some(command) = pre_build {
|
||||
let dir = repo_dir.join(&command.path);
|
||||
let pre_build_log = run_monitor_command(
|
||||
"pre build",
|
||||
format!("cd {} && {}", dir.display(), command.command),
|
||||
)
|
||||
.await;
|
||||
if !pre_build_log.success {
|
||||
logs.push(pre_build_log);
|
||||
return Ok(logs);
|
||||
}
|
||||
logs.push(pre_build_log);
|
||||
}
|
||||
// let pull_logs = git::pull(repo_dir.clone(), branch, &None).await;
|
||||
// if !all_logs_success(&pull_logs) {
|
||||
// logs.extend(pull_logs);
|
||||
// return Ok(logs);
|
||||
// }
|
||||
// logs.extend(pull_logs);
|
||||
// if let Some(command) = pre_build {
|
||||
// let dir = repo_dir.join(&command.path);
|
||||
// let pre_build_log = run_monitor_command(
|
||||
// "pre build",
|
||||
// format!("cd {} && {}", dir.display(), command.command),
|
||||
// )
|
||||
// .await;
|
||||
// if !pre_build_log.success {
|
||||
// logs.push(pre_build_log);
|
||||
// return Ok(logs);
|
||||
// }
|
||||
// logs.push(pre_build_log);
|
||||
// }
|
||||
let build_dir = repo_dir.join(build_path);
|
||||
let dockerfile_path = match dockerfile_path {
|
||||
Some(dockerfile_path) => dockerfile_path.to_owned(),
|
||||
|
||||
@@ -2,47 +2,10 @@ use std::path::PathBuf;
|
||||
|
||||
use ::run_command::async_run_command;
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use types::{monitor_timestamp, Build, Command, Deployment, GithubToken, GithubUsername, Log};
|
||||
use types::{monitor_timestamp, CloneArgs, Command, GithubToken, Log};
|
||||
|
||||
use crate::{run_monitor_command, to_monitor_name};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct CloneArgs {
|
||||
name: String,
|
||||
repo: Option<String>,
|
||||
branch: Option<String>,
|
||||
on_clone: Option<Command>,
|
||||
on_pull: Option<Command>,
|
||||
pub github_account: Option<GithubUsername>,
|
||||
}
|
||||
|
||||
impl From<&Deployment> for CloneArgs {
|
||||
fn from(d: &Deployment) -> Self {
|
||||
CloneArgs {
|
||||
name: d.name.clone(),
|
||||
repo: d.repo.clone(),
|
||||
branch: d.branch.clone(),
|
||||
on_clone: d.on_clone.clone(),
|
||||
on_pull: d.on_pull.clone(),
|
||||
github_account: d.github_account.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Build> for CloneArgs {
|
||||
fn from(b: &Build) -> Self {
|
||||
CloneArgs {
|
||||
name: b.name.clone(),
|
||||
repo: b.repo.clone(),
|
||||
branch: b.branch.clone(),
|
||||
on_clone: b.on_clone.clone(),
|
||||
on_pull: None,
|
||||
github_account: b.github_account.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pull(
|
||||
mut path: PathBuf,
|
||||
branch: &Option<String>,
|
||||
|
||||
@@ -7,6 +7,7 @@ use run_command::{async_run_command, CommandOutput};
|
||||
use serde::de::DeserializeOwned;
|
||||
use types::{monitor_timestamp, Log};
|
||||
|
||||
pub mod aws;
|
||||
pub mod docker;
|
||||
pub mod git;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "a client to interact with the monitor system"
|
||||
@@ -9,14 +9,15 @@ 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.1.11"
|
||||
monitor_types = "0.1.16"
|
||||
# monitor_types = { path = "../types" }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio = { version = "1.24", features = ["full"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
tokio-util = "0.7"
|
||||
anyhow = "1.0"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
futures-util = "0.3"
|
||||
futures-util = "0.3"
|
||||
envy = "0.4"
|
||||
@@ -88,9 +88,9 @@ impl MonitorClient {
|
||||
.context(format!("failed at building build {build_id}"))
|
||||
}
|
||||
|
||||
pub async fn reclone_build(&self, id: &str) -> anyhow::Result<Update> {
|
||||
self.post::<(), _>(&format!("/api/build/{id}/reclone"), None)
|
||||
.await
|
||||
.context(format!("failed at recloning build {id}"))
|
||||
}
|
||||
// pub async fn reclone_build(&self, id: &str) -> anyhow::Result<Update> {
|
||||
// self.post::<(), _>(&format!("/api/build/{id}/reclone"), None)
|
||||
// .await
|
||||
// .context(format!("failed at recloning build {id}"))
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use monitor_types::User;
|
||||
use reqwest::StatusCode;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
pub use futures_util;
|
||||
@@ -20,6 +20,15 @@ mod secret;
|
||||
mod server;
|
||||
mod update;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MonitorEnv {
|
||||
monitor_url: String,
|
||||
monitor_token: Option<String>,
|
||||
monitor_username: Option<String>,
|
||||
monitor_password: Option<String>,
|
||||
monitor_secret: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorClient {
|
||||
http_client: reqwest::Client,
|
||||
@@ -80,6 +89,26 @@ impl MonitorClient {
|
||||
Ok(client)
|
||||
}
|
||||
|
||||
pub async fn new_from_env() -> anyhow::Result<MonitorClient> {
|
||||
let env = envy::from_env::<MonitorEnv>()
|
||||
.context("failed to parse environment for monitor client")?;
|
||||
if let Some(token) = env.monitor_token {
|
||||
Ok(MonitorClient::new_with_token(&env.monitor_url, token))
|
||||
} else if let Some(password) = env.monitor_password {
|
||||
let username = env.monitor_username.ok_or(anyhow!(
|
||||
"must provide MONITOR_USERNAME to authenticate with MONITOR_PASSWORD"
|
||||
))?;
|
||||
MonitorClient::new_with_password(&env.monitor_url, username, password).await
|
||||
} else if let Some(secret) = env.monitor_secret {
|
||||
let username = env.monitor_username.ok_or(anyhow!(
|
||||
"must provide MONITOR_USERNAME to authenticate with MONITOR_SECRET"
|
||||
))?;
|
||||
MonitorClient::new_with_secret(&env.monitor_url, username, secret).await
|
||||
} else {
|
||||
Err(anyhow!("failed to initialize monitor client from env | must provide one of: (MONITOR_TOKEN), (MONITOR_USERNAME and MONITOR_PASSWORD), (MONITOR_USERNAME and MONITOR_SECRET)"))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_user(
|
||||
&self,
|
||||
username: impl Into<String>,
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
[package]
|
||||
name = "periphery_client"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
helpers = { package = "monitor_helpers", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
# types = { package = "monitor_types", path = "../types" }
|
||||
# helpers = { package = "monitor_helpers", path = "../helpers" }
|
||||
types = { package = "monitor_types", path = "../types" }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
tokio = "1.24"
|
||||
tokio = "1.25"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use anyhow::Context;
|
||||
use helpers::git::CloneArgs;
|
||||
use serde_json::json;
|
||||
use types::{Command, Log, Server};
|
||||
use types::{CloneArgs, Command, Log, Server};
|
||||
|
||||
use crate::PeripheryClient;
|
||||
|
||||
|
||||
@@ -14,12 +14,19 @@ mod git;
|
||||
mod image;
|
||||
mod network;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PeripheryClient {
|
||||
http_client: reqwest::Client,
|
||||
passkey: String,
|
||||
}
|
||||
|
||||
impl PeripheryClient {
|
||||
pub fn new(passkey: String) -> PeripheryClient {
|
||||
PeripheryClient {
|
||||
http_client: Default::default(),
|
||||
passkey,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn health_check(&self, server: &Server) -> anyhow::Result<String> {
|
||||
self.get_text(server, "/health", 1000)
|
||||
.await
|
||||
@@ -95,7 +102,8 @@ impl PeripheryClient {
|
||||
) -> anyhow::Result<String> {
|
||||
let mut req = self
|
||||
.http_client
|
||||
.get(format!("{}{endpoint}", server.address));
|
||||
.get(format!("{}{endpoint}", server.address))
|
||||
.header("authorization", &self.passkey);
|
||||
|
||||
if let Some(timeout) = timeout_ms.into() {
|
||||
req = req.timeout(Duration::from_millis(timeout))
|
||||
@@ -130,6 +138,7 @@ impl PeripheryClient {
|
||||
let res = self
|
||||
.http_client
|
||||
.get(format!("{}{endpoint}", server.address))
|
||||
.header("authorization", &self.passkey)
|
||||
.send()
|
||||
.await
|
||||
.context(format!(
|
||||
@@ -165,6 +174,7 @@ impl PeripheryClient {
|
||||
let res = self
|
||||
.http_client
|
||||
.post(format!("{}{endpoint}", server.address))
|
||||
.header("authorization", &self.passkey)
|
||||
.json(body)
|
||||
.send()
|
||||
.await
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_types"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "types for the mogh tech monitor"
|
||||
|
||||
@@ -32,31 +32,40 @@ pub struct Build {
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub server_id: String, // server which this image should be built on
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub server_id: Option<String>, // server which this image should be built on
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub aws_config: Option<AwsBuilderConfig>,
|
||||
|
||||
#[builder(default)]
|
||||
pub version: Version,
|
||||
|
||||
// git related
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub branch: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub github_account: Option<String>,
|
||||
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_clone: Option<Command>,
|
||||
|
||||
// build related
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub pre_build: Option<Command>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_build_args: Option<DockerBuildArgs>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_account: Option<String>,
|
||||
|
||||
@@ -128,8 +137,10 @@ impl Version {
|
||||
#[diff(attr(#[derive(Debug, Serialize, PartialEq)]))]
|
||||
pub struct DockerBuildArgs {
|
||||
pub build_path: String,
|
||||
#[builder(default)]
|
||||
pub dockerfile_path: Option<String>,
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub build_args: Vec<EnvironmentVar>,
|
||||
}
|
||||
|
||||
@@ -139,3 +150,40 @@ pub struct BuildVersionsReponse {
|
||||
pub version: Version,
|
||||
pub ts: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, Diff, Builder)]
|
||||
#[diff(attr(#[derive(Debug, Serialize, PartialEq)]))]
|
||||
pub struct AwsBuilderConfig {
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub region: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub instance_type: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub ami_id: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub volume_gb: Option<i32>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub subnet_id: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub security_group_ids: Option<Vec<String>>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub key_pair_name: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub assign_public_ip: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ pub struct CoreConfig {
|
||||
#[serde(default = "default_core_port")]
|
||||
pub port: u16,
|
||||
|
||||
// jwt config
|
||||
pub jwt_secret: String,
|
||||
|
||||
#[serde(default = "default_jwt_valid_for")]
|
||||
pub jwt_valid_for: Timelength,
|
||||
|
||||
@@ -42,20 +42,25 @@ pub struct CoreConfig {
|
||||
// used to verify validity from github webhooks
|
||||
pub github_webhook_secret: String,
|
||||
|
||||
// sent in auth header with req to periphery
|
||||
pub passkey: String,
|
||||
|
||||
// integration with slack app
|
||||
pub slack_url: Option<String>,
|
||||
|
||||
// enable login with local auth
|
||||
pub local_auth: bool,
|
||||
|
||||
// github integration
|
||||
pub mongo: MongoConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub github_oauth: OauthCredentials,
|
||||
|
||||
// google integration
|
||||
#[serde(default)]
|
||||
pub google_oauth: OauthCredentials,
|
||||
|
||||
// mongo config
|
||||
pub mongo: MongoConfig,
|
||||
#[serde(default)]
|
||||
pub aws: AwsBuilderConfig,
|
||||
}
|
||||
|
||||
fn default_core_port() -> u16 {
|
||||
@@ -93,6 +98,37 @@ fn default_core_mongo_db_name() -> String {
|
||||
"monitor".to_string()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AwsBuilderConfig {
|
||||
pub access_key_id: String,
|
||||
pub secret_access_key: String,
|
||||
pub default_ami_id: String,
|
||||
pub default_subnet_id: String,
|
||||
pub default_key_pair_name: String,
|
||||
#[serde(default = "default_aws_region")]
|
||||
pub default_region: String,
|
||||
#[serde(default = "default_volume_gb")]
|
||||
pub default_volume_gb: i32,
|
||||
#[serde(default = "default_instance_type")]
|
||||
pub default_instance_type: String,
|
||||
#[serde(default)]
|
||||
pub default_security_group_ids: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub default_assign_public_ip: bool,
|
||||
}
|
||||
|
||||
fn default_aws_region() -> String {
|
||||
String::from("us-east-1")
|
||||
}
|
||||
|
||||
fn default_volume_gb() -> i32 {
|
||||
8
|
||||
}
|
||||
|
||||
fn default_instance_type() -> String {
|
||||
String::from("m5.2xlarge")
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct PeripheryConfig {
|
||||
#[serde(default = "default_periphery_port")]
|
||||
@@ -104,6 +140,8 @@ pub struct PeripheryConfig {
|
||||
#[serde(default)]
|
||||
pub allowed_ips: Vec<IpAddr>,
|
||||
#[serde(default)]
|
||||
pub passkeys: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub secrets: SecretsMap,
|
||||
#[serde(default)]
|
||||
pub github_accounts: GithubAccounts,
|
||||
|
||||
@@ -32,31 +32,40 @@ pub struct Deployment {
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "docker_run_args_diff_no_change")]))]
|
||||
pub docker_run_args: DockerRunArgs,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub build_id: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub build_version: Option<Version>,
|
||||
|
||||
// deployment repo related
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub branch: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub github_account: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_clone: Option<Command>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub on_pull: Option<Command>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub repo_mount: Option<Conversion>,
|
||||
|
||||
@@ -94,39 +103,49 @@ pub struct DeploymentActionState {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Diff, Builder)]
|
||||
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||
pub struct DockerRunArgs {
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub image: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub ports: Vec<Conversion>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub volumes: Vec<Conversion>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub environment: Vec<EnvironmentVar>,
|
||||
|
||||
#[serde(default = "default_network")]
|
||||
#[builder(default = "default_network()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub network: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "restart_mode_diff_no_change")]))]
|
||||
pub restart: RestartMode,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub post_image: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub container_user: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub extra_args: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_account: Option<String>, // the username of the dockerhub account
|
||||
}
|
||||
|
||||
@@ -28,18 +28,23 @@ pub struct Group {
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub builds: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub deployments: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub servers: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub procedures: Vec<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub groups: Vec<String>,
|
||||
|
||||
|
||||
@@ -38,6 +38,16 @@ pub const GITHUB_WEBHOOK_USER_ID: &str = "github";
|
||||
#[typeshare]
|
||||
pub type PermissionsMap = HashMap<String, PermissionLevel>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct CloneArgs {
|
||||
pub name: String,
|
||||
pub repo: Option<String>,
|
||||
pub branch: Option<String>,
|
||||
pub on_clone: Option<Command>,
|
||||
pub on_pull: Option<Command>,
|
||||
pub github_account: Option<GithubUsername>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Diff)]
|
||||
#[diff(attr(#[derive(Debug, PartialEq, Serialize)]))]
|
||||
@@ -96,7 +106,6 @@ pub enum Operation {
|
||||
UpdateBuild,
|
||||
DeleteBuild,
|
||||
BuildBuild,
|
||||
RecloneBuild,
|
||||
|
||||
// deployment
|
||||
CreateDeployment,
|
||||
|
||||
@@ -24,10 +24,12 @@ pub struct Procedure {
|
||||
pub name: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub stages: Vec<ProcedureStage>,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub webhook_branches: Vec<String>,
|
||||
|
||||
@@ -72,7 +74,6 @@ pub enum ProcedureOperation {
|
||||
|
||||
// build
|
||||
BuildBuild,
|
||||
RecloneBuild,
|
||||
|
||||
// deployment
|
||||
DeployContainer,
|
||||
|
||||
@@ -34,36 +34,45 @@ pub struct Server {
|
||||
#[builder(setter(skip))]
|
||||
pub permissions: PermissionsMap,
|
||||
|
||||
#[builder(default = "true")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub enabled: bool,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "vec_diff_no_change")]))]
|
||||
pub to_notify: Vec<String>, // slack users to notify
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default = "true")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub auto_prune: bool,
|
||||
|
||||
#[serde(default = "default_cpu_alert")]
|
||||
#[builder(default = "default_cpu_alert()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "f32_diff_no_change")]))]
|
||||
pub cpu_alert: f32,
|
||||
|
||||
#[serde(default = "default_mem_alert")]
|
||||
#[builder(default = "default_mem_alert()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "f64_diff_no_change")]))]
|
||||
pub mem_alert: f64,
|
||||
|
||||
#[serde(default = "default_disk_alert")]
|
||||
#[builder(default = "default_disk_alert()")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "f64_diff_no_change")]))]
|
||||
pub disk_alert: f64,
|
||||
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "timelength_diff_no_change")]))]
|
||||
pub stats_interval: Timelength,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub region: Option<String>,
|
||||
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub instance_id: Option<String>,
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
Build, BuildActionState, Deployment, DeploymentActionState, Group, PermissionLevel,
|
||||
Build, BuildActionState, CloneArgs, Deployment, DeploymentActionState, Group, PermissionLevel,
|
||||
PermissionsMap, Procedure, Server, ServerActionState,
|
||||
};
|
||||
|
||||
@@ -68,3 +68,29 @@ impl Busy for BuildActionState {
|
||||
self.building || self.recloning || self.updating
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Deployment> for CloneArgs {
|
||||
fn from(d: &Deployment) -> Self {
|
||||
CloneArgs {
|
||||
name: d.name.clone(),
|
||||
repo: d.repo.clone(),
|
||||
branch: d.branch.clone(),
|
||||
on_clone: d.on_clone.clone(),
|
||||
on_pull: d.on_pull.clone(),
|
||||
github_account: d.github_account.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Build> for CloneArgs {
|
||||
fn from(b: &Build) -> Self {
|
||||
CloneArgs {
|
||||
name: b.name.clone(),
|
||||
repo: b.repo.clone(),
|
||||
branch: b.branch.clone(),
|
||||
on_clone: b.pre_build.clone(),
|
||||
on_pull: None,
|
||||
github_account: b.github_account.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,15 +21,22 @@ pub struct User {
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub username: String,
|
||||
|
||||
#[serde(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub enabled: bool,
|
||||
|
||||
#[serde(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub admin: bool,
|
||||
|
||||
#[serde(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub create_server_permissions: bool,
|
||||
|
||||
#[serde(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "Option::is_none")]))]
|
||||
pub create_build_permissions: bool,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub avatar: Option<String>,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_periphery"
|
||||
version = "0.1.11"
|
||||
version = "0.1.16"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor periphery binary | run monitor periphery as system daemon"
|
||||
@@ -13,13 +13,11 @@ path = "src/main.rs"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# helpers = { package = "monitor_helpers", path = "../lib/helpers" }
|
||||
# types = { package = "monitor_types", path = "../lib/types" }
|
||||
helpers = { package = "monitor_helpers", version = "0.1.11" }
|
||||
types = { package = "monitor_types", version = "0.1.11" }
|
||||
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.24", features = ["full"] }
|
||||
tokio = { version = "1.25", features = ["full"] }
|
||||
axum = { version = "0.6", features = ["ws"] }
|
||||
tower = { version = "0.4", features = ["full"] }
|
||||
dotenv = "0.15"
|
||||
@@ -30,7 +28,7 @@ bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
envy = "0.4"
|
||||
sysinfo = "0.27.7"
|
||||
toml = "0.6"
|
||||
toml = "0.7"
|
||||
daemonize = "0.4"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
futures-util = "0.3"
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use axum::{routing::post, Extension, Json, Router};
|
||||
use helpers::{
|
||||
git::{self, CloneArgs},
|
||||
handle_anyhow_error, to_monitor_name,
|
||||
};
|
||||
use helpers::{git, handle_anyhow_error, to_monitor_name};
|
||||
use serde::Deserialize;
|
||||
use types::{Command, Log};
|
||||
use types::{CloneArgs, Command, Log};
|
||||
|
||||
use crate::{helpers::get_github_token, PeripheryConfigExtension};
|
||||
|
||||
|
||||
@@ -43,7 +43,8 @@ pub fn router(config: PeripheryConfigExtension, home_dir: HomeDirExtension) -> R
|
||||
.nest("/build", build::router())
|
||||
.nest("/image", image::router())
|
||||
.layer(DockerClient::extension())
|
||||
.layer(middleware::from_fn(guard_request))
|
||||
.layer(middleware::from_fn(guard_request_by_ip))
|
||||
.layer(middleware::from_fn(guard_request_by_passkey))
|
||||
.layer(StatsClient::extension(
|
||||
config.stats_polling_rate.to_string().parse().unwrap(),
|
||||
))
|
||||
@@ -51,7 +52,59 @@ pub fn router(config: PeripheryConfigExtension, home_dir: HomeDirExtension) -> R
|
||||
.layer(home_dir)
|
||||
}
|
||||
|
||||
async fn guard_request(
|
||||
async fn guard_request_by_passkey(
|
||||
req: Request<Body>,
|
||||
next: Next<Body>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
let config = req.extensions().get::<Arc<PeripheryConfig>>().ok_or((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"could not get periphery config".to_string(),
|
||||
))?;
|
||||
if config.passkeys.is_empty() {
|
||||
return Ok(next.run(req).await);
|
||||
}
|
||||
let req_passkey = req.headers().get("authorization");
|
||||
if req_passkey.is_none() {
|
||||
return Err((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
format!("request was not sent with passkey"),
|
||||
));
|
||||
}
|
||||
let req_passkey = req_passkey
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.map_err(|e| {
|
||||
(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
format!("failed to get passkey from authorization header as str: {e:?}"),
|
||||
)
|
||||
})?
|
||||
.to_string();
|
||||
if config.passkeys.contains(&req_passkey) {
|
||||
Ok(next.run(req).await)
|
||||
} else {
|
||||
let ConnectInfo(socket_addr) =
|
||||
req.extensions().get::<ConnectInfo<SocketAddr>>().ok_or((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"could not get socket addr of request".to_string(),
|
||||
))?;
|
||||
let ip = socket_addr.ip();
|
||||
let method = req.method().to_owned();
|
||||
let uri = req.uri().to_owned();
|
||||
let body = req
|
||||
.extract::<Json<Value>, _>()
|
||||
.await
|
||||
.ok()
|
||||
.map(|Json(body)| body);
|
||||
eprintln!(
|
||||
"{} | unauthorized request from {ip} (bad passkey) | method: {method} | uri: {uri} | body: {body:?}",
|
||||
monitor_timestamp(),
|
||||
);
|
||||
Err((StatusCode::UNAUTHORIZED, format!("request passkey invalid")))
|
||||
}
|
||||
}
|
||||
|
||||
async fn guard_request_by_ip(
|
||||
req: Request<Body>,
|
||||
next: Next<Body>,
|
||||
) -> Result<Response, (StatusCode, String)> {
|
||||
@@ -78,9 +131,8 @@ async fn guard_request(
|
||||
.ok()
|
||||
.map(|Json(body)| body);
|
||||
eprintln!(
|
||||
"{} | unauthorized request from {ip} | method: {method} | uri: {uri} | body: {:?}",
|
||||
monitor_timestamp(),
|
||||
body
|
||||
"{} | unauthorized request from {ip} | method: {method} | uri: {uri} | body: {body:?}",
|
||||
monitor_timestamp()
|
||||
);
|
||||
Err((
|
||||
StatusCode::UNAUTHORIZED,
|
||||
|
||||
11
readme.md
Normal file
11
readme.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# monitor 🦎
|
||||
|
||||
a tool to build and deploy software across many servers
|
||||
|
||||
## table of contents
|
||||
|
||||
1. [introduction](https://github.com/mbecker20/monitor/blob/main/docs/introduction.md)
|
||||
2. [connecting servers](https://github.com/mbecker20/monitor/blob/main/docs/servers.md)
|
||||
3. [building](https://github.com/mbecker20/monitor/blob/main/docs/builds.md)
|
||||
4. [deploying](https://github.com/mbecker20/monitor/blob/main/docs/deployments.md)
|
||||
5. [permissioning](https://github.com/mbecker20/monitor/blob/main/docs/permissions.md)
|
||||
@@ -30,11 +30,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
// container.container
|
||||
// );
|
||||
|
||||
let update = test_build(&monitor).await?;
|
||||
println!("build update:\n{update:#?}");
|
||||
// let update = test_build(&monitor).await?;
|
||||
// println!("build update:\n{update:#?}");
|
||||
|
||||
// test_updates(&monitor).await.unwrap();
|
||||
|
||||
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");
|
||||
|
||||
@@ -3,8 +3,9 @@ use monitor_client::{
|
||||
futures_util::StreamExt,
|
||||
tokio_tungstenite::tungstenite::Message,
|
||||
types::{
|
||||
Build, Command, Conversion, Deployment, DeploymentWithContainerState, DockerBuildArgs,
|
||||
Server, SystemStats, Update,
|
||||
AwsBuilderConfigBuilder, Build, BuildBuilder, Command, Conversion, Deployment,
|
||||
DeploymentWithContainerState, DockerBuildArgs, DockerBuildArgsBuilder, Server, SystemStats,
|
||||
Update,
|
||||
},
|
||||
MonitorClient,
|
||||
};
|
||||
@@ -101,13 +102,9 @@ pub async fn test_build(monitor: &MonitorClient) -> anyhow::Result<Update> {
|
||||
println!("created build. updating...");
|
||||
build.repo = Some("mbecker20/monitor".to_string());
|
||||
// build.branch = Some("");
|
||||
build.on_clone = Some(Command {
|
||||
path: ".".to_string(),
|
||||
command: "yarn".to_string(),
|
||||
});
|
||||
build.pre_build = Some(Command {
|
||||
path: "periphery".to_string(),
|
||||
command: "yarn build".to_string(),
|
||||
path: ".".to_string(),
|
||||
command: "yarn && cd periphery && yarn build".to_string(),
|
||||
});
|
||||
build.docker_build_args = Some(DockerBuildArgs {
|
||||
build_path: "periphery".to_string(),
|
||||
@@ -129,3 +126,31 @@ pub async fn test_updates(monitor: &MonitorClient) -> anyhow::Result<()> {
|
||||
println!("{build_updates:#?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn test_aws_build(monitor: &MonitorClient) -> anyhow::Result<()> {
|
||||
let build = BuildBuilder::default()
|
||||
.name("test_tram".to_string())
|
||||
.repo(Some(String::from("SocialSocialTrading/master")))
|
||||
.branch(Some(String::from("master")))
|
||||
.docker_build_args(
|
||||
DockerBuildArgsBuilder::default()
|
||||
.build_path("backend".to_string())
|
||||
.dockerfile_path("Dockerfile".to_string().into())
|
||||
.build()
|
||||
.context("failed to construct DockerBuildArgs struct")?
|
||||
.into(),
|
||||
)
|
||||
.aws_config(
|
||||
AwsBuilderConfigBuilder::default()
|
||||
.build()
|
||||
.context("failed to construct AwsBuilderConfig struct")?
|
||||
.into(),
|
||||
)
|
||||
.build()
|
||||
.context("failed to construct Build struct")?;
|
||||
let build = monitor.create_full_build(&build).await?;
|
||||
println!("{build:#?}\n");
|
||||
let update = monitor.build(&build.id).await?;
|
||||
println!("{update:#?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user