forked from github-starred/komodo
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1374c26cd8 | ||
|
|
5467b40b2e | ||
|
|
165b9012da | ||
|
|
22630f665e | ||
|
|
3d867084ba | ||
|
|
171dd2d9e0 | ||
|
|
9709239f88 | ||
|
|
60d457b285 | ||
|
|
8b1d4793a7 | ||
|
|
f2166c8435 | ||
|
|
07d723a748 | ||
|
|
b36f485287 | ||
|
|
a121ae0828 | ||
|
|
e2b5a02008 | ||
|
|
575aa62625 | ||
|
|
ac88a2c4ed | ||
|
|
f1dcb71a8a | ||
|
|
30d04bc201 | ||
|
|
33a00bb1a2 | ||
|
|
ccca44ea89 |
17
.vscode/tasks.json
vendored
17
.vscode/tasks.json
vendored
@@ -92,15 +92,6 @@
|
||||
"cwd": "${workspaceFolder}/lib/types"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "publish",
|
||||
"args": ["--allow-dirty"],
|
||||
"label": "publish monitor helpers",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/lib/helpers"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "publish",
|
||||
@@ -109,14 +100,6 @@
|
||||
"cwd": "${workspaceFolder}/lib/monitor_client"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "publish",
|
||||
"label": "publish monitor periphery",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/periphery"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "publish",
|
||||
|
||||
530
Cargo.lock
generated
530
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"
|
||||
@@ -180,6 +505,15 @@ version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "base64-simd"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5"
|
||||
dependencies = [
|
||||
"simd-abstraction",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.14.0"
|
||||
@@ -299,6 +633,16 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
|
||||
[[package]]
|
||||
name = "bytes-utils"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
@@ -399,7 +743,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -415,8 +759,8 @@ dependencies = [
|
||||
"hex",
|
||||
"hmac",
|
||||
"jwt",
|
||||
"monitor_helpers 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.2.0",
|
||||
"mungos",
|
||||
"periphery_client",
|
||||
"serde",
|
||||
@@ -651,10 +995,10 @@ checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||
|
||||
[[package]]
|
||||
name = "db_client"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.2.0",
|
||||
"mungos",
|
||||
]
|
||||
|
||||
@@ -1104,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"
|
||||
@@ -1454,7 +1813,7 @@ dependencies = [
|
||||
"rand",
|
||||
"rustc_version_runtime",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"rustls-pemfile 0.3.0",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"serde_with",
|
||||
@@ -1479,7 +1838,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_cli"
|
||||
version = "0.1.21"
|
||||
version = "0.1.23"
|
||||
dependencies = [
|
||||
"async_timing_util",
|
||||
"clap",
|
||||
@@ -1495,12 +1854,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_client"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1512,47 +1871,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_helpers"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
"aws-config",
|
||||
"aws-sdk-ec2",
|
||||
"axum",
|
||||
"bollard",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.15 (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.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2e58a1c8a1e92c967b5c4ba10511bc4bbf65c824230d234b78ad9d3ed992b75"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
"axum",
|
||||
"bollard",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.2.0",
|
||||
"periphery_client",
|
||||
"rand",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_periphery"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -1563,8 +1905,8 @@ dependencies = [
|
||||
"dotenv",
|
||||
"envy",
|
||||
"futures-util",
|
||||
"monitor_helpers 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_helpers",
|
||||
"monitor_types 0.2.0",
|
||||
"run_command",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -1578,7 +1920,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1595,9 +1937,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_types"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc2fc31230469c5a56e57eacf0200422897d4e0808ba176a767ceb59a872d543"
|
||||
checksum = "62e9379ee43474ebac60b639f9094343b92cc9cbee55d8a447a65cd46305feee"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bollard",
|
||||
@@ -1816,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"
|
||||
@@ -1856,12 +2204,11 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
"monitor_helpers 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"monitor_types 0.2.0",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2022,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"
|
||||
@@ -2108,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]]
|
||||
@@ -2117,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]]
|
||||
@@ -2147,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"
|
||||
@@ -2156,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"
|
||||
@@ -2231,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"
|
||||
@@ -2372,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"
|
||||
@@ -2484,9 +2893,9 @@ checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.27.7"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "975fe381e0ecba475d4acff52466906d95b153a40324956552e027b2a9eaa89e"
|
||||
checksum = "727220a596b4ca0af040a07091e49f5c105ec8f2592674339a5bf35be592f76e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
@@ -2666,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"
|
||||
@@ -2795,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"
|
||||
@@ -3233,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"
|
||||
|
||||
@@ -3,8 +3,8 @@ WORKDIR /builder
|
||||
|
||||
COPY ./core ./core
|
||||
|
||||
# COPY ./lib/types ./lib/types
|
||||
# COPY ./lib/helpers ./lib/helpers
|
||||
COPY ./lib/types ./lib/types
|
||||
COPY ./lib/helpers ./lib/helpers
|
||||
|
||||
COPY ./lib/db_client ./lib/db_client
|
||||
COPY ./lib/periphery_client ./lib/periphery_client
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_cli"
|
||||
version = "0.1.21"
|
||||
version = "0.1.23"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor cli | tools to setup monitor system"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
@@ -41,14 +40,16 @@ pub struct CoreConfig {
|
||||
// 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 {
|
||||
@@ -86,6 +87,60 @@ fn default_core_mongo_db_name() -> String {
|
||||
"monitor".to_string()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AwsBuilderConfig {
|
||||
#[serde(skip_serializing)]
|
||||
pub access_key_id: String,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub secret_access_key: String,
|
||||
|
||||
pub default_ami_id: String,
|
||||
pub default_subnet_id: String,
|
||||
pub default_key_pair_name: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub available_ami_accounts: AvailableAmiAccounts,
|
||||
|
||||
#[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 AvailableAmiAccounts = HashMap<String, AmiAccounts>; // (ami_id, AmiAccounts)
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AmiAccounts {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub github: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub docker: Vec<String>,
|
||||
}
|
||||
|
||||
pub type GithubUsername = String;
|
||||
pub type GithubToken = String;
|
||||
pub type GithubAccounts = HashMap<GithubUsername, GithubToken>;
|
||||
|
||||
@@ -31,6 +31,21 @@ 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
|
||||
|
||||
[aws.available_ami_accounts]
|
||||
your_periphery_ami = { name = "default ami", github = ["github_username"], docker = ["docker_username"] }
|
||||
|
||||
[github_oauth]
|
||||
enabled = true
|
||||
id = "your_github_client_id"
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
[package]
|
||||
name = "core"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
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.15" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
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" }
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
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,
|
||||
@@ -8,11 +14,10 @@ use types::{
|
||||
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,12 @@ 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)
|
||||
@@ -117,11 +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 = match self.periphery.delete_repo(&server, &build.name).await {
|
||||
Ok(log) => log,
|
||||
Err(e) => Log::error("delete repo", format!("{e:#?}")),
|
||||
};
|
||||
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()
|
||||
};
|
||||
@@ -171,16 +158,25 @@ impl State {
|
||||
mut new_build: Build,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Build> {
|
||||
let start_ts = monitor_timestamp();
|
||||
let current_build = self
|
||||
.get_build_check_permissions(&new_build.id, user, PermissionLevel::Update)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
if let Some(new_server_id) = &new_build.server_id {
|
||||
if current_build.server_id.is_none()
|
||||
|| new_server_id != current_build.server_id.as_ref().unwrap()
|
||||
{
|
||||
self.get_server_check_permissions(new_server_id, user, PermissionLevel::Update)
|
||||
.await
|
||||
.context("user does not have permission to attach build to this server")?;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.last_built_at = current_build.last_built_at.clone();
|
||||
new_build.created_at = current_build.created_at.clone();
|
||||
new_build.updated_at = start_ts.clone();
|
||||
|
||||
@@ -192,41 +188,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 +250,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 +261,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, logs) = res.unwrap();
|
||||
update.logs.extend(logs);
|
||||
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 +359,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,73 +382,200 @@ impl State {
|
||||
.push(Log::error("build", "builder busy".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
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.success = all_logs_success(&update.logs);
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
|
||||
self.update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
pub async fn reclone_build(
|
||||
async fn create_ec2_instance_for_build(
|
||||
&self,
|
||||
build_id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<Update> {
|
||||
if self.build_busy(build_id).await {
|
||||
return Err(anyhow!("build busy"));
|
||||
build: &Build,
|
||||
) -> anyhow::Result<(Ec2Instance, Option<aws::Client>, Vec<Log>)> {
|
||||
if build.aws_config.is_none() {
|
||||
return Err(anyhow!("build has no aws_config attached"));
|
||||
}
|
||||
{
|
||||
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(),
|
||||
let start_instance_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 instance_id = &instance.instance_id;
|
||||
let start_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: start_instance_ts,
|
||||
end_ts: monitor_timestamp(),
|
||||
..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
|
||||
let start_connect_ts = monitor_timestamp();
|
||||
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 connect_log = Log {
|
||||
stage: "build instance connected".to_string(),
|
||||
success: true,
|
||||
stdout: "established contact with periphery on builder".to_string(),
|
||||
start_ts: start_connect_ts,
|
||||
end_ts: monitor_timestamp(),
|
||||
..Default::default()
|
||||
};
|
||||
return Ok((instance, Some(aws_client), vec![start_log, connect_log]));
|
||||
}
|
||||
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)
|
||||
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)
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -7,8 +7,8 @@ use axum::{
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{doc, Deserialize, Document, FindOptions, Serialize};
|
||||
use types::{
|
||||
traits::Permissioned, Build, BuildActionState, BuildVersionsReponse, Operation,
|
||||
PermissionLevel, UpdateStatus,
|
||||
traits::Permissioned, AwsBuilderConfig, Build, BuildActionState, BuildVersionsReponse,
|
||||
Operation, PermissionLevel, UpdateStatus,
|
||||
};
|
||||
use typeshare::typeshare;
|
||||
|
||||
@@ -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(
|
||||
@@ -227,6 +208,16 @@ pub fn router() -> Router {
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/aws_builder_defaults",
|
||||
get(|Extension(state): StateExtension| async move {
|
||||
Json(AwsBuilderConfig {
|
||||
access_key_id: String::new(),
|
||||
secret_access_key: String::new(),
|
||||
..state.config.aws.clone()
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
||||
@@ -8,10 +8,11 @@ use axum::{
|
||||
};
|
||||
use futures_util::future::join_all;
|
||||
use helpers::handle_anyhow_error;
|
||||
use mungos::{Deserialize, Document, Serialize};
|
||||
use mungos::{doc, options::FindOneOptions, Deserialize, Document, Serialize};
|
||||
use types::{
|
||||
traits::Permissioned, Deployment, DeploymentActionState, DeploymentWithContainerState,
|
||||
DockerContainerState, DockerContainerStats, Log, PermissionLevel, Server,
|
||||
DockerContainerState, DockerContainerStats, Log, Operation, PermissionLevel, Server,
|
||||
UpdateStatus,
|
||||
};
|
||||
use typeshare::typeshare;
|
||||
|
||||
@@ -297,15 +298,29 @@ pub fn router() -> Router {
|
||||
get(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Path(deployment_id): Path<DeploymentId>| async move {
|
||||
Path(DeploymentId { id })| async move {
|
||||
let stats = state
|
||||
.get_deployment_container_stats(&deployment_id.id, &user)
|
||||
.get_deployment_container_stats(&id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(stats))
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/:id/deployed_version",
|
||||
get(
|
||||
|Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Path(DeploymentId { id })| async move {
|
||||
let version = state
|
||||
.get_deployment_deployed_version(&id, &user)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(version)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -443,4 +458,53 @@ impl State {
|
||||
.await?;
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
async fn get_deployment_deployed_version(
|
||||
&self,
|
||||
id: &str,
|
||||
user: &RequestUser,
|
||||
) -> anyhow::Result<String> {
|
||||
let deployment = self
|
||||
.get_deployment_check_permissions(&id, &user, PermissionLevel::Read)
|
||||
.await?;
|
||||
if deployment.build_id.is_some() {
|
||||
let latest_deploy_update = self
|
||||
.db
|
||||
.updates
|
||||
.find_one(
|
||||
doc! {
|
||||
"target": {
|
||||
"type": "Deployment",
|
||||
"id": id
|
||||
},
|
||||
"operation": Operation::DeployContainer.to_string(),
|
||||
"status": UpdateStatus::Complete.to_string(),
|
||||
"success": true,
|
||||
},
|
||||
FindOneOptions::builder().sort(doc! { "_id": -1 }).build(),
|
||||
)
|
||||
.await
|
||||
.context("failed at query to get latest deploy update from mongo")?;
|
||||
if let Some(update) = latest_deploy_update {
|
||||
if let Some(version) = update.version {
|
||||
Ok(version.to_string())
|
||||
} else {
|
||||
Ok("latest".to_string())
|
||||
}
|
||||
} else {
|
||||
Ok("latest".to_string())
|
||||
}
|
||||
} else {
|
||||
let split = deployment
|
||||
.docker_run_args
|
||||
.image
|
||||
.split(':')
|
||||
.collect::<Vec<&str>>();
|
||||
if let Some(version) = split.get(1) {
|
||||
Ok(version.to_string())
|
||||
} else {
|
||||
Ok("latest".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -33,6 +33,13 @@ struct ModifyUserCreateServerBody {
|
||||
create_server_permissions: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct ModifyUserCreateBuildBody {
|
||||
user_id: String,
|
||||
create_build_permissions: bool,
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
@@ -62,6 +69,15 @@ pub fn router() -> Router {
|
||||
response!(Json(update))
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/modify_create_build",
|
||||
post(|state, user, body| async {
|
||||
let update = modify_user_create_build_permissions(state, user, body)
|
||||
.await
|
||||
.map_err(handle_anyhow_error)?;
|
||||
response!(Json(update))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
async fn update_permissions(
|
||||
@@ -309,3 +325,58 @@ async fn modify_user_create_server_permissions(
|
||||
update.id = state.add_update(update.clone()).await?;
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
async fn modify_user_create_build_permissions(
|
||||
Extension(state): StateExtension,
|
||||
Extension(user): RequestUserExtension,
|
||||
Json(ModifyUserCreateBuildBody {
|
||||
user_id,
|
||||
create_build_permissions,
|
||||
}): Json<ModifyUserCreateBuildBody>,
|
||||
) -> anyhow::Result<Update> {
|
||||
if !user.is_admin {
|
||||
return Err(anyhow!(
|
||||
"user does not have permissions for this action (not admin)"
|
||||
));
|
||||
}
|
||||
let user = state
|
||||
.db
|
||||
.users
|
||||
.find_one_by_id(&user_id)
|
||||
.await
|
||||
.context("failed at mongo query to find target user")?
|
||||
.ok_or(anyhow!("did not find any user with user_id {user_id}"))?;
|
||||
state
|
||||
.db
|
||||
.users
|
||||
.update_one::<Document>(
|
||||
&user_id,
|
||||
mungos::Update::Set(doc! { "create_build_permissions": create_build_permissions }),
|
||||
)
|
||||
.await?;
|
||||
let update_type = if create_build_permissions {
|
||||
"enabled"
|
||||
} else {
|
||||
"disabled"
|
||||
};
|
||||
let ts = monitor_timestamp();
|
||||
let mut update = Update {
|
||||
target: UpdateTarget::System,
|
||||
operation: Operation::ModifyUserCreateBuildPermissions,
|
||||
logs: vec![Log::simple(
|
||||
"modify user create build permissions",
|
||||
format!(
|
||||
"{update_type} create build permissions for {} (id: {})",
|
||||
user.username, user.id
|
||||
),
|
||||
)],
|
||||
start_ts: ts.clone(),
|
||||
end_ts: Some(ts),
|
||||
status: UpdateStatus::Complete,
|
||||
success: true,
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
update.id = state.add_update(update.clone()).await?;
|
||||
Ok(update)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { Component, createSignal } from "solid-js";
|
||||
import { Component, createSignal, Show } from "solid-js";
|
||||
import { client, pushNotification } from "..";
|
||||
import { useAppState } from "../state/StateProvider";
|
||||
import { Build, Deployment } from "../types";
|
||||
@@ -11,6 +11,7 @@ import Input from "./shared/Input";
|
||||
import Flex from "./shared/layout/Flex";
|
||||
import Grid from "./shared/layout/Grid";
|
||||
import CenterMenu from "./shared/menu/CenterMenu";
|
||||
import HoverMenu from "./shared/menu/HoverMenu";
|
||||
import Selector from "./shared/menu/Selector";
|
||||
|
||||
const CopyMenu: Component<{
|
||||
@@ -42,12 +43,11 @@ const CopyMenu: Component<{
|
||||
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(),
|
||||
server_id: selectedId()!,
|
||||
});
|
||||
}
|
||||
toggleShow();
|
||||
@@ -59,45 +59,53 @@ const CopyMenu: Component<{
|
||||
}
|
||||
};
|
||||
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"
|
||||
<HoverMenu
|
||||
target={
|
||||
<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}
|
||||
/>
|
||||
<Show when={p.type === "deployment"}>
|
||||
<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
|
||||
/>
|
||||
</Show>
|
||||
</Flex>
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
style={{ width: "100%" }}
|
||||
onConfirm={copy}
|
||||
>
|
||||
copy {p.type}
|
||||
</ConfirmButton>
|
||||
</Grid>
|
||||
)}
|
||||
position="center"
|
||||
/>
|
||||
}
|
||||
content={`copy ${p.type}`}
|
||||
position="bottom center"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -47,10 +47,10 @@ export const NewDeployment: Component<{ serverID: string }> = (p) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const NewBuild: Component<{ serverID: string }> = (p) => {
|
||||
export const NewBuild: Component<{}> = (p) => {
|
||||
const [showNew, toggleShowNew] = useToggle();
|
||||
const create = (name: string) => {
|
||||
client.create_build({ name, server_id: p.serverID });
|
||||
client.create_build({ name });
|
||||
};
|
||||
return (
|
||||
<Show
|
||||
|
||||
@@ -11,31 +11,29 @@ type State = {
|
||||
|
||||
const context = createContext<State>();
|
||||
|
||||
export const ActionStateProvider: ParentComponent<{}> = (p) => {
|
||||
export const ActionStateProvider: ParentComponent<{ build_id: string }> = (p) => {
|
||||
const { ws } = useAppState();
|
||||
const params = useParams();
|
||||
const [actions, setActions] = createStore<BuildActionState>({
|
||||
building: false,
|
||||
recloning: false,
|
||||
updating: false,
|
||||
});
|
||||
createEffect(() => {
|
||||
client.get_build_action_state(params.id).then(setActions);
|
||||
client.get_build_action_state(p.build_id).then(setActions);
|
||||
});
|
||||
onCleanup(
|
||||
ws.subscribe([Operation.BuildBuild], (update) => {
|
||||
if (update.target.id === params.id) {
|
||||
if (update.target.id === p.build_id) {
|
||||
setActions("building", update.status !== UpdateStatus.Complete);
|
||||
}
|
||||
})
|
||||
);
|
||||
onCleanup(
|
||||
ws.subscribe([Operation.RecloneBuild], (update) => {
|
||||
if (update.target.id === params.id) {
|
||||
setActions("recloning", update.status !== UpdateStatus.Complete);
|
||||
}
|
||||
})
|
||||
);
|
||||
// onCleanup(
|
||||
// ws.subscribe([Operation.RecloneBuild], (update) => {
|
||||
// if (update.target.id === params.id) {
|
||||
// setActions("recloning", update.status !== UpdateStatus.Complete);
|
||||
// }
|
||||
// })
|
||||
// );
|
||||
// onCleanup(
|
||||
// ws.subscribe([DELETE_BUILD], ({ complete, buildID }) => {
|
||||
// if (buildID === selected.id()) {
|
||||
|
||||
@@ -10,21 +10,21 @@ import { useActionStates } from "./ActionStateProvider";
|
||||
import { client } from "../..";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { PermissionLevel, ServerStatus } from "../../types";
|
||||
import { PermissionLevel, ServerStatus, ServerWithStatus } from "../../types";
|
||||
|
||||
const Actions: Component<{}> = (p) => {
|
||||
const { user } = useUser();
|
||||
const params = useParams() as { id: string };
|
||||
const { builds, servers } = useAppState();
|
||||
const build = () => builds.get(params.id)!;
|
||||
const server = () => build() && servers.get(build()!.server_id);
|
||||
const server = () => (build() && build().server_id) ? servers.get(build()!.server_id!) : undefined;
|
||||
const actions = useActionStates();
|
||||
const userCanExecute = () =>
|
||||
user().admin ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Execute ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Update;
|
||||
return (
|
||||
<Show when={userCanExecute() && server()?.status === ServerStatus.Ok}>
|
||||
<Show when={userCanExecute() && (server() ? server()?.status === ServerStatus.Ok : true)}>
|
||||
<Grid class={combineClasses("card shadow")} gridTemplateRows="auto 1fr">
|
||||
<h1>actions</h1>
|
||||
<Grid style={{ height: "fit-content" }}>
|
||||
@@ -48,7 +48,7 @@ const Actions: Component<{}> = (p) => {
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex class={combineClasses("action shadow")}>
|
||||
{/* <Flex class={combineClasses("action shadow")}>
|
||||
reclone{" "}
|
||||
<Show
|
||||
when={!actions.recloning}
|
||||
@@ -67,7 +67,7 @@ const Actions: Component<{}> = (p) => {
|
||||
<Icon type="reset" />
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Flex>
|
||||
</Flex> */}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Show>
|
||||
|
||||
@@ -2,9 +2,7 @@ import { useNavigate, useParams } from "@solidjs/router";
|
||||
import { Component, createEffect, onCleanup, Show } from "solid-js";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import { Operation, PermissionLevel } from "../../types";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { Operation } from "../../types";
|
||||
import NotFound from "../NotFound";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Actions from "./Actions";
|
||||
@@ -35,7 +33,7 @@ const Build: Component<{}> = (p) => {
|
||||
onCleanup(() => unsub);
|
||||
return (
|
||||
<Show when={build()} fallback={<NotFound type="build" />}>
|
||||
<ActionStateProvider>
|
||||
<ActionStateProvider build_id={params.id}>
|
||||
<Grid
|
||||
style={{
|
||||
width: "100%",
|
||||
|
||||
@@ -27,7 +27,8 @@ const Header: Component<{}> = (p) => {
|
||||
const userCanUpdate = () =>
|
||||
user().admin ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Update;
|
||||
const server = () => servers.get(build().server_id);
|
||||
const server = () =>
|
||||
build().server_id ? servers.get(build().server_id!) : undefined;
|
||||
return (
|
||||
<>
|
||||
<Grid
|
||||
@@ -69,13 +70,15 @@ const Header: Component<{}> = (p) => {
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Flex alignItems="center">
|
||||
<A
|
||||
href={`/server/${build().server_id}`}
|
||||
class="text-hover"
|
||||
style={{ opacity: 0.7, padding: 0 }}
|
||||
>
|
||||
{server()?.server.name}
|
||||
</A>
|
||||
<Show when={server()} fallback={<div style={{ opacity: 0.7 }}>{build().aws_config ? "aws build" : ""}</div>}>
|
||||
<A
|
||||
href={`/server/${build().server_id}`}
|
||||
class="text-hover"
|
||||
style={{ opacity: 0.7, padding: 0 }}
|
||||
>
|
||||
{server()?.server.name}
|
||||
</A>
|
||||
</Show>
|
||||
<div style={{ opacity: 0.7 }}>build</div>
|
||||
</Flex>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
|
||||
@@ -45,7 +45,7 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
set(...args);
|
||||
set("updated", true);
|
||||
};
|
||||
const server = () => servers.get(builds.get(params.id)!.server_id);
|
||||
const server = () => build.server_id ? servers.get(build.server_id) : undefined;
|
||||
|
||||
const load = () => {
|
||||
// console.log("load build");
|
||||
@@ -54,11 +54,11 @@ export const ConfigProvider: ParentComponent<{}> = (p) => {
|
||||
...build,
|
||||
repo: build.repo,
|
||||
branch: build.branch,
|
||||
on_clone: build.on_clone,
|
||||
pre_build: build.pre_build,
|
||||
docker_build_args: build.docker_build_args,
|
||||
docker_account: build.docker_account,
|
||||
github_account: build.github_account,
|
||||
aws_config: build.aws_config,
|
||||
loaded: true,
|
||||
updated: false,
|
||||
saving: false,
|
||||
|
||||
@@ -2,12 +2,10 @@ import { useParams } from "@solidjs/router";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { PermissionLevel } from "../../../types";
|
||||
import { getId } from "../../../util/helpers";
|
||||
import SimpleTabs from "../../shared/tabs/SimpleTabs";
|
||||
import { Tab } from "../../shared/tabs/Tabs";
|
||||
import BuildConfig from "./build-config/BuildConfig";
|
||||
import GitConfig from "./git-config/GitConfig";
|
||||
import BuilderConfig from "./builder/BuilderConfig";
|
||||
import BuildConfig from "./config/BuildConfig";
|
||||
import Owners from "./Permissions";
|
||||
import { ConfigProvider } from "./Provider";
|
||||
|
||||
@@ -24,12 +22,12 @@ const BuildTabs: Component<{}> = (p) => {
|
||||
tabs={
|
||||
[
|
||||
{
|
||||
title: "repo",
|
||||
element: () => <GitConfig />,
|
||||
title: "config",
|
||||
element: () => <BuildConfig />,
|
||||
},
|
||||
{
|
||||
title: "build",
|
||||
element: () => <BuildConfig />,
|
||||
title: "builder",
|
||||
element: () => <BuilderConfig />
|
||||
},
|
||||
user().admin && {
|
||||
title: "collaborators",
|
||||
|
||||
116
frontend/src/components/build/tabs/builder/AwsBuilderConfig.tsx
Normal file
116
frontend/src/components/build/tabs/builder/AwsBuilderConfig.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import Input from "../../../shared/Input";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const AwsBuilderConfig: Component<{}> = (p) => {
|
||||
const { build } = useConfig();
|
||||
return (
|
||||
<>
|
||||
<Ami />
|
||||
<InstanceType />
|
||||
<VolumeSize />
|
||||
<Show when={!build.updated}>
|
||||
<div style={{ height: "4rem" }} />
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Ami: Component = () => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const get_ami_id = () => {
|
||||
if (build.aws_config?.ami_id) {
|
||||
return build.aws_config.ami_id;
|
||||
} else {
|
||||
return aws_builder_config()?.default_ami_id || "unknown";
|
||||
}
|
||||
};
|
||||
const get_ami_name = (ami_id: string) => {
|
||||
if (aws_builder_config() === undefined || ami_id === "unknown")
|
||||
return "unknown";
|
||||
return (
|
||||
aws_builder_config()!.available_ami_accounts![ami_id]?.name || "unknown"
|
||||
);
|
||||
};
|
||||
const ami_ids = () => {
|
||||
if (aws_builder_config() === undefined) return [];
|
||||
return Object.keys(aws_builder_config()!.available_ami_accounts!);
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>ami</h1>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={get_ami_id()}
|
||||
items={ami_ids()}
|
||||
onSelect={(ami_id) => setBuild("aws_config", "ami_id", ami_id)}
|
||||
itemMap={get_ami_name}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const InstanceType: Component = () => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>instance type</h1>
|
||||
<Input
|
||||
placeholder={aws_builder_config()?.default_instance_type}
|
||||
value={build.aws_config?.instance_type}
|
||||
onEdit={(instance_type) =>
|
||||
setBuild("aws_config", "instance_type", instance_type)
|
||||
}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const VolumeSize: Component = () => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>volume size</h1>
|
||||
<Flex gap="0.25rem" alignItems="center">
|
||||
<Input
|
||||
style={{ width: "4rem" }}
|
||||
placeholder={aws_builder_config()?.default_volume_gb?.toString()}
|
||||
value={
|
||||
build.aws_config?.volume_gb
|
||||
? build.aws_config.volume_gb.toString()
|
||||
: ""
|
||||
}
|
||||
onEdit={(volume_size) =>
|
||||
setBuild("aws_config", "volume_gb", Number(volume_size))
|
||||
}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
GB
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default AwsBuilderConfig;
|
||||
@@ -1,43 +1,27 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { pushNotification, URL } from "../../../..";
|
||||
import { combineClasses, copyToClipboard, 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";
|
||||
import Loading from "../../../shared/loading/Loading";
|
||||
import { useConfig } from "../Provider";
|
||||
import Git from "./Git";
|
||||
import OnClone from "./OnClone";
|
||||
import Loading from "../../../shared/loading/Loading";
|
||||
import BuilderType from "./BuilderType";
|
||||
import BuilderServer from "./BuilderServer";
|
||||
import AwsBuilderConfig from "./AwsBuilderConfig";
|
||||
|
||||
const GitConfig: Component<{}> = (p) => {
|
||||
const BuilderConfig: Component<{}> = (p) => {
|
||||
const { build, reset, save, userCanUpdate } = useConfig();
|
||||
const listenerUrl = () => `${URL}/api/listener/build/${getId(build)}`;
|
||||
return (
|
||||
<Show when={build.loaded}>
|
||||
<Grid class="config">
|
||||
<Grid class="config-items scroller">
|
||||
<Git />
|
||||
<OnClone />
|
||||
<Show when={userCanUpdate()}>
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>webhook url</h1>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<div class="ellipsis" style={{ width: "250px" }}>
|
||||
{listenerUrl()}
|
||||
</div>
|
||||
<ConfirmButton
|
||||
class="blue"
|
||||
onFirstClick={() => {
|
||||
copyToClipboard(listenerUrl());
|
||||
pushNotification("good", "copied url to clipboard");
|
||||
}}
|
||||
confirm={<Icon type="check" />}
|
||||
>
|
||||
<Icon type="clipboard" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<BuilderType />
|
||||
<Show when={build.server_id}>
|
||||
<BuilderServer />
|
||||
<div style={{ height: "12rem" }} />
|
||||
</Show>
|
||||
<Show when={build.aws_config}>
|
||||
<AwsBuilderConfig />
|
||||
</Show>
|
||||
</Grid>
|
||||
<Show when={userCanUpdate() && build.updated}>
|
||||
@@ -66,4 +50,4 @@ const GitConfig: Component<{}> = (p) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default GitConfig;
|
||||
export default BuilderConfig;
|
||||
46
frontend/src/components/build/tabs/builder/BuilderServer.tsx
Normal file
46
frontend/src/components/build/tabs/builder/BuilderServer.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Component } from "solid-js";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { PermissionLevel } from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const BuilderServer: Component<{}> = (p) => {
|
||||
const { servers, getPermissionOnServer } = useAppState();
|
||||
const { setBuild, server, userCanUpdate } = useConfig();
|
||||
const availableServers = () => {
|
||||
if (!servers.loaded()) return [];
|
||||
return servers
|
||||
.ids()!
|
||||
.filter((id) => {
|
||||
return getPermissionOnServer(id) === PermissionLevel.Update;
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>builder server</h1>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={server()?.server ? getId(server()!.server) : "select server"}
|
||||
items={availableServers()}
|
||||
onSelect={(server_id) => setBuild("server_id", server_id)}
|
||||
itemMap={(server_id) =>
|
||||
server_id === "select server"
|
||||
? "select server"
|
||||
: servers.get(server_id)!.server.name
|
||||
}
|
||||
disabled={!userCanUpdate()}
|
||||
position="bottom right"
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuilderServer;
|
||||
52
frontend/src/components/build/tabs/builder/BuilderType.tsx
Normal file
52
frontend/src/components/build/tabs/builder/BuilderType.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const BuilderType: Component<{}> = (p) => {
|
||||
const { servers } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const builderType = () => {
|
||||
if (build.server_id) {
|
||||
return "server";
|
||||
} else if (build.aws_config) {
|
||||
return "aws";
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>builder type</h1>
|
||||
<Show when={userCanUpdate()} fallback={<h2>{builderType()}</h2>}>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={builderType() || "select type"}
|
||||
items={["aws", "server"]}
|
||||
position="bottom right"
|
||||
onSelect={(type) => {
|
||||
if (type !== builderType()) {
|
||||
if (type === "server") {
|
||||
const server_id =
|
||||
servers.ids()?.length || 0 > 0
|
||||
? servers.ids()![0]
|
||||
: undefined;
|
||||
setBuild({ server_id, aws_config: undefined });
|
||||
} else if (type === "aws") {
|
||||
setBuild({ server_id: undefined, aws_config: {} });
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Show>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuilderType;
|
||||
@@ -1,12 +1,10 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import {
|
||||
combineClasses,
|
||||
parseDotEnvToEnvVars,
|
||||
parseEnvVarseToDotEnv,
|
||||
} from "../../../../util/helpers";
|
||||
import { useToggle } from "../../../../util/hooks";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import CenterMenu from "../../../shared/menu/CenterMenu";
|
||||
import TextArea from "../../../shared/TextArea";
|
||||
import { useConfig } from "../Provider";
|
||||
@@ -14,24 +12,26 @@ import { useConfig } from "../Provider";
|
||||
const BuildArgs: Component<{}> = (p) => {
|
||||
const { build, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>build args</h1>
|
||||
<Flex alignItems="center" gap="0.2rem">
|
||||
<Show
|
||||
when={
|
||||
!build.docker_build_args?.build_args ||
|
||||
build.docker_build_args.build_args.length === 0
|
||||
}
|
||||
>
|
||||
<div>none</div>
|
||||
</Show>
|
||||
<Show when={userCanUpdate()}>
|
||||
<EditBuildArgs />
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>build args</h1>
|
||||
<Flex alignItems="center" gap="0.2rem">
|
||||
<Show
|
||||
when={
|
||||
!build.docker_build_args?.build_args ||
|
||||
build.docker_build_args.build_args.length === 0
|
||||
}
|
||||
>
|
||||
<div>none</div>
|
||||
</Show>
|
||||
<Show when={userCanUpdate()}>
|
||||
<EditBuildArgs />
|
||||
</Show>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ import { useConfig } from "../Provider";
|
||||
import Loading from "../../../shared/loading/Loading";
|
||||
import BuildArgs from "./BuildArgs";
|
||||
import Version from "./Version";
|
||||
import Repo from "./Repo";
|
||||
import ListenerUrl from "./ListenerUrl";
|
||||
|
||||
const BuildConfig: Component<{}> = (p) => {
|
||||
const { build, reset, save, userCanUpdate } = useConfig();
|
||||
@@ -17,9 +19,11 @@ const BuildConfig: Component<{}> = (p) => {
|
||||
<Grid class="config">
|
||||
<Grid class="config-items scroller">
|
||||
<Version />
|
||||
<Repo />
|
||||
<Docker />
|
||||
<BuildArgs />
|
||||
<CliBuild />
|
||||
<ListenerUrl />
|
||||
</Grid>
|
||||
<Show when={userCanUpdate() && build.updated}>
|
||||
<Show
|
||||
@@ -10,15 +10,28 @@ import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Docker: Component<{}> = (p) => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [dockerAccounts, setDockerAccounts] = createSignal<string[]>();
|
||||
const [peripheryDockerAccounts, setPeripheryDockerAccounts] =
|
||||
createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(build.server_id)
|
||||
.then(setDockerAccounts);
|
||||
.get_server_docker_accounts(build.server_id!)
|
||||
.then(setPeripheryDockerAccounts);
|
||||
}
|
||||
});
|
||||
const dockerAccounts = () => {
|
||||
if (build.server_id) {
|
||||
return peripheryDockerAccounts() || [];
|
||||
} else if (build.aws_config) {
|
||||
const ami_id =
|
||||
build.aws_config?.ami_id || aws_builder_config()?.default_ami_id;
|
||||
return ami_id
|
||||
? aws_builder_config()?.available_ami_accounts![ami_id].docker || []
|
||||
: [];
|
||||
} else return [];
|
||||
};
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>docker build</h1> {/* checkbox here? */}
|
||||
@@ -62,7 +75,7 @@ const Docker: Component<{}> = (p) => {
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={build.docker_account || "none"}
|
||||
items={["none", ...dockerAccounts()!]}
|
||||
items={["none", ...dockerAccounts()]}
|
||||
onSelect={(account) => {
|
||||
setBuild(
|
||||
"docker_account",
|
||||
37
frontend/src/components/build/tabs/config/ListenerUrl.tsx
Normal file
37
frontend/src/components/build/tabs/config/ListenerUrl.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { pushNotification, URL } from "../../../..";
|
||||
import { copyToClipboard, 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";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const ListenerUrl: Component<{}> = (p) => {
|
||||
const { build, userCanUpdate } = useConfig();
|
||||
const listenerUrl = () => `${URL}/api/listener/build/${getId(build)}`;
|
||||
return (
|
||||
<Show when={userCanUpdate()}>
|
||||
<Grid class="config-item shadow">
|
||||
<h1>webhook url</h1>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<div class="ellipsis" style={{ width: "250px" }}>
|
||||
{listenerUrl()}
|
||||
</div>
|
||||
<ConfirmButton
|
||||
class="blue"
|
||||
onFirstClick={() => {
|
||||
copyToClipboard(listenerUrl());
|
||||
pushNotification("good", "copied url to clipboard");
|
||||
}}
|
||||
confirm={<Icon type="check" />}
|
||||
>
|
||||
<Icon type="clipboard" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
export default ListenerUrl;
|
||||
90
frontend/src/components/build/tabs/config/Repo.tsx
Normal file
90
frontend/src/components/build/tabs/config/Repo.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Input from "../../../shared/Input";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { client } from "../../../..";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
|
||||
const Repo: Component<{}> = (p) => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [peripheryGithubAccounts, setPeripheryGithubAccounts] =
|
||||
createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_github_accounts(build.server_id!)
|
||||
.then(setPeripheryGithubAccounts);
|
||||
}
|
||||
});
|
||||
const githubAccounts = () => {
|
||||
if (build.server_id) {
|
||||
return peripheryGithubAccounts() || [];
|
||||
} else if (build.aws_config) {
|
||||
const ami_id =
|
||||
build.aws_config?.ami_id || aws_builder_config()?.default_ami_id;
|
||||
return ami_id
|
||||
? aws_builder_config()?.available_ami_accounts![ami_id].github || []
|
||||
: [];
|
||||
} else return [];
|
||||
};
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>repo config</h1>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>repo: </h2>
|
||||
<Input
|
||||
placeholder="ie. solidjs/solid"
|
||||
value={build.repo || ""}
|
||||
onEdit={(value) => setBuild("repo", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>branch: </h2>
|
||||
<Input
|
||||
placeholder="defaults to main"
|
||||
value={build.branch || (userCanUpdate() ? "" : "main")}
|
||||
onEdit={(value) => setBuild("branch", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Show when={githubAccounts()}>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>github account: </h2>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={build.github_account || "none"}
|
||||
items={["none", ...githubAccounts()]}
|
||||
onSelect={(account) => {
|
||||
setBuild(
|
||||
"github_account",
|
||||
account === "none" ? undefined : account
|
||||
);
|
||||
}}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Repo;
|
||||
@@ -1,72 +0,0 @@
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
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, server, userCanUpdate } = useConfig();
|
||||
const [githubAccounts, setGithubAccounts] = createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client.get_server_github_accounts(build.server_id).then(setGithubAccounts);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>github config</h1>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>repo: </h2>
|
||||
<Input
|
||||
placeholder="ie. solidjs/solid"
|
||||
value={build.repo || ""}
|
||||
onEdit={(value) => setBuild("repo", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>branch: </h2>
|
||||
<Input
|
||||
placeholder="defaults to main"
|
||||
value={build.branch || (userCanUpdate() ? "" : "main")}
|
||||
onEdit={(value) => setBuild("branch", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>github account: </h2>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={build.github_account || "none"}
|
||||
items={["none", ...githubAccounts()!]}
|
||||
onSelect={(account) => {
|
||||
setBuild(
|
||||
"github_account",
|
||||
account === "none" ? undefined : account
|
||||
);
|
||||
}}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Git;
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Component } from "solid-js";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import Input from "../../../shared/Input";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
|
||||
const OnClone: Component = () => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>on clone</h1>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>path:</h2>
|
||||
<Input
|
||||
placeholder="relative to repo"
|
||||
value={build.on_clone?.path || ""}
|
||||
onEdit={(path) => {
|
||||
if (
|
||||
path.length === 0 &&
|
||||
(!build.on_clone ||
|
||||
!build.on_clone.command ||
|
||||
build.on_clone.command.length === 0)
|
||||
) {
|
||||
setBuild("on_clone", undefined);
|
||||
}
|
||||
setBuild("on_clone", { path });
|
||||
}}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>command:</h2>
|
||||
<Input
|
||||
placeholder="command"
|
||||
value={build.on_clone?.command || ""}
|
||||
onEdit={(command) => {
|
||||
if (
|
||||
command.length === 0 &&
|
||||
(!build.on_clone ||
|
||||
!build.on_clone.path ||
|
||||
build.on_clone.path.length === 0)
|
||||
) {
|
||||
setBuild("on_clone", undefined);
|
||||
}
|
||||
setBuild("on_clone", { command });
|
||||
}}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnClone;
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { Component, createResource, Show } from "solid-js";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import {
|
||||
combineClasses,
|
||||
deploymentHeaderStateClass,
|
||||
getId,
|
||||
readableVersion,
|
||||
} from "../../util/helpers";
|
||||
import Icon from "../shared/Icon";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
@@ -20,7 +21,7 @@ import CopyMenu from "../CopyMenu";
|
||||
import ConfirmMenuButton from "../shared/ConfirmMenuButton";
|
||||
|
||||
const Header: Component<{}> = (p) => {
|
||||
const { deployments, servers } = useAppState();
|
||||
const { deployments, servers, builds } = useAppState();
|
||||
const params = useParams();
|
||||
const deployment = () => deployments.get(params.id)!;
|
||||
const { user } = useUser();
|
||||
@@ -37,6 +38,27 @@ const Header: Component<{}> = (p) => {
|
||||
deployment().deployment.permissions![getId(user())] ===
|
||||
PermissionLevel.Update;
|
||||
const server = () => servers.get(deployment().deployment.server_id);
|
||||
const [deployed_version] = createResource(() =>
|
||||
client.get_deployment_deployed_version(params.id)
|
||||
);
|
||||
const image = () => {
|
||||
if (deployment().deployment.build_id) {
|
||||
const build = builds.get(deployment().deployment.build_id!)!;
|
||||
if (deployment().state === DockerContainerState.NotDeployed) {
|
||||
const version = deployment().deployment.build_version
|
||||
? readableVersion(deployment().deployment.build_version!).replaceAll(
|
||||
"v",
|
||||
""
|
||||
)
|
||||
: "latest";
|
||||
return `${build.name}:${version}`;
|
||||
} else {
|
||||
return deployed_version() && `${build.name}:${deployed_version()}`;
|
||||
}
|
||||
} else {
|
||||
return deployment().deployment.docker_run_args.image || "unknown";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Grid
|
||||
@@ -52,7 +74,10 @@ const Header: Component<{}> = (p) => {
|
||||
}}
|
||||
>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>{deployment()!.deployment.name}</h1>
|
||||
<Flex alignItems="center">
|
||||
<h1>{deployment()!.deployment.name}</h1>
|
||||
<div style={{ opacity: 0.7 }}>{image()}</div>
|
||||
</Flex>
|
||||
<Show when={userCanUpdate()}>
|
||||
<Flex alignItems="center">
|
||||
<CopyMenu type="deployment" id={params.id} />
|
||||
|
||||
@@ -49,9 +49,9 @@ const Config: Component<{}> = () => {
|
||||
</Show>
|
||||
<Network />
|
||||
<Restart />
|
||||
<Env />
|
||||
<Ports />
|
||||
<Mounts />
|
||||
<Env />
|
||||
<ExtraArgs />
|
||||
<PostImage />
|
||||
<Show when={isMobile()}>
|
||||
|
||||
@@ -17,7 +17,7 @@ const Env: Component<{}> = (p) => {
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>environment</h1>
|
||||
<Flex alignItems="center" gap="0.2rem">
|
||||
<Flex alignItems="center">
|
||||
<Show
|
||||
when={
|
||||
!deployment.docker_run_args.environment ||
|
||||
|
||||
@@ -22,21 +22,11 @@ const ExtraArgs: Component<{}> = (p) => {
|
||||
<Grid class="config-item shadow">
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<h1>extra args</h1>
|
||||
<Flex alignItems="center">
|
||||
<Show
|
||||
when={
|
||||
!deployment.docker_run_args.extra_args ||
|
||||
deployment.docker_run_args.extra_args.length === 0
|
||||
}
|
||||
>
|
||||
<div>none</div>
|
||||
</Show>
|
||||
<Show when={userCanUpdate()}>
|
||||
<button class="green" onClick={onAdd}>
|
||||
<Icon type="plus" />
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Show when={userCanUpdate()}>
|
||||
<button class="green" onClick={onAdd}>
|
||||
<Icon type="plus" />
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
<For each={[...deployment.docker_run_args.extra_args!.keys()]}>
|
||||
{(_, index) => (
|
||||
|
||||
@@ -20,6 +20,7 @@ const Network: Component<{}> = (p) => {
|
||||
onSelect={(network) => setDeployment("docker_run_args", { network })}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
searchStyle={{ width: "100%", "min-width": "12rem" }}
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
@@ -23,21 +23,11 @@ const Volumes: Component<{}> = (p) => {
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<h1>volumes</h1>
|
||||
<Flex alignItems="center">
|
||||
<Show
|
||||
when={
|
||||
!deployment.docker_run_args.volumes ||
|
||||
deployment.docker_run_args.volumes.length === 0
|
||||
}
|
||||
>
|
||||
<div>none</div>
|
||||
</Show>
|
||||
<Show when={userCanUpdate()}>
|
||||
<button class="green" onClick={onAdd}>
|
||||
<Icon type="plus" />
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
<Show when={userCanUpdate()}>
|
||||
<button class="green" onClick={onAdd}>
|
||||
<Icon type="plus" />
|
||||
</button>
|
||||
</Show>
|
||||
</Flex>
|
||||
<For each={deployment.docker_run_args.volumes}>
|
||||
{({ local, container }, index) => (
|
||||
|
||||
@@ -70,7 +70,7 @@ const Log: Component<{
|
||||
const buffer = useBuffer(scrolled, 250);
|
||||
const [poll, togglePoll] = useLocalStorageToggle(
|
||||
"deployment-log-polling",
|
||||
true
|
||||
false
|
||||
);
|
||||
clearInterval(interval);
|
||||
interval = setInterval(() => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAppState } from "../../state/StateProvider";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import SimpleTabs from "../shared/tabs/SimpleTabs";
|
||||
import Summary from "./Summary";
|
||||
import Builds from "./Tree/Builds";
|
||||
import Groups from "./Tree/Groups";
|
||||
import { TreeProvider } from "./Tree/Provider";
|
||||
import Servers from "./Tree/Servers";
|
||||
@@ -36,6 +37,10 @@ const Home: Component<{}> = (p) => {
|
||||
title: "servers",
|
||||
element: () => <Servers serverIDs={servers.ids()!} showAdd />,
|
||||
},
|
||||
{
|
||||
title: "builds",
|
||||
element: () => <Builds />
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</TreeProvider>
|
||||
|
||||
171
frontend/src/components/home/Tree/Builds.tsx
Normal file
171
frontend/src/components/home/Tree/Builds.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import { Component, createMemo, createSignal, For, 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 { getId, readableMonitorTimestamp } from "../../../util/helpers";
|
||||
import {
|
||||
ActionStateProvider,
|
||||
useActionStates,
|
||||
} from "../../build/ActionStateProvider";
|
||||
import { NewBuild } from "../../New";
|
||||
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 Loading from "../../shared/loading/Loading";
|
||||
import Selector from "../../shared/menu/Selector";
|
||||
import { TreeSortType, TREE_SORTS, useTreeState } from "./Provider";
|
||||
|
||||
const Builds: Component<{}> = (p) => {
|
||||
const { user } = useUser();
|
||||
const { builds } = useAppState();
|
||||
const { sort, setSort, build_sorter } = useTreeState();
|
||||
const [buildFilter, setBuildFilter] = createSignal("");
|
||||
const buildIDs = createMemo(() => {
|
||||
if (builds.loaded()) {
|
||||
const filters = buildFilter()
|
||||
.split(" ")
|
||||
.filter((term) => term.length > 0)
|
||||
.map((term) => term.toLowerCase());
|
||||
return builds
|
||||
.ids()!
|
||||
.filter((id) => {
|
||||
const name = builds.get(id)!.name;
|
||||
for (const term of filters) {
|
||||
if (!name.includes(term)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sort(build_sorter());
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Grid>
|
||||
<Grid gridTemplateColumns="1fr auto auto">
|
||||
<Input
|
||||
placeholder="filter builds"
|
||||
value={buildFilter()}
|
||||
onEdit={setBuildFilter}
|
||||
style={{ width: "100%", padding: "0.5rem" }}
|
||||
/>
|
||||
<Selector
|
||||
selected={sort()}
|
||||
items={TREE_SORTS as any as string[]}
|
||||
onSelect={(mode) => setSort(mode as TreeSortType)}
|
||||
position="bottom right"
|
||||
targetClass="blue"
|
||||
targetStyle={{ height: "100%" }}
|
||||
containerStyle={{ height: "100%" }}
|
||||
/>
|
||||
<Show when={user().admin || user().create_build_permissions}>
|
||||
<NewBuild />
|
||||
</Show>
|
||||
</Grid>
|
||||
<For each={buildIDs()}>
|
||||
{(id) => (
|
||||
<ActionStateProvider build_id={id}>
|
||||
<Build id={id} />
|
||||
</ActionStateProvider>
|
||||
)}
|
||||
</For>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const Build: Component<{ id: string }> = (p) => {
|
||||
const { isMobile } = useAppDimensions();
|
||||
const { user } = useUser();
|
||||
const { builds, servers } = useAppState();
|
||||
const build = () => builds.get(p.id)!;
|
||||
const server = () =>
|
||||
build().server_id ? servers.get(build().server_id!) : undefined;
|
||||
const version = () => {
|
||||
return `v${build().version.major}.${build().version.minor}.${
|
||||
build().version.patch
|
||||
}`;
|
||||
};
|
||||
const lastBuiltAt = () => {
|
||||
if (
|
||||
build().last_built_at === undefined ||
|
||||
build().last_built_at?.length === 0 ||
|
||||
build().last_built_at === "never"
|
||||
) {
|
||||
return "not built";
|
||||
} else {
|
||||
return readableMonitorTimestamp(build().last_built_at!);
|
||||
}
|
||||
};
|
||||
const actions = useActionStates();
|
||||
const userCanExecute = () =>
|
||||
user().admin ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Execute ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Update;
|
||||
const isAwsBuild = () => build().aws_config ? true : false;
|
||||
return (
|
||||
<A
|
||||
href={`/build/${p.id}`}
|
||||
class="card light shadow"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "fit-content",
|
||||
"box-sizing": "border-box",
|
||||
"justify-content": "space-between",
|
||||
padding: "0.5rem",
|
||||
}}
|
||||
>
|
||||
<h1 style={{ "font-size": "1.25rem" }}>{build().name}</h1>
|
||||
<Flex alignItems="center">
|
||||
<Show when={server()}>
|
||||
<A
|
||||
href={`/server/${build().server_id!}`}
|
||||
style={{ padding: 0, opacity: 0.7 }}
|
||||
>
|
||||
<div class="text-hover">{server()?.server.name}</div>
|
||||
</A>
|
||||
</Show>
|
||||
<Show when={isAwsBuild()}>
|
||||
<div style={{ opacity: 0.7 }}>aws build</div>
|
||||
</Show>
|
||||
<h2>{version()}</h2>
|
||||
<Show when={!isMobile()}>
|
||||
<div style={{ opacity: 0.7 }}>{lastBuiltAt()}</div>
|
||||
</Show>
|
||||
<Show when={userCanExecute()}>
|
||||
<Show
|
||||
when={!actions.building}
|
||||
fallback={
|
||||
<button
|
||||
class="green"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<Loading type="spinner" />
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
onConfirm={() => {
|
||||
client.build(p.id);
|
||||
}}
|
||||
>
|
||||
<Icon type="build" width="0.9rem" />
|
||||
</ConfirmButton>
|
||||
</Show>
|
||||
</Show>
|
||||
</Flex>
|
||||
</A>
|
||||
);
|
||||
};
|
||||
|
||||
export default Builds;
|
||||
@@ -6,7 +6,7 @@ export const TREE_SORTS = ["name", "created"] as const;
|
||||
export type TreeSortType = typeof TREE_SORTS[number];
|
||||
|
||||
const value = () => {
|
||||
const { servers, groups } = useAppState();
|
||||
const { servers, groups, builds } = useAppState();
|
||||
const [sort, setSort] = useLocalStorage<TreeSortType>(
|
||||
TREE_SORTS[0],
|
||||
"home-sort-v1"
|
||||
@@ -29,7 +29,7 @@ const value = () => {
|
||||
}
|
||||
};
|
||||
const group_sorter = () => {
|
||||
if (!groups.loaded) return () => 0;
|
||||
if (!groups.loaded()) return () => 0;
|
||||
if (sort() === "name") {
|
||||
return (a: string, b: string) => {
|
||||
const ga = groups.get(a)!;
|
||||
@@ -44,12 +44,30 @@ const value = () => {
|
||||
} else {
|
||||
return () => 0;
|
||||
}
|
||||
};
|
||||
const build_sorter = () => {
|
||||
if (!builds.loaded()) return () => 0;
|
||||
if (sort() === "name") {
|
||||
return (a: string, b: string) => {
|
||||
const ba = builds.get(a)!;
|
||||
const bb = builds.get(b)!;
|
||||
if (ba.name < bb.name) {
|
||||
return -1;
|
||||
} else if (ba.name > bb.name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
} else {
|
||||
return () => 0;
|
||||
}
|
||||
};
|
||||
return {
|
||||
sort,
|
||||
setSort,
|
||||
server_sorter,
|
||||
group_sorter,
|
||||
build_sorter
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,50 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { Component, createResource, Show } from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { combineClasses, deploymentStateClass, getId } from "../../util/helpers";
|
||||
import { DockerContainerState } from "../../types";
|
||||
import {
|
||||
combineClasses,
|
||||
deploymentStateClass,
|
||||
getId,
|
||||
readableVersion,
|
||||
} from "../../util/helpers";
|
||||
import Circle from "../shared/Circle";
|
||||
import Flex from "../shared/layout/Flex";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import s from "./serverchildren.module.scss";
|
||||
|
||||
const Deployment: Component<{ id: string }> = (p) => {
|
||||
const { deployments } = useAppState();
|
||||
const { deployments, builds } = useAppState();
|
||||
const deployment = () => deployments.get(p.id)!;
|
||||
const [deployed_version] = createResource(() =>
|
||||
client.get_deployment_deployed_version(p.id)
|
||||
);
|
||||
const image = () => {
|
||||
if (deployment().deployment.build_id) {
|
||||
const build = builds.get(deployment().deployment.build_id!)!;
|
||||
if (deployment().state === DockerContainerState.NotDeployed) {
|
||||
const version = deployment().deployment.build_version
|
||||
? readableVersion(deployment().deployment.build_version!).replaceAll(
|
||||
"v",
|
||||
""
|
||||
)
|
||||
: "latest";
|
||||
return `${build.name}:${version}`;
|
||||
} else {
|
||||
return deployed_version() && `${build.name}:${deployed_version()}`;
|
||||
}
|
||||
} else {
|
||||
return deployment().deployment.docker_run_args.image || "unknown";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Show when={deployment()}>
|
||||
<A
|
||||
href={`/deployment/${p.id}`}
|
||||
class={combineClasses(
|
||||
s.DropdownItem,
|
||||
)}
|
||||
>
|
||||
<h2>{deployment().deployment.name}</h2>
|
||||
<A href={`/deployment/${p.id}`} class={combineClasses(s.DropdownItem)}>
|
||||
<Grid gap="0">
|
||||
<h2>{deployment().deployment.name}</h2>
|
||||
<div style={{ opacity: 0.7 }}>{image()}</div>
|
||||
</Grid>
|
||||
<Flex alignItems="center">
|
||||
<div style={{ opacity: 0.7 }}>{deployments.status(p.id)}</div>
|
||||
<Circle
|
||||
|
||||
@@ -6,15 +6,14 @@ import SimpleTabs from "../shared/tabs/SimpleTabs";
|
||||
import s from "./serverchildren.module.scss";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import { PermissionLevel } from "../../types";
|
||||
import { NewBuild, NewDeployment } from "../New";
|
||||
import { NewDeployment } from "../New";
|
||||
import Deployment from "./Deployment";
|
||||
import Build from "./Build";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
|
||||
const ServerChildren: Component<{ id: string }> = (p) => {
|
||||
const { user } = useUser();
|
||||
const { isSemiMobile } = useAppDimensions();
|
||||
const { servers, deployments, builds } = useAppState();
|
||||
const { servers, deployments } = useAppState();
|
||||
const server = () => servers.get(p.id);
|
||||
const deploymentIDs = createMemo(() => {
|
||||
return (deployments.loaded() &&
|
||||
@@ -24,61 +23,79 @@ const ServerChildren: Component<{ id: string }> = (p) => {
|
||||
(id) => deployments.get(id)?.deployment.server_id === p.id
|
||||
)) as string[];
|
||||
});
|
||||
const buildIDs = createMemo(() => {
|
||||
return (builds.loaded() &&
|
||||
builds
|
||||
.ids()!
|
||||
.filter((id) => builds.get(id)?.server_id === p.id)) as string[];
|
||||
});
|
||||
// const buildIDs = createMemo(() => {
|
||||
// return (builds.loaded() &&
|
||||
// builds
|
||||
// .ids()!
|
||||
// .filter((id) => builds.get(id)?.server_id === p.id)) as string[];
|
||||
// });
|
||||
return (
|
||||
<SimpleTabs
|
||||
containerClass="card shadow"
|
||||
localStorageKey={`${p.id}-home-tab`}
|
||||
tabs={[
|
||||
{
|
||||
title: "deployments",
|
||||
element: () => (
|
||||
<Grid
|
||||
gap=".5rem"
|
||||
class={combineClasses(s.Deployments)}
|
||||
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||
>
|
||||
<For each={deploymentIDs()}>{(id) => <Deployment id={id} />}</For>
|
||||
<Show
|
||||
when={
|
||||
user().admin ||
|
||||
server()?.server.permissions![getId(user())] ===
|
||||
PermissionLevel.Update
|
||||
}
|
||||
>
|
||||
<NewDeployment serverID={p.id} />
|
||||
</Show>
|
||||
</Grid>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "builds",
|
||||
element: () => (
|
||||
<Grid
|
||||
gap=".5rem"
|
||||
class={combineClasses(s.Deployments)}
|
||||
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||
>
|
||||
<For each={buildIDs()}>{(id) => <Build id={id} />}</For>
|
||||
<Show
|
||||
when={
|
||||
user().admin ||
|
||||
server()?.server.permissions![getId(user())] ===
|
||||
PermissionLevel.Update
|
||||
}
|
||||
>
|
||||
<NewBuild serverID={p.id} />
|
||||
</Show>
|
||||
</Grid>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<div class="card shadow">
|
||||
<Grid
|
||||
gap=".5rem"
|
||||
class={combineClasses(s.Deployments)}
|
||||
gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||
>
|
||||
<For each={deploymentIDs()}>{(id) => <Deployment id={id} />}</For>
|
||||
<Show
|
||||
when={
|
||||
user().admin ||
|
||||
server()?.server.permissions![getId(user())] ===
|
||||
PermissionLevel.Update
|
||||
}
|
||||
>
|
||||
<NewDeployment serverID={p.id} />
|
||||
</Show>
|
||||
</Grid>
|
||||
</div>
|
||||
// <SimpleTabs
|
||||
// containerClass="card shadow"
|
||||
// localStorageKey={`${p.id}-home-tab`}
|
||||
// tabs={[
|
||||
// {
|
||||
// title: "deployments",
|
||||
// element: () => (
|
||||
// <Grid
|
||||
// gap=".5rem"
|
||||
// class={combineClasses(s.Deployments)}
|
||||
// gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||
// >
|
||||
// <For each={deploymentIDs()}>{(id) => <Deployment id={id} />}</For>
|
||||
// <Show
|
||||
// when={
|
||||
// user().admin ||
|
||||
// server()?.server.permissions![getId(user())] ===
|
||||
// PermissionLevel.Update
|
||||
// }
|
||||
// >
|
||||
// <NewDeployment serverID={p.id} />
|
||||
// </Show>
|
||||
// </Grid>
|
||||
// ),
|
||||
// },
|
||||
// // {
|
||||
// // title: "builds",
|
||||
// // element: () => (
|
||||
// // <Grid
|
||||
// // gap=".5rem"
|
||||
// // class={combineClasses(s.Deployments)}
|
||||
// // gridTemplateColumns={isSemiMobile() ? "1fr" : "1fr 1fr"}
|
||||
// // >
|
||||
// // <For each={buildIDs()}>{(id) => <Build id={id} />}</For>
|
||||
// // <Show
|
||||
// // when={
|
||||
// // user().admin ||
|
||||
// // server()?.server.permissions![getId(user())] ===
|
||||
// // PermissionLevel.Update
|
||||
// // }
|
||||
// // >
|
||||
// // <NewBuild serverID={p.id} />
|
||||
// // </Show>
|
||||
// // </Grid>
|
||||
// // ),
|
||||
// // },
|
||||
// ]}
|
||||
// />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ const ConfirmButton: Component<{
|
||||
onBlur={() => set(false)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (confirm()) {
|
||||
p.onConfirm && p.onConfirm();
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import {
|
||||
Accessor,
|
||||
Component,
|
||||
createEffect,
|
||||
createSignal,
|
||||
JSX,
|
||||
JSXElement,
|
||||
Show,
|
||||
@@ -27,16 +25,16 @@ const CenterMenu: Component<{
|
||||
style?: JSX.CSSProperties;
|
||||
position?: "top" | "center";
|
||||
}> = (p) => {
|
||||
const [buffer, set] = createSignal(p.show());
|
||||
createEffect(() => {
|
||||
if (p.show()) {
|
||||
set(true);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
set(false);
|
||||
}, 350);
|
||||
}
|
||||
});
|
||||
// const [buffer, set] = createSignal(p.show());
|
||||
// createEffect(() => {
|
||||
// if (p.show()) {
|
||||
// set(true);
|
||||
// } else {
|
||||
// setTimeout(() => {
|
||||
// set(false);
|
||||
// }, 350);
|
||||
// }
|
||||
// });
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
@@ -49,7 +47,7 @@ const CenterMenu: Component<{
|
||||
>
|
||||
{p.target}
|
||||
</button>
|
||||
<Show when={buffer()}>
|
||||
<Show when={p.show()}>
|
||||
<Child {...p} show={p.show} toggleShow={p.toggleShow} />
|
||||
</Show>
|
||||
</>
|
||||
@@ -69,7 +67,7 @@ const Child: Component<{
|
||||
useKeyDown("Escape", p.toggleShow);
|
||||
return (
|
||||
<Grid
|
||||
class={combineClasses(s.CenterMenuContainer, p.show() ? s.Enter : s.Exit)}
|
||||
class={combineClasses(s.CenterMenuContainer)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
p.toggleShow();
|
||||
|
||||
@@ -22,18 +22,18 @@ const HoverMenu: Component<{
|
||||
containerStyle?: JSX.CSSProperties;
|
||||
}> = (p) => {
|
||||
const [show, set] = createSignal(false);
|
||||
const [buffer, setBuffer] = createSignal(false);
|
||||
let timeout: number;
|
||||
createEffect(() => {
|
||||
clearTimeout(timeout);
|
||||
if (show()) {
|
||||
setBuffer(true);
|
||||
} else {
|
||||
timeout = setTimeout(() => {
|
||||
setBuffer(false);
|
||||
}, 350);
|
||||
}
|
||||
});
|
||||
// const [buffer, setBuffer] = createSignal(false);
|
||||
// let timeout: number;
|
||||
// createEffect(() => {
|
||||
// clearTimeout(timeout);
|
||||
// if (show()) {
|
||||
// setBuffer(true);
|
||||
// } else {
|
||||
// timeout = setTimeout(() => {
|
||||
// setBuffer(false);
|
||||
// }, 350);
|
||||
// }
|
||||
// });
|
||||
return (
|
||||
<Flex
|
||||
class={s.HoverMenuTarget}
|
||||
@@ -44,13 +44,13 @@ const HoverMenu: Component<{
|
||||
alignItems="center"
|
||||
>
|
||||
{p.target}
|
||||
<Show when={buffer()}>
|
||||
<Show when={show()}>
|
||||
<div
|
||||
class={combineClasses(
|
||||
p.contentClass,
|
||||
getPositionClass(p.position),
|
||||
s.HoverMenu,
|
||||
show() ? s.Enter : s.Exit,
|
||||
// show() ? s.Enter : s.Exit,
|
||||
)}
|
||||
onMouseOut={() => {
|
||||
set(false);
|
||||
@@ -59,7 +59,7 @@ const HoverMenu: Component<{
|
||||
set(false)
|
||||
e.stopPropagation();
|
||||
}}
|
||||
style={{ ...p.contentStyle, padding: p.padding }}
|
||||
style={{ ...p.contentStyle, padding: p.padding || "0.5rem" }}
|
||||
>
|
||||
{p.content}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
Component,
|
||||
createEffect,
|
||||
createSignal,
|
||||
JSX,
|
||||
JSXElement,
|
||||
Show,
|
||||
@@ -22,20 +20,20 @@ const Menu: Component<{
|
||||
containerStyle?: JSX.CSSProperties;
|
||||
backgroundColor?: string;
|
||||
}> = (p) => {
|
||||
const [buffer, set] = createSignal(p.show);
|
||||
createEffect(() => {
|
||||
if (p.show) {
|
||||
set(true);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
set(false);
|
||||
}, 350);
|
||||
}
|
||||
});
|
||||
// const [buffer, set] = createSignal(p.show);
|
||||
// createEffect(() => {
|
||||
// if (p.show) {
|
||||
// set(true);
|
||||
// } else {
|
||||
// setTimeout(() => {
|
||||
// set(false);
|
||||
// }, 350);
|
||||
// }
|
||||
// });
|
||||
return (
|
||||
<div class={s.MenuContainer} style={p.containerStyle}>
|
||||
{p.target}
|
||||
<Show when={buffer()}>
|
||||
<Show when={p.show}>
|
||||
<div
|
||||
class={s.MenuBackground}
|
||||
style={{ "background-color": p.backgroundColor }}
|
||||
@@ -47,7 +45,7 @@ const Menu: Component<{
|
||||
s.Menu,
|
||||
"shadow",
|
||||
getPositionClass(p.position),
|
||||
p.show ? s.Enter : s.Exit
|
||||
// p.show ? s.Enter : s.Exit
|
||||
)}
|
||||
style={{ padding: p.padding as any, ...p.menuStyle }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
|
||||
@@ -16,7 +16,6 @@ const Account: Component<{ close: () => void }> = (p) => {
|
||||
<Show when={isMobile()}>
|
||||
<Flex justifyContent="center">{user().username}</Flex>
|
||||
</Show>
|
||||
<Flex justifyContent="center">admin: {user().admin.toString()}</Flex>
|
||||
<Show when={user().admin}>
|
||||
<A
|
||||
href="/users"
|
||||
@@ -27,12 +26,12 @@ const Account: Component<{ close: () => void }> = (p) => {
|
||||
manage users
|
||||
</A>
|
||||
</Show>
|
||||
<Show when={!user().admin}>
|
||||
{/* <Show when={!user().admin}>
|
||||
<Flex justifyContent="center">
|
||||
create server permissions:{" "}
|
||||
{user().create_server_permissions.toString()}
|
||||
{user().create_server_permissions?.toString()}
|
||||
</Flex>
|
||||
</Show>
|
||||
</Show> */}
|
||||
<A
|
||||
href="/account"
|
||||
class="grey"
|
||||
|
||||
@@ -17,7 +17,7 @@ import { ControlledTabs } from "../../shared/tabs/Tabs";
|
||||
import { useAppDimensions } from "../../../state/DimensionProvider";
|
||||
import Grid from "../../shared/layout/Grid";
|
||||
import { A, useNavigate } from "@solidjs/router";
|
||||
import { ServerStatus } from "../../../types";
|
||||
import { Build, ServerStatus } from "../../../types";
|
||||
|
||||
const mobileStyle: JSX.CSSProperties = {
|
||||
// position: "fixed",
|
||||
@@ -191,8 +191,10 @@ const Builds: Component<{ close: () => void }> = (p) => {
|
||||
gap="0.2rem"
|
||||
style={{ opacity: 0.6, "font-size": "0.9rem" }}
|
||||
>
|
||||
{servers.get(build.server_id)?.server.name}
|
||||
<Icon type="caret-right" width="0.7rem" />
|
||||
<Show when={build.server_id}>
|
||||
{build.server_id && servers.get(build.server_id)?.server.name}
|
||||
<Icon type="caret-right" width="0.7rem" />
|
||||
</Show>
|
||||
build
|
||||
</Flex>
|
||||
</Grid>
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Show,
|
||||
} from "solid-js";
|
||||
import { client } from "../..";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { Operation } from "../../types";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
@@ -18,9 +19,19 @@ import Loading from "../shared/loading/Loading";
|
||||
import s from "./users.module.scss";
|
||||
|
||||
const Users: Component<{}> = (p) => {
|
||||
const { isMobile } = useAppDimensions();
|
||||
const { ws } = useAppState();
|
||||
const [users, { refetch }] = createResource(() => client.list_users());
|
||||
onCleanup(ws.subscribe([Operation.ModifyUserEnabled], refetch));
|
||||
onCleanup(
|
||||
ws.subscribe(
|
||||
[
|
||||
Operation.ModifyUserEnabled,
|
||||
Operation.ModifyUserCreateServerPermissions,
|
||||
Operation.ModifyUserCreateBuildPermissions,
|
||||
],
|
||||
refetch
|
||||
)
|
||||
);
|
||||
const [search, setSearch] = createSignal("");
|
||||
const filteredUsers = createMemo(() =>
|
||||
users()?.filter((user) => user.username.includes(search()))
|
||||
@@ -34,55 +45,78 @@ const Users: Component<{}> = (p) => {
|
||||
</Grid>
|
||||
}
|
||||
>
|
||||
<Grid class={s.UsersContent}>
|
||||
<Grid class={combineClasses(s.Users, "card shadow")}>
|
||||
<Flex justifyContent="space-between">
|
||||
<h1>users</h1>
|
||||
<Input
|
||||
class="lightgrey"
|
||||
placeholder="search"
|
||||
value={search()}
|
||||
onEdit={setSearch}
|
||||
/>
|
||||
</Flex>
|
||||
<For each={filteredUsers()}>
|
||||
{(user) => (
|
||||
<Flex class={combineClasses(s.User, "shadow")}>
|
||||
<div class={s.Username}>{user.username}</div>
|
||||
<Flex alignItems="center">
|
||||
<button
|
||||
class={user.enabled ? "green" : "red"}
|
||||
onClick={() => {
|
||||
client.modify_user_enabled({
|
||||
user_id: getId(user),
|
||||
enabled: !user.enabled,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{user.enabled ? "enabled" : "disabled"}
|
||||
</button>
|
||||
<button
|
||||
class={user.create_server_permissions ? "green" : "red"}
|
||||
onClick={() => {
|
||||
client.modify_user_create_server_permissions({
|
||||
user_id: getId(user),
|
||||
create_server_permissions: !user.create_server_permissions,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{user.create_server_permissions ? "can create servers" : "cannot create servers"}
|
||||
</button>
|
||||
{/* <ConfirmButton
|
||||
<Grid
|
||||
class="card shadow"
|
||||
style={{ width: "100%", "box-sizing": "border-box" }}
|
||||
>
|
||||
<Flex justifyContent="space-between">
|
||||
<h1>users</h1>
|
||||
<Input
|
||||
class="lightgrey"
|
||||
placeholder="search"
|
||||
value={search()}
|
||||
onEdit={setSearch}
|
||||
/>
|
||||
</Flex>
|
||||
<For each={filteredUsers()}>
|
||||
{(user) => (
|
||||
<Flex class={combineClasses(s.User, "shadow")}>
|
||||
<div class={s.Username}>{user.username}</div>
|
||||
<Grid
|
||||
placeItems="center end"
|
||||
gridTemplateColumns={!isMobile() ? "1fr 1fr 1fr" : undefined}
|
||||
>
|
||||
<button
|
||||
class={user.enabled ? "green" : "red"}
|
||||
style={{ width: isMobile() ? "11rem" : "6rem" }}
|
||||
onClick={() => {
|
||||
client.modify_user_enabled({
|
||||
user_id: getId(user),
|
||||
enabled: !user.enabled,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{user.enabled ? "enabled" : "disabled"}
|
||||
</button>
|
||||
<button
|
||||
class={user.create_server_permissions ? "green" : "red"}
|
||||
style={{ width: "11rem" }}
|
||||
onClick={() => {
|
||||
client.modify_user_create_server_permissions({
|
||||
user_id: getId(user),
|
||||
create_server_permissions:
|
||||
!user.create_server_permissions,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{user.create_server_permissions
|
||||
? "can create servers"
|
||||
: "cannot create servers"}
|
||||
</button>
|
||||
<button
|
||||
class={user.create_build_permissions ? "green" : "red"}
|
||||
style={{ width: "11rem" }}
|
||||
onClick={() => {
|
||||
client.modify_user_create_build_permissions({
|
||||
user_id: getId(user),
|
||||
create_build_permissions: !user.create_build_permissions,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{user.create_build_permissions
|
||||
? "can create builds"
|
||||
: "cannot create builds"}
|
||||
</button>
|
||||
{/* <ConfirmButton
|
||||
class="red"
|
||||
onConfirm={() => deleteUser(user._id!)}
|
||||
>
|
||||
<Icon type="trash" />
|
||||
</ConfirmButton> */}
|
||||
</Flex>
|
||||
</Flex>
|
||||
)}
|
||||
</For>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Flex>
|
||||
)}
|
||||
</For>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { createContext, ParentComponent, useContext } from "solid-js";
|
||||
import { createContext, createResource, ParentComponent, Resource, useContext } from "solid-js";
|
||||
import { useWindowKeyDown } from "../util/hooks";
|
||||
import {
|
||||
useBuilds,
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
} from "./hooks";
|
||||
import connectToWs from "./ws";
|
||||
import { useUser } from "./UserProvider";
|
||||
import { PermissionLevel } from "../types";
|
||||
import { AwsBuilderConfig, PermissionLevel } from "../types";
|
||||
import { client } from "..";
|
||||
|
||||
export type State = {
|
||||
usernames: ReturnType<typeof useUsernames>;
|
||||
@@ -32,6 +33,7 @@ export type State = {
|
||||
procedures: ReturnType<typeof useProcedures>;
|
||||
getPermissionOnProcedure: (id: string) => PermissionLevel;
|
||||
updates: ReturnType<typeof useUpdates>;
|
||||
aws_builder_config: Resource<AwsBuilderConfig>;
|
||||
};
|
||||
|
||||
const context = createContext<
|
||||
@@ -51,6 +53,7 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
const procedures = useProcedures();
|
||||
const deployments = useDeployments();
|
||||
const usernames = useUsernames();
|
||||
const [aws_builder_config] = createResource(() => client.get_aws_builder_defaults());
|
||||
const state: State = {
|
||||
usernames,
|
||||
servers,
|
||||
@@ -129,6 +132,7 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
}
|
||||
},
|
||||
updates: useUpdates(),
|
||||
aws_builder_config,
|
||||
};
|
||||
|
||||
// createEffect(() => {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Generated by typeshare 1.0.0
|
||||
*/
|
||||
|
||||
export type AvailableAmiAccounts = Record<string, AmiAccounts>;
|
||||
|
||||
export type PermissionsMap = Record<string, PermissionLevel>;
|
||||
|
||||
export interface Action {
|
||||
@@ -21,12 +23,12 @@ export interface Build {
|
||||
_id?: string;
|
||||
name: string;
|
||||
permissions?: PermissionsMap;
|
||||
server_id: string;
|
||||
server_id?: string;
|
||||
aws_config?: AwsBuilderBuildConfig;
|
||||
version: Version;
|
||||
repo?: string;
|
||||
branch?: string;
|
||||
github_account?: string;
|
||||
on_clone?: Command;
|
||||
pre_build?: Command;
|
||||
docker_build_args?: DockerBuildArgs;
|
||||
docker_account?: string;
|
||||
@@ -37,7 +39,6 @@ export interface Build {
|
||||
|
||||
export interface BuildActionState {
|
||||
building: boolean;
|
||||
recloning: boolean;
|
||||
updating: boolean;
|
||||
}
|
||||
|
||||
@@ -58,6 +59,37 @@ export interface BuildVersionsReponse {
|
||||
ts: string;
|
||||
}
|
||||
|
||||
export interface AwsBuilderBuildConfig {
|
||||
region?: string;
|
||||
instance_type?: string;
|
||||
ami_id?: string;
|
||||
volume_gb?: number;
|
||||
subnet_id?: string;
|
||||
security_group_ids?: string[];
|
||||
key_pair_name?: string;
|
||||
assign_public_ip?: boolean;
|
||||
}
|
||||
|
||||
export interface AwsBuilderConfig {
|
||||
access_key_id: string;
|
||||
secret_access_key: string;
|
||||
default_ami_id: string;
|
||||
default_subnet_id: string;
|
||||
default_key_pair_name: string;
|
||||
available_ami_accounts?: AvailableAmiAccounts;
|
||||
default_region?: string;
|
||||
default_volume_gb?: number;
|
||||
default_instance_type?: string;
|
||||
default_security_group_ids?: string[];
|
||||
default_assign_public_ip?: boolean;
|
||||
}
|
||||
|
||||
export interface AmiAccounts {
|
||||
name: string;
|
||||
github?: string[];
|
||||
docker?: string[];
|
||||
}
|
||||
|
||||
export interface Deployment {
|
||||
_id?: string;
|
||||
name: string;
|
||||
@@ -327,9 +359,10 @@ export interface Log {
|
||||
export interface User {
|
||||
_id?: string;
|
||||
username: string;
|
||||
enabled: boolean;
|
||||
admin: boolean;
|
||||
create_server_permissions: boolean;
|
||||
enabled?: boolean;
|
||||
admin?: boolean;
|
||||
create_server_permissions?: boolean;
|
||||
create_build_permissions?: boolean;
|
||||
avatar?: string;
|
||||
secrets?: ApiSecret[];
|
||||
password?: string;
|
||||
@@ -382,7 +415,6 @@ export enum Operation {
|
||||
UpdateBuild = "update_build",
|
||||
DeleteBuild = "delete_build",
|
||||
BuildBuild = "build_build",
|
||||
RecloneBuild = "reclone_build",
|
||||
CreateDeployment = "create_deployment",
|
||||
UpdateDeployment = "update_deployment",
|
||||
DeleteDeployment = "delete_deployment",
|
||||
@@ -400,6 +432,7 @@ export enum Operation {
|
||||
DeleteGroup = "delete_group",
|
||||
ModifyUserEnabled = "modify_user_enabled",
|
||||
ModifyUserCreateServerPermissions = "modify_user_create_server_permissions",
|
||||
ModifyUserCreateBuildPermissions = "modify_user_create_build_permissions",
|
||||
ModifyUserPermissions = "modify_user_permissions",
|
||||
AutoBuild = "auto_build",
|
||||
AutoPull = "auto_pull",
|
||||
@@ -449,7 +482,6 @@ export enum ProcedureOperation {
|
||||
PruneContainersServer = "prune_containers_server",
|
||||
PruneNetworksServer = "prune_networks_server",
|
||||
BuildBuild = "build_build",
|
||||
RecloneBuild = "reclone_build",
|
||||
DeployContainer = "deploy_container",
|
||||
StopContainer = "stop_container",
|
||||
StartContainer = "start_container",
|
||||
|
||||
@@ -2,6 +2,7 @@ import axios from "axios";
|
||||
import fileDownload from "js-file-download";
|
||||
import { URL } from "..";
|
||||
import {
|
||||
AwsBuilderConfig,
|
||||
BasicContainerInfo,
|
||||
Build,
|
||||
BuildActionState,
|
||||
@@ -38,6 +39,7 @@ import {
|
||||
CreateSecretBody,
|
||||
CreateServerBody,
|
||||
LoginOptions,
|
||||
ModifyUserCreateBuildBody,
|
||||
ModifyUserCreateServerBody,
|
||||
ModifyUserEnabledBody,
|
||||
PermissionsUpdateBody,
|
||||
@@ -145,6 +147,10 @@ export class Client {
|
||||
return this.get(`/api/deployment/${id}/stats`);
|
||||
}
|
||||
|
||||
get_deployment_deployed_version(id: string): Promise<string> {
|
||||
return this.get(`/api/deployment/${id}/deployed_version`);
|
||||
}
|
||||
|
||||
create_deployment(body: CreateDeploymentBody): Promise<Deployment> {
|
||||
return this.post("/api/deployment/create", body);
|
||||
}
|
||||
@@ -351,6 +357,10 @@ export class Client {
|
||||
return this.post(`/api/build/${id}/reclone`);
|
||||
}
|
||||
|
||||
get_aws_builder_defaults(): Promise<AwsBuilderConfig> {
|
||||
return this.get("/api/build/aws_builder_defaults");
|
||||
}
|
||||
|
||||
// procedure
|
||||
|
||||
list_procedures(query?: QueryObject): Promise<Procedure[]> {
|
||||
@@ -454,6 +464,12 @@ export class Client {
|
||||
return this.post("/api/permissions/modify_create_server", body);
|
||||
}
|
||||
|
||||
modify_user_create_build_permissions(
|
||||
body: ModifyUserCreateBuildBody
|
||||
): Promise<Update> {
|
||||
return this.post("/api/permissions/modify_create_build", body);
|
||||
}
|
||||
|
||||
async get<R = any>(url: string): Promise<R> {
|
||||
return await axios({
|
||||
method: "get",
|
||||
|
||||
@@ -6,12 +6,10 @@ import { PermissionLevel, PermissionsTarget } from "../types";
|
||||
|
||||
export interface CreateBuildBody {
|
||||
name: string;
|
||||
server_id: string;
|
||||
}
|
||||
|
||||
export interface CopyBuildBody {
|
||||
name: string;
|
||||
server_id: string;
|
||||
}
|
||||
|
||||
export interface BuildVersionsQuery {
|
||||
@@ -56,6 +54,11 @@ export interface ModifyUserCreateServerBody {
|
||||
create_server_permissions: boolean;
|
||||
}
|
||||
|
||||
export interface ModifyUserCreateBuildBody {
|
||||
user_id: string;
|
||||
create_build_permissions: boolean;
|
||||
}
|
||||
|
||||
export interface CreateProcedureBody {
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
[package]
|
||||
name = "db_client"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
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.15" }
|
||||
# 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.15"
|
||||
version = "0.2.0"
|
||||
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.15" }
|
||||
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"
|
||||
@@ -23,3 +24,5 @@ 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>,
|
||||
@@ -120,7 +83,7 @@ async fn clone(
|
||||
access_token: Option<GithubToken>,
|
||||
) -> Log {
|
||||
let _ = std::fs::remove_dir_all(destination);
|
||||
let access_token = match access_token {
|
||||
let access_token_at = match &access_token {
|
||||
Some(token) => format!("{token}@"),
|
||||
None => String::new(),
|
||||
};
|
||||
@@ -128,12 +91,12 @@ async fn clone(
|
||||
Some(branch) => format!(" -b {branch}"),
|
||||
None => String::new(),
|
||||
};
|
||||
let repo_url = format!("https://{access_token}github.com/{repo}.git");
|
||||
let repo_url = format!("https://{access_token_at}github.com/{repo}.git");
|
||||
let command = format!("git clone {repo_url} {destination}{branch}");
|
||||
let start_ts = monitor_timestamp();
|
||||
let output = async_run_command(&command).await;
|
||||
let command = if access_token.len() > 0 {
|
||||
command.replace(&access_token, "<TOKEN>")
|
||||
let command = if access_token_at.len() > 0 {
|
||||
command.replace(&access_token.unwrap(), "<TOKEN>")
|
||||
} else {
|
||||
command
|
||||
};
|
||||
|
||||
@@ -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.15"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "a client to interact with the monitor system"
|
||||
@@ -9,7 +9,7 @@ license = "GPL-3.0-or-later"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
monitor_types = "0.1.15"
|
||||
monitor_types = "0.2.0"
|
||||
# monitor_types = { path = "../types" }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
tokio-tungstenite = { version = "0.18", features=["native-tls"] }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use anyhow::Context;
|
||||
use monitor_types::{Build, BuildActionState, BuildVersionsReponse, Update};
|
||||
use monitor_types::{AwsBuilderConfig, Build, BuildActionState, BuildVersionsReponse, Update};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use crate::MonitorClient;
|
||||
@@ -14,6 +14,7 @@ impl MonitorClient {
|
||||
pub async fn get_build(&self, build_id: &str) -> anyhow::Result<Build> {
|
||||
self.get(&format!("/api/build/{build_id}"), Option::<()>::None)
|
||||
.await
|
||||
.context(format!("failed at getting build {build_id}"))
|
||||
}
|
||||
|
||||
pub async fn get_build_action_state(&self, build_id: &str) -> anyhow::Result<BuildActionState> {
|
||||
@@ -22,6 +23,9 @@ impl MonitorClient {
|
||||
Option::<()>::None,
|
||||
)
|
||||
.await
|
||||
.context(format!(
|
||||
"failed at getting action state for build {build_id}"
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_build_versions(
|
||||
@@ -37,6 +41,7 @@ impl MonitorClient {
|
||||
json!({ "page": page, "major": major.into(), "minor": minor.into(), "patch": patch.into() }),
|
||||
)
|
||||
.await
|
||||
.context("failed at getting build versions")
|
||||
}
|
||||
|
||||
pub async fn create_build(&self, name: &str, server_id: &str) -> anyhow::Result<Build> {
|
||||
@@ -53,18 +58,16 @@ impl MonitorClient {
|
||||
pub async fn create_full_build(&self, build: &Build) -> anyhow::Result<Build> {
|
||||
self.post::<&Build, _>("/api/build/create_full", build)
|
||||
.await
|
||||
.context(format!("failed at creating full build"))
|
||||
.context(format!(
|
||||
"failed at creating full build with name {}",
|
||||
build.name
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn copy_build(
|
||||
&self,
|
||||
id: &str,
|
||||
new_name: &str,
|
||||
new_server_id: &str,
|
||||
) -> anyhow::Result<Build> {
|
||||
pub async fn copy_build(&self, id: &str, new_name: &str) -> anyhow::Result<Build> {
|
||||
self.post(
|
||||
&format!("/api/build/{id}/copy"),
|
||||
json!({ "name": new_name, "server_id": new_server_id }),
|
||||
json!({ "name": new_name }),
|
||||
)
|
||||
.await
|
||||
.context(format!("failed at copying build {id}"))
|
||||
@@ -88,9 +91,15 @@ 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)
|
||||
pub async fn get_aws_builder_defaults(&self) -> anyhow::Result<AwsBuilderConfig> {
|
||||
self.get("/api/build/aws_builder_defaults", Option::<()>::None)
|
||||
.await
|
||||
.context(format!("failed at recloning build {id}"))
|
||||
.context("failed at getting aws builder defaults")
|
||||
}
|
||||
|
||||
// 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}"))
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ impl MonitorClient {
|
||||
Option::<()>::None,
|
||||
)
|
||||
.await
|
||||
.context("failed at get_deployment_action_state")
|
||||
}
|
||||
|
||||
pub async fn get_deployment_container_log(
|
||||
@@ -56,7 +57,21 @@ impl MonitorClient {
|
||||
Option::<()>::None,
|
||||
)
|
||||
.await
|
||||
.context("failed at get_deployment_container_log")
|
||||
.context("failed at get_deployment_container_stats")
|
||||
}
|
||||
|
||||
pub async fn get_deployment_deployed_version(
|
||||
&self,
|
||||
deployment_id: &str,
|
||||
) -> anyhow::Result<String> {
|
||||
self.get(
|
||||
&format!("/api/deployment/{deployment_id}/deployed_version"),
|
||||
Option::<()>::None,
|
||||
)
|
||||
.await
|
||||
.context(format!(
|
||||
"failed at get_deployment_deployed_version for id {deployment_id}"
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn create_deployment(
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
[package]
|
||||
name = "periphery_client"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
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.15" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
# 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.25"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ impl PeripheryClient {
|
||||
pub fn new(passkey: String) -> PeripheryClient {
|
||||
PeripheryClient {
|
||||
http_client: Default::default(),
|
||||
passkey
|
||||
passkey,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_types"
|
||||
version = "0.1.15"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "types for the mogh tech monitor"
|
||||
|
||||
@@ -32,8 +32,13 @@ 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<AwsBuilderBuildConfig>,
|
||||
|
||||
#[builder(default)]
|
||||
pub version: Version,
|
||||
@@ -51,10 +56,6 @@ pub struct Build {
|
||||
#[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>,
|
||||
|
||||
// build related
|
||||
#[builder(default)]
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
@@ -68,7 +69,7 @@ pub struct Build {
|
||||
#[diff(attr(#[serde(skip_serializing_if = "option_diff_no_change")]))]
|
||||
pub docker_account: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
#[diff(attr(#[serde(skip)]))]
|
||||
#[builder(setter(skip))]
|
||||
pub last_built_at: String,
|
||||
@@ -87,7 +88,6 @@ pub struct Build {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct BuildActionState {
|
||||
pub building: bool,
|
||||
pub recloning: bool,
|
||||
pub updating: bool,
|
||||
}
|
||||
|
||||
@@ -149,3 +149,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 AwsBuilderBuildConfig {
|
||||
#[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>,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{collections::HashMap, net::IpAddr, path::PathBuf};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::Timelength;
|
||||
|
||||
@@ -23,8 +24,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,
|
||||
|
||||
@@ -51,14 +52,16 @@ pub struct CoreConfig {
|
||||
// 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 {
|
||||
@@ -96,6 +99,63 @@ fn default_core_mongo_db_name() -> String {
|
||||
"monitor".to_string()
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AwsBuilderConfig {
|
||||
#[serde(skip_serializing)]
|
||||
pub access_key_id: String,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub secret_access_key: String,
|
||||
|
||||
pub default_ami_id: String,
|
||||
pub default_subnet_id: String,
|
||||
pub default_key_pair_name: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub available_ami_accounts: AvailableAmiAccounts,
|
||||
|
||||
#[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")
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type AvailableAmiAccounts = HashMap<String, AmiAccounts>; // (ami_id, AmiAccounts)
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AmiAccounts {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub github: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub docker: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct PeripheryConfig {
|
||||
#[serde(default = "default_periphery_port")]
|
||||
|
||||
@@ -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,
|
||||
@@ -122,6 +131,7 @@ pub enum Operation {
|
||||
// user
|
||||
ModifyUserEnabled,
|
||||
ModifyUserCreateServerPermissions,
|
||||
ModifyUserCreateBuildPermissions,
|
||||
ModifyUserPermissions,
|
||||
|
||||
// github webhook automation
|
||||
@@ -272,24 +282,3 @@ pub fn unix_from_monitor_ts(ts: &str) -> anyhow::Result<i64> {
|
||||
.context("failed to parse rfc3339 timestamp")?
|
||||
.timestamp_millis())
|
||||
}
|
||||
|
||||
// pub mod i64_to_str {
|
||||
// use serde::{Deserializer, Serializer};
|
||||
// pub fn serialize<S>(t: &i64, s: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
// s.serialize_str(&t.to_string())
|
||||
// }
|
||||
// pub fn deserialize<'de, D>(d: D) -> Result<i64, D::Error> where D: Deserializer<'de> {
|
||||
// let str = d.deserialize_str(StrVisitor)
|
||||
// }
|
||||
// }
|
||||
|
||||
// struct StrVisitor;
|
||||
|
||||
// impl<'de> Visitor<'de> for StrVisitor {
|
||||
// type Value = &'de str;
|
||||
|
||||
// fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
// formatter.write_str("a json string value")
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
@@ -74,7 +74,6 @@ pub enum ProcedureOperation {
|
||||
|
||||
// build
|
||||
BuildBuild,
|
||||
RecloneBuild,
|
||||
|
||||
// deployment
|
||||
DeployContainer,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
Build, BuildActionState, Deployment, DeploymentActionState, Group, PermissionLevel,
|
||||
Build, BuildActionState, CloneArgs, Deployment, DeploymentActionState, Group, PermissionLevel,
|
||||
PermissionsMap, Procedure, Server, ServerActionState,
|
||||
};
|
||||
|
||||
@@ -65,6 +65,32 @@ impl Busy for DeploymentActionState {
|
||||
|
||||
impl Busy for BuildActionState {
|
||||
fn busy(&self) -> bool {
|
||||
self.building || self.recloning || self.updating
|
||||
self.building || 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.15"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["MoghTech"]
|
||||
description = "monitor periphery binary | run monitor periphery as system daemon"
|
||||
@@ -13,10 +13,8 @@ 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.15" }
|
||||
types = { package = "monitor_types", version = "0.1.15" }
|
||||
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.25", features = ["full"] }
|
||||
@@ -29,7 +27,7 @@ serde_json = "1.0"
|
||||
bollard = "0.13"
|
||||
anyhow = "1.0"
|
||||
envy = "0.4"
|
||||
sysinfo = "0.27.7"
|
||||
sysinfo = "0.28"
|
||||
toml = "0.7"
|
||||
daemonize = "0.4"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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,
|
||||
AwsBuilderBuildConfig, AwsBuilderConfig, 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,27 @@ 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_monitor".to_string())
|
||||
.repo(Some(String::from("mbecker20/monitor")))
|
||||
.branch(Some(String::from("main")))
|
||||
.docker_account(Some(String::from("mbecker2020")))
|
||||
.docker_build_args(
|
||||
DockerBuildArgsBuilder::default()
|
||||
.build_path(".".to_string())
|
||||
.dockerfile_path("Dockerfile.core".to_string().into())
|
||||
.build()
|
||||
.context("failed to construct DockerBuildArgs struct")?
|
||||
.into(),
|
||||
)
|
||||
.aws_config(AwsBuilderBuildConfig::default().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