forked from github-starred/komodo
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c38849961e | ||
|
|
3acfa0c4b1 | ||
|
|
62b083c3be | ||
|
|
ee6fc4c590 | ||
|
|
4fa550b3d3 | ||
|
|
1c44ae98fb | ||
|
|
148223f995 | ||
|
|
4678f83542 | ||
|
|
340bac078f | ||
|
|
3f8e75bbd8 | ||
|
|
c4278d14a9 | ||
|
|
4909106c3c | ||
|
|
0f04a2848a | ||
|
|
ba073bf8b2 | ||
|
|
640809aa6b | ||
|
|
943bb4c61a | ||
|
|
ef43cb7920 | ||
|
|
1abe634679 | ||
|
|
18ab18b6f6 | ||
|
|
aad971a599 | ||
|
|
3d9da97d7b | ||
|
|
2d0a09f760 | ||
|
|
568c317d6d | ||
|
|
f0e1f253f4 | ||
|
|
7634b9fa1d | ||
|
|
2e1795eba6 | ||
|
|
0f6b3d6e9b | ||
|
|
a70afcc461 | ||
|
|
d78fc2b282 | ||
|
|
249610afce | ||
|
|
b93b639b40 | ||
|
|
13ceb55fe8 | ||
|
|
7ebd38d350 | ||
|
|
c12c74005e | ||
|
|
c44dc1c6f6 | ||
|
|
cd2b9ec4ed | ||
|
|
dc62442c00 | ||
|
|
81a90f7ae4 | ||
|
|
af092e0d88 | ||
|
|
0e70015fd1 | ||
|
|
a214deef86 | ||
|
|
0efb2966b0 | ||
|
|
1e8422a506 | ||
|
|
7bb386e6d0 | ||
|
|
c464ca5612 | ||
|
|
742630fdee | ||
|
|
154fd899fe | ||
|
|
aa25f9d4c9 | ||
|
|
eeed94c8fd | ||
|
|
b2f771199d | ||
|
|
4bceb97a66 | ||
|
|
8734e6fc4c |
Generated
+92
-81
@@ -32,7 +32,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "alert_logger"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.5",
|
||||
@@ -111,15 +111,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.82"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5"
|
||||
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -180,9 +180,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2a89e0000cde82447155d64eeb71720b933b4396a6fbbebad3f8b4f88ca7b54"
|
||||
checksum = "baaa0be6ee7d90b775ae6ccb6d2ba182b91219ec2001f92338773a094246af1d"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -223,9 +223,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-runtime"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4963ac9ff2d33a4231b3806c1c69f578f221a9cabb89ad2bde62ce2b442c8a7"
|
||||
checksum = "785da4a15e7b166b505fd577e4560c7a7cd8fbdf842eb1336cbcbf8944ce56f1"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-sigv4",
|
||||
@@ -246,9 +246,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ec2"
|
||||
version = "1.34.0"
|
||||
version = "1.40.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b58453a3033a5c2e1631abec61b6915c507a726d628a0c2d7b81578d81be1b3"
|
||||
checksum = "a449fb7b9cab253e5ad0e6173188b2c5103e62183b1c099ec610fafb498dbe7c"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -270,9 +270,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.20.0"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32fcc572fd5c58489ec205ec3e4e5f7d63018898a485cbf922a462af496bc300"
|
||||
checksum = "ca3d6c4cba4e009391b72b0fcf12aff04ea3c9c3aa2ecaafa330326a8bd7e601"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -292,9 +292,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.20.0"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b6275fa8684a1192754221173b1f7a7c1260d6b0571cc2b8af09468eb0cffe5"
|
||||
checksum = "73400dc239d14f63d932f4ca7b55af5e9ef1f857f7d70655249ccc287adb2570"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -314,9 +314,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.20.0"
|
||||
version = "1.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30acd58272fd567e4853c5075d838be1626b59057e0249c9be5a1a7eb13bf70f"
|
||||
checksum = "10f8858308af76fba3e5ffcf1bb56af5471574d2bdfaf0159470c25bc2f760e5"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -337,9 +337,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sigv4"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d6f29688a4be9895c0ba8bef861ad0c0dac5c15e9618b9b7a6c233990fc263"
|
||||
checksum = "58b56f1cbe6fd4d0c2573df72868f20ab1c125ca9c9dbce17927a463433a2e57"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-smithy-http",
|
||||
@@ -371,9 +371,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-http"
|
||||
version = "0.60.7"
|
||||
version = "0.60.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f10fa66956f01540051b0aa7ad54574640f748f9839e843442d99b970d3aff9"
|
||||
checksum = "4a7de001a1b9a25601016d8057ea16e31a45fdca3751304c8edf4ad72e706c08"
|
||||
dependencies = [
|
||||
"aws-smithy-runtime-api",
|
||||
"aws-smithy-types",
|
||||
@@ -410,9 +410,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-runtime"
|
||||
version = "1.3.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de34bcfa1fb3c82a80e252a753db34a6658e07f23d3a5b3fc96919518fa7a3f5"
|
||||
checksum = "c9ac79e9f3a4d576f3cd4a470a0275b138d9e7b11b1cd514a6858ae0a79dd5bb"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-http",
|
||||
@@ -436,9 +436,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-runtime-api"
|
||||
version = "1.4.0"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cc56a5c96ec741de6c5e6bf1ce6948be969d6506dfa9c39cffc284e31e4979b"
|
||||
checksum = "04ec42c2f5c0e7796a2848dde4d9f3bf8ce12ccbb3d5aa40c52fa0cdd61a1c47"
|
||||
dependencies = [
|
||||
"aws-smithy-async",
|
||||
"aws-smithy-types",
|
||||
@@ -453,9 +453,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-types"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abe14dceea1e70101d38fbf2a99e6a34159477c0fb95e68e05c66bd7ae4c3729"
|
||||
checksum = "baf98d97bba6ddaba180f1b1147e202d8fe04940403a95a3f826c790f931bbd1"
|
||||
dependencies = [
|
||||
"base64-simd",
|
||||
"bytes",
|
||||
@@ -479,9 +479,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-smithy-xml"
|
||||
version = "0.60.7"
|
||||
version = "0.60.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "872c68cf019c0e4afc5de7753c4f7288ce4b71663212771bf5e4542eb9346ca9"
|
||||
checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55"
|
||||
dependencies = [
|
||||
"xmlparser",
|
||||
]
|
||||
@@ -920,6 +920,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
@@ -2007,7 +2017,7 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "logger"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"monitor_client",
|
||||
@@ -2084,7 +2094,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "migrator"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -2210,9 +2220,27 @@ dependencies = [
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_cli"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"colored",
|
||||
"futures",
|
||||
"monitor_client",
|
||||
"partial_derive2",
|
||||
"serde",
|
||||
"strum 0.26.2",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monitor_client"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2227,7 +2255,7 @@ dependencies = [
|
||||
"mongo_indexed",
|
||||
"mungos",
|
||||
"partial_derive2",
|
||||
"reqwest 0.12.3",
|
||||
"reqwest 0.12.4",
|
||||
"resolver_api",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2244,7 +2272,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_core"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -2267,9 +2295,10 @@ dependencies = [
|
||||
"monitor_client",
|
||||
"mungos",
|
||||
"parse_csl",
|
||||
"partial_derive2",
|
||||
"periphery_client",
|
||||
"rand",
|
||||
"reqwest 0.12.3",
|
||||
"reqwest 0.12.4",
|
||||
"resolver_api",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2290,7 +2319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_periphery"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -2321,7 +2350,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "monitor_sync"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2332,23 +2361,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monrun"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"futures",
|
||||
"monitor_client",
|
||||
"partial_derive2",
|
||||
"serde",
|
||||
"strum 0.26.2",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mungos"
|
||||
version = "0.5.4"
|
||||
@@ -2614,18 +2626,18 @@ checksum = "ffa94c2e5674923c67d7f3dfce1279507b191e10eb064881b46ed3e1256e5ca6"
|
||||
|
||||
[[package]]
|
||||
name = "partial_derive2"
|
||||
version = "0.3.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "258bf4a15b30c642aa810f6a33cc8c0dfddd2fa46918ca068fde958882d6b18b"
|
||||
checksum = "57fd88f09814d56f73ea68256ebdd2cdd75a4a3b48d41c63bbc27c46c6733588"
|
||||
dependencies = [
|
||||
"partial_derive2_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "partial_derive2_derive"
|
||||
version = "0.3.2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e49f7410574cdfb56362c529cb2893a246e147072d5e993c46341f02891f9cd1"
|
||||
checksum = "bdc7cb36e2cd4e6e7a53e3d35fd3321e1bd83382e0207901bff7ffb779cf7619"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2649,11 +2661,11 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"monitor_client",
|
||||
"reqwest 0.12.3",
|
||||
"reqwest 0.12.4",
|
||||
"resolver_api",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -2865,9 +2877,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.3"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e6cc1e89e689536eb5aeede61520e874df5a4707df811cd5da4aa5fbb2aae19"
|
||||
checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10"
|
||||
dependencies = [
|
||||
"base64 0.22.0",
|
||||
"bytes",
|
||||
@@ -3168,9 +3180,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.198"
|
||||
version = "1.0.201"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
|
||||
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -3186,9 +3198,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.198"
|
||||
version = "1.0.201"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
|
||||
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3573,9 +3585,9 @@ checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.30.11"
|
||||
version = "0.30.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87341a165d73787554941cd5ef55ad728011566fe714e987d1b976c15dbc3a83"
|
||||
checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
@@ -3648,7 +3660,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tests"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dotenv",
|
||||
@@ -3666,18 +3678,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.58"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.58"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3832,9 +3844,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -3842,7 +3854,6 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4141,9 +4152,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "typeshare"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99877a66770b25072a67101e80790fd7fe9ed9eff8c69ffb81e9bf5cbea7733f"
|
||||
checksum = "04f17399b76c2e743d58eac0635d7686e9c00f48cd4776f00695d9882a7d3187"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"serde",
|
||||
@@ -4153,9 +4164,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typeshare-annotation"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecce25dea8aeaadc44909f4c1226d22d84512fccd07d22447ecbad176bc09545"
|
||||
checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.53",
|
||||
@@ -4205,7 +4216,7 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "update_logger"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"logger",
|
||||
|
||||
+13
-13
@@ -3,14 +3,13 @@ resolver = "2"
|
||||
members = ["bin/*", "lib/*", "client/core/rs", "client/periphery/rs"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
edition = "2021"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[workspace.dependencies]
|
||||
# LOCAL
|
||||
monitor_macros = { path = "lib/macros" }
|
||||
monitor_client = { path = "client/core/rs" }
|
||||
periphery_client = { path = "client/periphery/rs" }
|
||||
logger = { path = "lib/logger" }
|
||||
@@ -24,7 +23,7 @@ derive_empty_traits = "0.1.0"
|
||||
merge_config_files = "0.1.5"
|
||||
termination_signal = "0.1.3"
|
||||
async_timing_util = "0.1.14"
|
||||
partial_derive2 = "0.3.2"
|
||||
partial_derive2 = "0.4.1"
|
||||
derive_variants = "0.1.3"
|
||||
mongo_indexed = "0.2.2"
|
||||
resolver_api = "0.1.9"
|
||||
@@ -34,12 +33,12 @@ svi = "0.1.4"
|
||||
|
||||
# ASYNC
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
reqwest = { version = "0.12.3", features = ["json"] }
|
||||
tokio-util = "0.7.10"
|
||||
reqwest = { version = "0.12.4", features = ["json"] }
|
||||
tokio-util = "0.7.11"
|
||||
futures = "0.3.30"
|
||||
futures-util = "0.3.30"
|
||||
async-trait = "0.1.80"
|
||||
async-recursion = "1.1.0"
|
||||
async-recursion = "1.1.1"
|
||||
|
||||
# SERVER
|
||||
axum = { version = "0.7.5", features = ["ws", "json"] }
|
||||
@@ -49,14 +48,14 @@ tower-http = { version = "0.5.2", features = ["fs", "cors"] }
|
||||
tokio-tungstenite = "0.21.0"
|
||||
|
||||
# SER/DE
|
||||
serde = { version = "1.0.198", features = ["derive"] }
|
||||
serde = { version = "1.0.201", features = ["derive"] }
|
||||
strum = { version = "0.26.2", features = ["derive"] }
|
||||
serde_json = "1.0.116"
|
||||
toml = "0.8.12"
|
||||
|
||||
# ERROR
|
||||
anyhow = "1.0.82"
|
||||
thiserror = "1.0.58"
|
||||
anyhow = "1.0.83"
|
||||
thiserror = "1.0.60"
|
||||
|
||||
# LOGGING
|
||||
opentelemetry_sdk = { version = "0.22.1", features = ["rt-tokio"] }
|
||||
@@ -83,12 +82,13 @@ hex = "0.4.3"
|
||||
|
||||
# SYSTEM
|
||||
bollard = "0.16.1"
|
||||
sysinfo = "0.30.11"
|
||||
sysinfo = "0.30.12"
|
||||
|
||||
# CLOUD
|
||||
aws-config = "1.2.0"
|
||||
aws-sdk-ec2 = "1.34.0"
|
||||
aws-config = "1.3.0"
|
||||
aws-sdk-ec2 = "1.40.0"
|
||||
|
||||
# MISC
|
||||
derive_builder = "0.20.0"
|
||||
typeshare = "1.0.2"
|
||||
colored = "2.1.0"
|
||||
typeshare = "1.0.3"
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
[package]
|
||||
name = "monrun"
|
||||
name = "monitor_cli"
|
||||
description = "Command line tool to sync monitor resources and execute file defined procedures"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "monitor"
|
||||
path = "src/main.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
@@ -13,6 +18,7 @@ monitor_client.workspace = true
|
||||
# mogh
|
||||
partial_derive2.workspace = true
|
||||
# external
|
||||
colored.workspace = true
|
||||
anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
serde.workspace = true
|
||||
@@ -0,0 +1,21 @@
|
||||
# monitor CLI
|
||||
|
||||
Monitor CLI is a command line tool to sync monitor resources and execute file defined procedures.
|
||||
|
||||
## Examples
|
||||
|
||||
```sh
|
||||
## Sync resources in a single file
|
||||
monitor sync ./resources/deployments.toml
|
||||
|
||||
## Sync resources gathered across multiple files in a directory
|
||||
monitor sync ./resources
|
||||
|
||||
## Path defaults to './resources', in this case you can just use:
|
||||
monitor sync
|
||||
```
|
||||
|
||||
```sh
|
||||
## Execute a TOML defined procedure
|
||||
monitor exec ./execution/execution.toml
|
||||
```
|
||||
@@ -0,0 +1,49 @@
|
||||
[[build]]
|
||||
name = "monitor_core"
|
||||
description = "Public monitor core build"
|
||||
tags = ["monitor"]
|
||||
|
||||
[build.config]
|
||||
builder_id = "mogh-builder"
|
||||
repo = "mbecker20/monitor"
|
||||
branch = "main"
|
||||
docker_account = "mbecker2020"
|
||||
build_path = "."
|
||||
dockerfile_path = "bin/core/Dockerfile"
|
||||
|
||||
[[build]]
|
||||
name = "monitor_core_dev"
|
||||
description = ""
|
||||
tags = ["monitor", "dev"]
|
||||
|
||||
[build.config]
|
||||
builder_id = "mogh-builder"
|
||||
repo = "mbecker20/monitor"
|
||||
branch = "main"
|
||||
docker_account = "mbecker2020"
|
||||
build_path = "."
|
||||
dockerfile_path = "bin/core/Dockerfile"
|
||||
|
||||
## BUILDER
|
||||
|
||||
[[builder]]
|
||||
name = "mogh-builder"
|
||||
description = ""
|
||||
tags = []
|
||||
|
||||
[builder.config]
|
||||
type = "Aws"
|
||||
|
||||
[builder.config.params]
|
||||
region = "us-east-2"
|
||||
instance_type = "c5.2xlarge"
|
||||
volume_gb = 20
|
||||
port = 8120
|
||||
ami_id = "ami-0005a05fa63a080ab"
|
||||
subnet_id = "subnet-02ae5ad480eacc4bc"
|
||||
security_group_ids = ["sg-049d98c819f9ace58", "sg-006c0ca638af8eb44"]
|
||||
key_pair_name = "mogh-key"
|
||||
assign_public_ip = true
|
||||
use_public_ip = false
|
||||
github_accounts = []
|
||||
docker_accounts = []
|
||||
@@ -0,0 +1,181 @@
|
||||
## MONITOR PROXY
|
||||
[[deployment]]
|
||||
name = "monitor-proxy"
|
||||
description = "An NGINX proxy for mogh.tech"
|
||||
tags = ["monitor"]
|
||||
config.server_id = "monitor-01"
|
||||
config.network = "host"
|
||||
config.restart = "on-failure"
|
||||
config.image.type = "Image"
|
||||
config.image.params.image = "jc21/nginx-proxy-manager"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/data/nginx/data"
|
||||
container = "/data"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/data/nginx/letsencrypt"
|
||||
container = "/etc/letsencrypt"
|
||||
|
||||
## MONITOR MONGO
|
||||
[[deployment]]
|
||||
name = "monitor-mongo"
|
||||
description = ""
|
||||
tags = ["monitor"]
|
||||
|
||||
[deployment.config]
|
||||
server_id = "monitor-01"
|
||||
network = "host"
|
||||
restart = "no"
|
||||
|
||||
[deployment.config.image]
|
||||
type = "Image"
|
||||
params.image = "mongo"
|
||||
|
||||
## MONITOR CORE
|
||||
[[deployment]]
|
||||
name = "monitor-core"
|
||||
description = ""
|
||||
tags = ["monitor"]
|
||||
|
||||
[deployment.config]
|
||||
server_id = "monitor-01"
|
||||
network = "host"
|
||||
restart = "no"
|
||||
|
||||
[deployment.config.image]
|
||||
type = "Image"
|
||||
params.image = "mbecker2020/monitor_core"
|
||||
|
||||
## GRAFANA
|
||||
[[deployment]]
|
||||
name = "grafana"
|
||||
description = ""
|
||||
tags = ["logging"]
|
||||
|
||||
[deployment.config]
|
||||
server_id = "monitor-01"
|
||||
network = "host"
|
||||
restart = "unless-stopped"
|
||||
extra_args = ["--user root"]
|
||||
|
||||
[deployment.config.image]
|
||||
type = "Image"
|
||||
params.image = "grafana/grafana"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/data/grafana"
|
||||
container = "/var/lib/grafana"
|
||||
|
||||
[[deployment.config.environment]]
|
||||
variable = "GF_SERVER_HTTP_PORT"
|
||||
value = "3080"
|
||||
|
||||
[[deployment.config.labels]]
|
||||
variable = "vector"
|
||||
value = "key-value"
|
||||
|
||||
## LOKI
|
||||
[[deployment]]
|
||||
name = "loki"
|
||||
description = ""
|
||||
tags = ["logging"]
|
||||
|
||||
[deployment.config]
|
||||
server_id = "monitor-01"
|
||||
network = "host"
|
||||
restart = "unless-stopped"
|
||||
extra_args = ["--user root"]
|
||||
|
||||
[deployment.config.image]
|
||||
type = "Image"
|
||||
params.image = "grafana/loki"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/data/loki"
|
||||
container = "/loki"
|
||||
|
||||
[[deployment]]
|
||||
name = "tempo"
|
||||
description = ""
|
||||
tags = ["logging"]
|
||||
|
||||
[deployment.config]
|
||||
server_id = "monitor-01"
|
||||
network = "host"
|
||||
restart = "unless-stopped"
|
||||
command = "-server.http-listen-port=3200 -server.grpc-listen-port=9096 --storage.trace.backend=local --storage.trace.local.path=/tmp/tempo/traces --storage.trace.wal.path=/tmp/tempo/wal"
|
||||
extra_args = ["--user root"]
|
||||
|
||||
[deployment.config.image]
|
||||
type = "Image"
|
||||
params.image = "grafana/tempo"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/data/tempo"
|
||||
container = "/tmp/tempo"
|
||||
|
||||
[[deployment.config.labels]]
|
||||
variable = "vector"
|
||||
value = "key-value"
|
||||
|
||||
## VECTOR
|
||||
[[deployment]]
|
||||
name = "vector"
|
||||
description = ""
|
||||
tags = ["logging"]
|
||||
|
||||
[deployment.config]
|
||||
server_id = "monitor-01"
|
||||
network = "host"
|
||||
restart = "unless-stopped"
|
||||
command = "--config /etc/vector/*.toml"
|
||||
extra_args = ["--user root"]
|
||||
|
||||
[deployment.config.image]
|
||||
type = "Image"
|
||||
params.image = "timberio/vector:latest-debian"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/home/ubuntu/.config/vector"
|
||||
container = "/etc/vector"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/data/vector"
|
||||
container = "/var/lib/vector"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/var/run/docker.sock"
|
||||
container = "/var/run/docker.sock"
|
||||
|
||||
[[deployment.config.labels]]
|
||||
variable = "vector"
|
||||
value = "key-value"
|
||||
|
||||
## MONITOR CORE DEV
|
||||
[[deployment]]
|
||||
name = "monitor-core-dev"
|
||||
description = ""
|
||||
tags = ["monitor", "dev"]
|
||||
|
||||
[deployment.config]
|
||||
server_id = "monitor-01"
|
||||
redeploy_on_build = true
|
||||
network = "host"
|
||||
restart = "no"
|
||||
|
||||
[deployment.config.image]
|
||||
type = "Build"
|
||||
params.build_id = "monitor_core"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/home/ubuntu/.config/monitor/dev.core.config.toml"
|
||||
container = "/config/config.toml"
|
||||
|
||||
[[deployment.config.volumes]]
|
||||
local = "/etc/monitor/repos/monitor-dev-frontend/frontend/dist"
|
||||
container = "/frontend"
|
||||
|
||||
[[deployment.config.labels]]
|
||||
variable = "vector"
|
||||
value = "rust"
|
||||
@@ -0,0 +1,8 @@
|
||||
[[procedure]]
|
||||
name = "test-procedure"
|
||||
description = ""
|
||||
tags = []
|
||||
|
||||
[procedure.config]
|
||||
procedure_type = "Sequence"
|
||||
executions = []
|
||||
@@ -0,0 +1,18 @@
|
||||
[[repo]]
|
||||
name = "monitor-dev-frontend"
|
||||
description = "Used as frontend for monitor-core-dev"
|
||||
tags = ["monitor", "dev"]
|
||||
|
||||
[repo.config]
|
||||
server_id = "monitor-01"
|
||||
repo = "mbecker20/monitor"
|
||||
branch = "main"
|
||||
github_account = ""
|
||||
|
||||
[repo.config.on_clone]
|
||||
path = ""
|
||||
command = ""
|
||||
|
||||
[repo.config.on_pull]
|
||||
path = "frontend"
|
||||
command = "sh on_pull.sh"
|
||||
@@ -0,0 +1,51 @@
|
||||
[[server]]
|
||||
name = "monitor-01"
|
||||
description = ""
|
||||
tags = ["monitor"]
|
||||
|
||||
[server.config]
|
||||
address = "http://localhost:8120"
|
||||
enabled = true
|
||||
stats_monitoring = true
|
||||
auto_prune = true
|
||||
send_unreachable_alerts = true
|
||||
send_cpu_alerts = true
|
||||
send_mem_alerts = true
|
||||
send_disk_alerts = true
|
||||
region = "us-east-2"
|
||||
|
||||
## TEMPLATE
|
||||
|
||||
[[server_template]]
|
||||
name = "mogh-template"
|
||||
description = ""
|
||||
tags = []
|
||||
|
||||
[server_template.config]
|
||||
type = "Aws"
|
||||
|
||||
[server_template.config.params]
|
||||
region = "us-east-2"
|
||||
instance_type = "t3.medium"
|
||||
ami_id = "ami-0005a05fa63a080ab"
|
||||
subnet_id = "subnet-02ae5ad480eacc4bc"
|
||||
key_pair_name = "mogh-key"
|
||||
assign_public_ip = true
|
||||
use_public_ip = false
|
||||
port = 8120
|
||||
user_data = ""
|
||||
security_group_ids = ["sg-049d98c819f9ace58", "sg-006c0ca638af8eb44"]
|
||||
|
||||
[[server_template.config.params.volumes]]
|
||||
device_name = "/dev/sda1"
|
||||
size_gb = 20
|
||||
volume_type = "gp2"
|
||||
iops = 0
|
||||
throughput = 0
|
||||
|
||||
[[server_template.config.params.volumes]]
|
||||
device_name = "/dev/sdb"
|
||||
size_gb = 10
|
||||
volume_type = "gp3"
|
||||
iops = 0
|
||||
throughput = 0
|
||||
@@ -5,6 +5,7 @@ use std::{io::Read, path::PathBuf, str::FromStr, sync::OnceLock};
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::{Parser, Subcommand};
|
||||
use colored::Colorize;
|
||||
use monitor_client::{api::read, MonitorClient};
|
||||
use serde::{de::DeserializeOwned, Deserialize};
|
||||
|
||||
@@ -20,12 +21,15 @@ fn cli_args() -> &'static CliArgs {
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct CliArgs {
|
||||
/// Sync or Exec
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
/// The path to a creds file.
|
||||
#[arg(long, default_value_t = String::from("./creds.toml"))]
|
||||
creds: String,
|
||||
/// Log less (just resource names).
|
||||
#[arg(long, default_value_t = false)]
|
||||
verbose: bool,
|
||||
quiet: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Subcommand)]
|
||||
@@ -89,8 +93,8 @@ fn parse_toml_file<T: DeserializeOwned>(
|
||||
toml::from_str(&contents).context("failed to parse toml contents")
|
||||
}
|
||||
|
||||
fn wait_for_enter(message: &str) -> anyhow::Result<()> {
|
||||
println!("\nPress ENTER to {message}\n");
|
||||
fn wait_for_enter(press_enter_to: &str) -> anyhow::Result<()> {
|
||||
println!("\nPress {} to {}\n", "ENTER".green(), press_enter_to.bold());
|
||||
let buffer = &mut [0u8];
|
||||
std::io::stdin()
|
||||
.read_exact(buffer)
|
||||
@@ -7,7 +7,7 @@ use monitor_client::{
|
||||
builder::BuilderListItem, deployment::DeploymentListItem,
|
||||
procedure::ProcedureListItem, repo::RepoListItem,
|
||||
server::ServerListItem, server_template::ServerTemplateListItem,
|
||||
user::User, user_group::UserGroup,
|
||||
tag::Tag, user::User, user_group::UserGroup,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -219,6 +219,22 @@ pub fn id_to_procedure() -> &'static HashMap<String, ProcedureListItem>
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name_to_server_template(
|
||||
) -> &'static HashMap<String, ServerTemplateListItem> {
|
||||
static NAME_TO_SERVER_TEMPLATE: OnceLock<
|
||||
HashMap<String, ServerTemplateListItem>,
|
||||
> = OnceLock::new();
|
||||
NAME_TO_SERVER_TEMPLATE.get_or_init(|| {
|
||||
futures::executor::block_on(
|
||||
monitor_client().read(read::ListServerTemplates::default()),
|
||||
)
|
||||
.expect("failed to get server templates from monitor")
|
||||
.into_iter()
|
||||
.map(|procedure| (procedure.name.clone(), procedure))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn id_to_server_template(
|
||||
) -> &'static HashMap<String, ServerTemplateListItem> {
|
||||
static ID_TO_SERVER_TEMPLATE: OnceLock<
|
||||
@@ -228,7 +244,7 @@ pub fn id_to_server_template(
|
||||
futures::executor::block_on(
|
||||
monitor_client().read(read::ListServerTemplates::default()),
|
||||
)
|
||||
.expect("failed to get procedures from monitor")
|
||||
.expect("failed to get server templates from monitor")
|
||||
.into_iter()
|
||||
.map(|procedure| (procedure.id.clone(), procedure))
|
||||
.collect()
|
||||
@@ -242,7 +258,7 @@ pub fn name_to_user_group() -> &'static HashMap<String, UserGroup> {
|
||||
futures::executor::block_on(
|
||||
monitor_client().read(read::ListUserGroups::default()),
|
||||
)
|
||||
.expect("failed to get procedures from monitor")
|
||||
.expect("failed to get user groups from monitor")
|
||||
.into_iter()
|
||||
.map(|user_group| (user_group.name.clone(), user_group))
|
||||
.collect()
|
||||
@@ -256,9 +272,22 @@ pub fn id_to_user() -> &'static HashMap<String, User> {
|
||||
futures::executor::block_on(
|
||||
monitor_client().read(read::ListUsers::default()),
|
||||
)
|
||||
.expect("failed to get procedures from monitor")
|
||||
.expect("failed to get users from monitor")
|
||||
.into_iter()
|
||||
.map(|user| (user.id.clone(), user))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn id_to_tag() -> &'static HashMap<String, Tag> {
|
||||
static ID_TO_TAG: OnceLock<HashMap<String, Tag>> = OnceLock::new();
|
||||
ID_TO_TAG.get_or_init(|| {
|
||||
futures::executor::block_on(
|
||||
monitor_client().read(read::ListTags::default()),
|
||||
)
|
||||
.expect("failed to get tags from monitor")
|
||||
.into_iter()
|
||||
.map(|tag| (tag.id.clone(), tag))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use std::path::Path;
|
||||
use monitor_client::entities::{
|
||||
alerter::Alerter, build::Build, builder::Builder,
|
||||
deployment::Deployment, procedure::Procedure, repo::Repo,
|
||||
server::Server,
|
||||
server::Server, server_template::ServerTemplate,
|
||||
};
|
||||
|
||||
use crate::{sync::resources::ResourceSync, wait_for_enter};
|
||||
@@ -17,8 +17,10 @@ pub async fn run_sync(path: &Path) -> anyhow::Result<()> {
|
||||
|
||||
let resources = resource_file::read_resources(path)?;
|
||||
|
||||
println!("{resources:#?}");
|
||||
info!("computing sync actions...");
|
||||
|
||||
let (server_template_creates, server_template_updates) =
|
||||
ServerTemplate::get_updates(resources.server_templates).await?;
|
||||
let (server_creates, server_updates) =
|
||||
Server::get_updates(resources.servers).await?;
|
||||
let (deployment_creates, deployment_updates) =
|
||||
@@ -36,9 +38,37 @@ pub async fn run_sync(path: &Path) -> anyhow::Result<()> {
|
||||
let (user_group_creates, user_group_updates) =
|
||||
user_group::get_updates(resources.user_groups).await?;
|
||||
|
||||
wait_for_enter("CONTINUE")?;
|
||||
if server_template_creates.is_empty()
|
||||
&& server_template_updates.is_empty()
|
||||
&& server_creates.is_empty()
|
||||
&& server_updates.is_empty()
|
||||
&& deployment_creates.is_empty()
|
||||
&& deployment_updates.is_empty()
|
||||
&& build_creates.is_empty()
|
||||
&& build_updates.is_empty()
|
||||
&& builder_creates.is_empty()
|
||||
&& builder_updates.is_empty()
|
||||
&& alerter_creates.is_empty()
|
||||
&& alerter_updates.is_empty()
|
||||
&& repo_creates.is_empty()
|
||||
&& repo_updates.is_empty()
|
||||
&& procedure_creates.is_empty()
|
||||
&& procedure_updates.is_empty()
|
||||
&& user_group_creates.is_empty()
|
||||
&& user_group_updates.is_empty()
|
||||
{
|
||||
info!("nothing to do. exiting.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
wait_for_enter("run sync")?;
|
||||
|
||||
// No deps
|
||||
ServerTemplate::run_updates(
|
||||
server_template_creates,
|
||||
server_template_updates,
|
||||
)
|
||||
.await;
|
||||
Server::run_updates(server_creates, server_updates).await;
|
||||
Alerter::run_updates(alerter_creates, alerter_updates).await;
|
||||
|
||||
@@ -31,6 +31,7 @@ fn read_resources_recursive(
|
||||
}
|
||||
};
|
||||
info!("adding resources from {path:?}");
|
||||
resources.server_templates.extend(more.server_templates);
|
||||
resources.servers.extend(more.servers);
|
||||
resources.builds.extend(more.builds);
|
||||
resources.deployments.extend(more.deployments);
|
||||
@@ -7,8 +7,7 @@ use monitor_client::{
|
||||
},
|
||||
entities::{
|
||||
alerter::{
|
||||
Alerter, AlerterConfig, AlerterInfo, AlerterListItemInfo,
|
||||
PartialAlerterConfig,
|
||||
Alerter, AlerterConfig, AlerterConfigDiff, AlerterInfo, AlerterListItemInfo, PartialAlerterConfig
|
||||
},
|
||||
resource::{Resource, ResourceListItem},
|
||||
toml::ResourceToml,
|
||||
@@ -22,9 +21,10 @@ use crate::{maps::name_to_alerter, monitor_client};
|
||||
use super::ResourceSync;
|
||||
|
||||
impl ResourceSync for Alerter {
|
||||
type Config = AlerterConfig;
|
||||
type Info = AlerterInfo;
|
||||
type PartialConfig = PartialAlerterConfig;
|
||||
type FullConfig = AlerterConfig;
|
||||
type FullInfo = AlerterInfo;
|
||||
type ConfigDiff = AlerterConfigDiff;
|
||||
type ListItemInfo = AlerterListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
@@ -68,14 +68,14 @@ impl ResourceSync for Alerter {
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>> {
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client().read(GetAlerter { alerter: id }).await
|
||||
}
|
||||
|
||||
async fn minimize_update(
|
||||
original: Self::FullConfig,
|
||||
async fn get_diff(
|
||||
original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig> {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
Ok(original.partial_diff(update))
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ use monitor_client::{
|
||||
},
|
||||
entities::{
|
||||
build::{
|
||||
Build, BuildConfig, BuildInfo, BuildListItemInfo,
|
||||
PartialBuildConfig,
|
||||
Build, BuildConfig, BuildConfigDiff, BuildInfo,
|
||||
BuildListItemInfo, PartialBuildConfig,
|
||||
},
|
||||
resource::{Resource, ResourceListItem},
|
||||
toml::ResourceToml,
|
||||
@@ -25,9 +25,10 @@ use crate::{
|
||||
use super::ResourceSync;
|
||||
|
||||
impl ResourceSync for Build {
|
||||
type Config = BuildConfig;
|
||||
type Info = BuildInfo;
|
||||
type PartialConfig = PartialBuildConfig;
|
||||
type FullConfig = BuildConfig;
|
||||
type FullInfo = BuildInfo;
|
||||
type ConfigDiff = BuildConfigDiff;
|
||||
type ListItemInfo = BuildListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
@@ -71,14 +72,14 @@ impl ResourceSync for Build {
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>> {
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client().read(GetBuild { build: id }).await
|
||||
}
|
||||
|
||||
async fn minimize_update(
|
||||
mut original: Self::FullConfig,
|
||||
async fn get_diff(
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig> {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
// need to replace the builder id with name
|
||||
original.builder_id = id_to_builder()
|
||||
.get(&original.builder_id)
|
||||
@@ -7,7 +7,7 @@ use monitor_client::{
|
||||
},
|
||||
entities::{
|
||||
builder::{
|
||||
Builder, BuilderConfig, BuilderListItemInfo,
|
||||
Builder, BuilderConfig, BuilderConfigDiff, BuilderListItemInfo,
|
||||
PartialBuilderConfig,
|
||||
},
|
||||
resource::{Resource, ResourceListItem},
|
||||
@@ -25,9 +25,10 @@ use crate::{
|
||||
use super::ResourceSync;
|
||||
|
||||
impl ResourceSync for Builder {
|
||||
type Config = BuilderConfig;
|
||||
type Info = ();
|
||||
type PartialConfig = PartialBuilderConfig;
|
||||
type FullConfig = BuilderConfig;
|
||||
type FullInfo = ();
|
||||
type ConfigDiff = BuilderConfigDiff;
|
||||
type ListItemInfo = BuilderListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
@@ -71,14 +72,14 @@ impl ResourceSync for Builder {
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>> {
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client().read(GetBuilder { builder: id }).await
|
||||
}
|
||||
|
||||
async fn minimize_update(
|
||||
mut original: Self::FullConfig,
|
||||
async fn get_diff(
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig> {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
// need to replace server builder id with name
|
||||
if let BuilderConfig::Server(config) = &mut original {
|
||||
config.server_id = id_to_server()
|
||||
+10
-8
@@ -4,8 +4,9 @@ use monitor_client::{
|
||||
api::{read::GetDeployment, write},
|
||||
entities::{
|
||||
deployment::{
|
||||
Deployment, DeploymentConfig, DeploymentImage,
|
||||
DeploymentListItemInfo, PartialDeploymentConfig,
|
||||
Deployment, DeploymentConfig, DeploymentConfigDiff,
|
||||
DeploymentImage, DeploymentListItemInfo,
|
||||
PartialDeploymentConfig,
|
||||
},
|
||||
resource::{Resource, ResourceListItem},
|
||||
toml::ResourceToml,
|
||||
@@ -22,9 +23,10 @@ use crate::{
|
||||
use super::ResourceSync;
|
||||
|
||||
impl ResourceSync for Deployment {
|
||||
type Config = DeploymentConfig;
|
||||
type Info = ();
|
||||
type PartialConfig = PartialDeploymentConfig;
|
||||
type FullConfig = DeploymentConfig;
|
||||
type FullInfo = ();
|
||||
type ConfigDiff = DeploymentConfigDiff;
|
||||
type ListItemInfo = DeploymentListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
@@ -68,16 +70,16 @@ impl ResourceSync for Deployment {
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>> {
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client()
|
||||
.read(GetDeployment { deployment: id })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn minimize_update(
|
||||
mut original: Self::FullConfig,
|
||||
async fn get_diff(
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig> {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
// need to replace the server id with name
|
||||
original.server_id = id_to_server()
|
||||
.get(&original.server_id)
|
||||
@@ -0,0 +1,310 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use colored::Colorize;
|
||||
use monitor_client::{
|
||||
api::write::{UpdateDescription, UpdateTagsOnResource},
|
||||
entities::{
|
||||
resource::{Resource, ResourceListItem},
|
||||
toml::ResourceToml,
|
||||
update::ResourceTarget,
|
||||
},
|
||||
};
|
||||
use partial_derive2::{Diff, FieldDiff, MaybeNone, PartialDiff};
|
||||
|
||||
use crate::{cli_args, maps::id_to_tag, monitor_client};
|
||||
|
||||
pub mod alerter;
|
||||
pub mod build;
|
||||
pub mod builder;
|
||||
pub mod deployment;
|
||||
pub mod procedure;
|
||||
pub mod repo;
|
||||
pub mod server;
|
||||
pub mod server_template;
|
||||
|
||||
type ToUpdate<T> = Vec<ToUpdateItem<T>>;
|
||||
type ToCreate<T> = Vec<ResourceToml<T>>;
|
||||
type UpdatesResult<T> = (ToCreate<T>, ToUpdate<T>);
|
||||
|
||||
pub struct ToUpdateItem<T> {
|
||||
pub id: String,
|
||||
pub resource: ResourceToml<T>,
|
||||
pub update_description: bool,
|
||||
pub update_tags: bool,
|
||||
}
|
||||
|
||||
pub trait ResourceSync {
|
||||
type Config: Clone
|
||||
+ Send
|
||||
+ PartialDiff<Self::PartialConfig, Self::ConfigDiff>
|
||||
+ 'static;
|
||||
type Info: Default;
|
||||
type PartialConfig: std::fmt::Debug
|
||||
+ Clone
|
||||
+ Send
|
||||
+ From<Self::ConfigDiff>
|
||||
+ 'static;
|
||||
type ConfigDiff: Diff + MaybeNone;
|
||||
type ListItemInfo: 'static;
|
||||
|
||||
fn display() -> &'static str;
|
||||
|
||||
fn resource_target(id: String) -> ResourceTarget;
|
||||
|
||||
fn name_to_resource(
|
||||
) -> &'static HashMap<String, ResourceListItem<Self::ListItemInfo>>;
|
||||
|
||||
/// Creates the resource and returns created id.
|
||||
async fn create(
|
||||
resource: ResourceToml<Self::PartialConfig>,
|
||||
) -> anyhow::Result<String>;
|
||||
|
||||
/// Updates the resource at id with the partial config.
|
||||
async fn update(
|
||||
id: String,
|
||||
resource: ResourceToml<Self::PartialConfig>,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>>;
|
||||
|
||||
/// Diffs the declared toml (partial) against the full existing config.
|
||||
/// Removes all fields from toml (partial) that haven't changed.
|
||||
async fn get_diff(
|
||||
original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff>;
|
||||
|
||||
async fn get_updates(
|
||||
resources: Vec<ResourceToml<Self::PartialConfig>>,
|
||||
) -> anyhow::Result<UpdatesResult<Self::PartialConfig>> {
|
||||
let map = Self::name_to_resource();
|
||||
|
||||
let mut to_create = ToCreate::<Self::PartialConfig>::new();
|
||||
let mut to_update = ToUpdate::<Self::PartialConfig>::new();
|
||||
|
||||
let quiet = cli_args().quiet;
|
||||
|
||||
for mut resource in resources {
|
||||
match map.get(&resource.name).map(|s| s.id.clone()) {
|
||||
Some(id) => {
|
||||
// Get the full original config for the resource.
|
||||
let original = Self::get(id.clone()).await?;
|
||||
|
||||
let diff =
|
||||
Self::get_diff(original.config, resource.config).await?;
|
||||
|
||||
let original_tags = original
|
||||
.tags
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
id_to_tag().get(id).map(|t| t.name.clone())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Only proceed if there are any fields to update,
|
||||
// or a change to tags / description
|
||||
if diff.is_none()
|
||||
&& resource.description == original.description
|
||||
&& resource.tags == original_tags
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
println!(
|
||||
"\n{}: {}: '{}'\n-------------------",
|
||||
"UPDATE".blue(),
|
||||
Self::display(),
|
||||
resource.name.bold(),
|
||||
);
|
||||
let mut lines = Vec::<String>::new();
|
||||
if resource.description != original.description {
|
||||
lines.push(format!(
|
||||
"{}: 'description'\n{}: {}\n{}: {}",
|
||||
"field".dimmed(),
|
||||
"from".dimmed(),
|
||||
original.description.red(),
|
||||
"to".dimmed(),
|
||||
resource.description.green()
|
||||
))
|
||||
}
|
||||
if resource.tags != original_tags {
|
||||
let from = format!("{:?}", original_tags).red();
|
||||
let to = format!("{:?}", resource.tags).green();
|
||||
lines.push(format!(
|
||||
"{}: 'tags'\n{}: {from}\n{}: {to}",
|
||||
"field".dimmed(),
|
||||
"from".dimmed(),
|
||||
"to".dimmed(),
|
||||
));
|
||||
}
|
||||
lines.extend(diff.iter_field_diffs().map(
|
||||
|FieldDiff { field, from, to }| {
|
||||
format!(
|
||||
"{}: '{field}'\n{}: {}\n{}: {}",
|
||||
"field".dimmed(),
|
||||
"from".dimmed(),
|
||||
from.red(),
|
||||
"to".dimmed(),
|
||||
to.green()
|
||||
)
|
||||
},
|
||||
));
|
||||
println!("{}", lines.join("\n-------------------\n"));
|
||||
}
|
||||
|
||||
// Minimizes updates through diffing.
|
||||
resource.config = diff.into();
|
||||
|
||||
let update = ToUpdateItem {
|
||||
id,
|
||||
update_description: resource.description
|
||||
!= original.description,
|
||||
update_tags: resource.tags != original_tags,
|
||||
resource,
|
||||
};
|
||||
|
||||
to_update.push(update);
|
||||
}
|
||||
None => {
|
||||
if !quiet {
|
||||
println!(
|
||||
"{}: {}: {}: {resource:#?}",
|
||||
"CREATE".green(),
|
||||
Self::display(),
|
||||
resource.name.bold().green(),
|
||||
)
|
||||
}
|
||||
to_create.push(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if quiet && !to_create.is_empty() {
|
||||
println!(
|
||||
"\n{}s {}: {:#?}",
|
||||
Self::display(),
|
||||
"TO CREATE".green(),
|
||||
to_create.iter().map(|item| item.name.as_str())
|
||||
);
|
||||
}
|
||||
|
||||
if quiet && !to_update.is_empty() {
|
||||
println!(
|
||||
"\n{}s {}: {:#?}",
|
||||
Self::display(),
|
||||
"TO UPDATE".blue(),
|
||||
to_update
|
||||
.iter()
|
||||
.map(|update| update.resource.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
Ok((to_create, to_update))
|
||||
}
|
||||
|
||||
async fn run_updates(
|
||||
to_create: ToCreate<Self::PartialConfig>,
|
||||
to_update: ToUpdate<Self::PartialConfig>,
|
||||
) {
|
||||
let log_after = !to_update.is_empty() || !to_create.is_empty();
|
||||
|
||||
for resource in to_create {
|
||||
let name = resource.name.clone();
|
||||
let tags = resource.tags.clone();
|
||||
let description = resource.description.clone();
|
||||
let id = match Self::create(resource).await {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"failed to create {} {name} | {e:#}",
|
||||
Self::display(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
Self::update_tags(id.clone(), &name, tags).await;
|
||||
Self::update_description(id, &name, description).await;
|
||||
info!("{} {name} created", Self::display());
|
||||
}
|
||||
|
||||
for ToUpdateItem {
|
||||
id,
|
||||
resource,
|
||||
update_description,
|
||||
update_tags,
|
||||
} in to_update
|
||||
{
|
||||
// Update resource
|
||||
let name = resource.name.clone();
|
||||
let tags = resource.tags.clone();
|
||||
let description = resource.description.clone();
|
||||
|
||||
if update_description {
|
||||
Self::update_description(id.clone(), &name, description)
|
||||
.await;
|
||||
}
|
||||
|
||||
if update_tags {
|
||||
Self::update_tags(id.clone(), &name, tags).await;
|
||||
}
|
||||
|
||||
if let Err(e) = Self::update(id, resource).await {
|
||||
warn!(
|
||||
"failed to update config on {} {name} | {e:#}",
|
||||
Self::display()
|
||||
);
|
||||
} else {
|
||||
info!("updated {} {name} config", Self::display());
|
||||
}
|
||||
|
||||
info!("{} {name} updated", Self::display());
|
||||
}
|
||||
|
||||
if log_after {
|
||||
info!(
|
||||
"============ {}s synced ✅ ============",
|
||||
Self::display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_tags(id: String, name: &str, tags: Vec<String>) {
|
||||
// Update tags
|
||||
if let Err(e) = monitor_client()
|
||||
.write(UpdateTagsOnResource {
|
||||
target: Self::resource_target(id),
|
||||
tags,
|
||||
})
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"failed to update tags on {} {name} | {e:#}",
|
||||
Self::display(),
|
||||
);
|
||||
} else {
|
||||
info!("updated {} {name} tags", Self::display());
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_description(
|
||||
id: String,
|
||||
name: &str,
|
||||
description: String,
|
||||
) {
|
||||
if let Err(e) = monitor_client()
|
||||
.write(UpdateDescription {
|
||||
target: Self::resource_target(id.clone()),
|
||||
description,
|
||||
})
|
||||
.await
|
||||
{
|
||||
warn!("failed to update resource {id} description | {e:#}");
|
||||
} else {
|
||||
info!("updated {} {name} description", Self::display());
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
-22
@@ -8,15 +8,14 @@ use monitor_client::{
|
||||
},
|
||||
entities::{
|
||||
procedure::{
|
||||
PartialProcedureConfig, Procedure, ProcedureConfig,
|
||||
ProcedureListItemInfo,
|
||||
PartialProcedureConfig, Procedure, ProcedureConfig, ProcedureConfigDiff, ProcedureListItemInfo
|
||||
},
|
||||
resource::{Resource, ResourceListItem},
|
||||
toml::ResourceToml,
|
||||
update::ResourceTarget,
|
||||
},
|
||||
};
|
||||
use partial_derive2::PartialDiff;
|
||||
use partial_derive2::{MaybeNone, PartialDiff};
|
||||
|
||||
use crate::{
|
||||
maps::{
|
||||
@@ -24,14 +23,16 @@ use crate::{
|
||||
id_to_server, name_to_procedure,
|
||||
},
|
||||
monitor_client,
|
||||
sync::resources::ToUpdateItem,
|
||||
};
|
||||
|
||||
use super::{ResourceSync, ToCreate, ToUpdate};
|
||||
|
||||
impl ResourceSync for Procedure {
|
||||
type Config = ProcedureConfig;
|
||||
type Info = ();
|
||||
type PartialConfig = PartialProcedureConfig;
|
||||
type FullConfig = ProcedureConfig;
|
||||
type FullInfo = ();
|
||||
type ConfigDiff = ProcedureConfigDiff;
|
||||
type ListItemInfo = ProcedureListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
@@ -83,28 +84,43 @@ impl ResourceSync for Procedure {
|
||||
|
||||
for i in 0..10 {
|
||||
let mut to_pull = Vec::new();
|
||||
for (id, resource) in &to_update {
|
||||
for ToUpdateItem {
|
||||
id,
|
||||
resource,
|
||||
update_description,
|
||||
update_tags,
|
||||
} in &to_update
|
||||
{
|
||||
// Update resource
|
||||
let name = resource.name.clone();
|
||||
let tags = resource.tags.clone();
|
||||
let description = resource.description.clone();
|
||||
if let Err(e) =
|
||||
Self::update(id.clone(), resource.clone()).await
|
||||
{
|
||||
if i == 9 {
|
||||
warn!(
|
||||
"failed to update {} {name} | {e:#}",
|
||||
Self::display()
|
||||
);
|
||||
if *update_description {
|
||||
Self::update_description(id.clone(), &name, description)
|
||||
.await;
|
||||
}
|
||||
if *update_tags {
|
||||
Self::update_tags(id.clone(), &name, tags).await;
|
||||
}
|
||||
if !resource.config.is_none() {
|
||||
if let Err(e) =
|
||||
Self::update(id.clone(), resource.clone()).await
|
||||
{
|
||||
if i == 9 {
|
||||
warn!(
|
||||
"failed to update {} {name} | {e:#}",
|
||||
Self::display()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::update_tags(id.clone(), &name, tags).await;
|
||||
Self::update_description(id.clone(), description).await;
|
||||
|
||||
info!("{} {name} updated", Self::display());
|
||||
// have to clone out so to_update is mutable
|
||||
to_pull.push(id.clone());
|
||||
}
|
||||
to_update.retain(|(id, _)| !to_pull.contains(id));
|
||||
//
|
||||
to_update.retain(|resource| !to_pull.contains(&resource.id));
|
||||
|
||||
let mut to_pull = Vec::new();
|
||||
for resource in &to_create {
|
||||
@@ -124,7 +140,7 @@ impl ResourceSync for Procedure {
|
||||
}
|
||||
};
|
||||
Self::update_tags(id.clone(), &name, tags).await;
|
||||
Self::update_description(id, description).await;
|
||||
Self::update_description(id, &name, description).await;
|
||||
info!("{} {name} created", Self::display());
|
||||
to_pull.push(name);
|
||||
}
|
||||
@@ -143,14 +159,14 @@ impl ResourceSync for Procedure {
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>> {
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client().read(GetProcedure { procedure: id }).await
|
||||
}
|
||||
|
||||
async fn minimize_update(
|
||||
mut original: Self::FullConfig,
|
||||
async fn get_diff(
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig> {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
for execution in &mut original.executions {
|
||||
match &mut execution.execution {
|
||||
Execution::None(_) => {}
|
||||
@@ -7,7 +7,8 @@ use monitor_client::{
|
||||
},
|
||||
entities::{
|
||||
repo::{
|
||||
PartialRepoConfig, Repo, RepoConfig, RepoInfo, RepoListItemInfo,
|
||||
PartialRepoConfig, Repo, RepoConfig, RepoConfigDiff, RepoInfo,
|
||||
RepoListItemInfo,
|
||||
},
|
||||
resource::{Resource, ResourceListItem},
|
||||
toml::ResourceToml,
|
||||
@@ -24,9 +25,10 @@ use crate::{
|
||||
use super::ResourceSync;
|
||||
|
||||
impl ResourceSync for Repo {
|
||||
type Config = RepoConfig;
|
||||
type Info = RepoInfo;
|
||||
type PartialConfig = PartialRepoConfig;
|
||||
type FullConfig = RepoConfig;
|
||||
type FullInfo = RepoInfo;
|
||||
type ConfigDiff = RepoConfigDiff;
|
||||
type ListItemInfo = RepoListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
@@ -70,14 +72,14 @@ impl ResourceSync for Repo {
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>> {
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client().read(GetRepo { repo: id }).await
|
||||
}
|
||||
|
||||
async fn minimize_update(
|
||||
mut original: Self::FullConfig,
|
||||
async fn get_diff(
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig> {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
// Need to replace server id with name
|
||||
original.server_id = id_to_server()
|
||||
.get(&original.server_id)
|
||||
@@ -8,7 +8,8 @@ use monitor_client::{
|
||||
entities::{
|
||||
resource::{Resource, ResourceListItem},
|
||||
server::{
|
||||
PartialServerConfig, Server, ServerConfig, ServerListItemInfo,
|
||||
PartialServerConfig, Server, ServerConfig, ServerConfigDiff,
|
||||
ServerListItemInfo,
|
||||
},
|
||||
toml::ResourceToml,
|
||||
update::ResourceTarget,
|
||||
@@ -21,10 +22,11 @@ use crate::{maps::name_to_server, monitor_client};
|
||||
use super::ResourceSync;
|
||||
|
||||
impl ResourceSync for Server {
|
||||
type ListItemInfo = ServerListItemInfo;
|
||||
type FullConfig = ServerConfig;
|
||||
type FullInfo = ();
|
||||
type Config = ServerConfig;
|
||||
type Info = ();
|
||||
type PartialConfig = PartialServerConfig;
|
||||
type ConfigDiff = ServerConfigDiff;
|
||||
type ListItemInfo = ServerListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
"server"
|
||||
@@ -67,14 +69,14 @@ impl ResourceSync for Server {
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>> {
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client().read(GetServer { server: id }).await
|
||||
}
|
||||
|
||||
async fn minimize_update(
|
||||
original: Self::FullConfig,
|
||||
async fn get_diff(
|
||||
original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig> {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
Ok(original.partial_diff(update))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use monitor_client::{
|
||||
api::{
|
||||
read::GetServerTemplate,
|
||||
write::{CreateServerTemplate, UpdateServerTemplate},
|
||||
},
|
||||
entities::{
|
||||
resource::{Resource, ResourceListItem},
|
||||
server_template::{
|
||||
PartialServerTemplateConfig, ServerTemplate, ServerTemplateConfig, ServerTemplateConfigDiff, ServerTemplateListItemInfo
|
||||
},
|
||||
toml::ResourceToml,
|
||||
update::ResourceTarget,
|
||||
},
|
||||
};
|
||||
use partial_derive2::PartialDiff;
|
||||
|
||||
use crate::{maps::name_to_server_template, monitor_client};
|
||||
|
||||
use super::ResourceSync;
|
||||
|
||||
impl ResourceSync for ServerTemplate {
|
||||
type Config = ServerTemplateConfig;
|
||||
type Info = ();
|
||||
type PartialConfig = PartialServerTemplateConfig;
|
||||
type ConfigDiff = ServerTemplateConfigDiff;
|
||||
type ListItemInfo = ServerTemplateListItemInfo;
|
||||
|
||||
fn display() -> &'static str {
|
||||
"server template"
|
||||
}
|
||||
|
||||
fn resource_target(id: String) -> ResourceTarget {
|
||||
ResourceTarget::ServerTemplate(id)
|
||||
}
|
||||
|
||||
fn name_to_resource(
|
||||
) -> &'static HashMap<String, ResourceListItem<Self::ListItemInfo>>
|
||||
{
|
||||
name_to_server_template()
|
||||
}
|
||||
|
||||
async fn create(
|
||||
resource: ResourceToml<Self::PartialConfig>,
|
||||
) -> anyhow::Result<String> {
|
||||
monitor_client()
|
||||
.write(CreateServerTemplate {
|
||||
name: resource.name,
|
||||
config: resource.config,
|
||||
})
|
||||
.await
|
||||
.map(|res| res.id)
|
||||
}
|
||||
|
||||
async fn update(
|
||||
id: String,
|
||||
resource: ResourceToml<Self::PartialConfig>,
|
||||
) -> anyhow::Result<()> {
|
||||
monitor_client()
|
||||
.write(UpdateServerTemplate {
|
||||
id,
|
||||
config: resource.config,
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
monitor_client()
|
||||
.read(GetServerTemplate {
|
||||
server_template: id,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_diff(
|
||||
original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
Ok(original.partial_diff(update))
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -13,19 +13,20 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# local
|
||||
monitor_client.workspace = true
|
||||
periphery_client.workspace = true
|
||||
monitor_client.workspace = true
|
||||
logger.workspace = true
|
||||
# mogh
|
||||
async_timing_util.workspace = true
|
||||
merge_config_files.workspace = true
|
||||
parse_csl.workspace = true
|
||||
termination_signal.workspace = true
|
||||
resolver_api.workspace = true
|
||||
mungos.workspace = true
|
||||
async_timing_util.workspace = true
|
||||
partial_derive2.workspace = true
|
||||
mongo_indexed.workspace = true
|
||||
slack.workspace = true
|
||||
resolver_api.workspace = true
|
||||
parse_csl.workspace = true
|
||||
mungos.workspace = true
|
||||
serror.workspace = true
|
||||
slack.workspace = true
|
||||
# external
|
||||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
|
||||
@@ -46,9 +46,9 @@ use crate::{
|
||||
channel::build_cancel_channel,
|
||||
periphery_client,
|
||||
query::get_deployment_state,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
resource,
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
@@ -60,7 +60,7 @@ impl Resolve<RunBuild, User> for State {
|
||||
RunBuild { build }: RunBuild,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let mut build = Build::get_resource_check_permissions(
|
||||
let mut build = resource::get_check_permissions::<Build>(
|
||||
&build,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
@@ -262,7 +262,7 @@ impl Resolve<CancelBuild, User> for State {
|
||||
CancelBuild { build }: CancelBuild,
|
||||
user: User,
|
||||
) -> anyhow::Result<CancelBuildResponse> {
|
||||
let build = Build::get_resource_check_permissions(
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
&build,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
@@ -323,13 +323,13 @@ async fn get_build_builder(
|
||||
return Err(anyhow!("build has not configured a builder"));
|
||||
}
|
||||
let builder =
|
||||
Builder::get_resource(&build.config.builder_id).await?;
|
||||
resource::get::<Builder>(&build.config.builder_id).await?;
|
||||
match builder.config {
|
||||
BuilderConfig::Server(config) => {
|
||||
if config.server_id.is_empty() {
|
||||
return Err(anyhow!("builder has not configured a server"));
|
||||
}
|
||||
let server = Server::get_resource(&config.server_id).await?;
|
||||
let server = resource::get::<Server>(&config.server_id).await?;
|
||||
let periphery = periphery_client(&server)?;
|
||||
Ok((
|
||||
periphery,
|
||||
@@ -355,7 +355,7 @@ async fn get_aws_builder(
|
||||
let instance_name = format!(
|
||||
"BUILDER-{}-v{}",
|
||||
build.name,
|
||||
build.config.version.to_string()
|
||||
build.config.version
|
||||
);
|
||||
let Ec2Instance { instance_id, ip } = launch_ec2_instance(
|
||||
&instance_name,
|
||||
|
||||
@@ -24,10 +24,10 @@ use crate::{
|
||||
helpers::{
|
||||
periphery_client,
|
||||
query::get_server_with_status,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
monitor::update_cache_for_server,
|
||||
resource,
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
@@ -43,12 +43,13 @@ impl Resolve<Deploy, User> for State {
|
||||
}: Deploy,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let mut deployment = Deployment::get_resource_check_permissions(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
let mut deployment =
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// get the action state for the deployment (or insert default).
|
||||
let action_state = action_states()
|
||||
@@ -77,7 +78,7 @@ impl Resolve<Deploy, User> for State {
|
||||
|
||||
let version = match deployment.config.image {
|
||||
DeploymentImage::Build { build_id, version } => {
|
||||
let build = Build::get_resource(&build_id).await?;
|
||||
let build = resource::get::<Build>(&build_id).await?;
|
||||
let image_name = get_image_name(&build);
|
||||
let version = if version.is_none() {
|
||||
build.config.version
|
||||
@@ -85,7 +86,7 @@ impl Resolve<Deploy, User> for State {
|
||||
version
|
||||
};
|
||||
deployment.config.image = DeploymentImage::Image {
|
||||
image: format!("{image_name}:{}", version.to_string()),
|
||||
image: format!("{image_name}:{version}"),
|
||||
};
|
||||
if deployment.config.docker_account.is_empty() {
|
||||
deployment.config.docker_account =
|
||||
@@ -143,7 +144,7 @@ impl Resolve<StartContainer, User> for State {
|
||||
StartContainer { deployment }: StartContainer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
@@ -222,7 +223,7 @@ impl Resolve<StopContainer, User> for State {
|
||||
}: StopContainer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
@@ -381,7 +382,7 @@ impl Resolve<RemoveContainer, User> for State {
|
||||
}: RemoveContainer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
|
||||
@@ -13,10 +13,8 @@ use tokio::sync::Mutex;
|
||||
use crate::{
|
||||
helpers::{
|
||||
procedure::execute_procedure,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, State},
|
||||
}, resource, state::{action_states, State}
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -27,7 +25,7 @@ impl Resolve<RunProcedure, User> for State {
|
||||
RunProcedure { procedure }: RunProcedure,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let procedure = Procedure::get_resource_check_permissions(
|
||||
let procedure = resource::get_check_permissions::<Procedure>(
|
||||
&procedure,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
|
||||
@@ -23,9 +23,9 @@ use crate::{
|
||||
config::core_config,
|
||||
helpers::{
|
||||
periphery_client,
|
||||
resource::StateResource,
|
||||
update::{add_update, update_update},
|
||||
},
|
||||
resource,
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Resolve<CloneRepo, User> for State {
|
||||
CloneRepo { repo }: CloneRepo,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let repo = Repo::get_resource_check_permissions(
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
&repo,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
@@ -57,7 +57,8 @@ impl Resolve<CloneRepo, User> for State {
|
||||
return Err(anyhow!("repo has no server attached"));
|
||||
}
|
||||
|
||||
let server = Server::get_resource(&repo.config.server_id).await?;
|
||||
let server =
|
||||
resource::get::<Server>(&repo.config.server_id).await?;
|
||||
|
||||
let periphery = periphery_client(&server)?;
|
||||
|
||||
@@ -126,10 +127,10 @@ impl Resolve<PullRepo, User> for State {
|
||||
PullRepo { repo }: PullRepo,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let repo = Repo::get_resource_check_permissions(
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
&repo,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -146,7 +147,8 @@ impl Resolve<PullRepo, User> for State {
|
||||
return Err(anyhow!("repo has no server attached"));
|
||||
}
|
||||
|
||||
let server = Server::get_resource(&repo.config.server_id).await?;
|
||||
let server =
|
||||
resource::get::<Server>(&repo.config.server_id).await?;
|
||||
|
||||
let periphery = periphery_client(&server)?;
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ use serror::serialize_error_pretty;
|
||||
use crate::{
|
||||
helpers::{
|
||||
periphery_client,
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
resource,
|
||||
state::{action_states, State},
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ impl Resolve<PruneDockerContainers, User> for State {
|
||||
PruneDockerContainers { server }: PruneDockerContainers,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
@@ -89,7 +89,7 @@ impl Resolve<PruneDockerNetworks, User> for State {
|
||||
PruneDockerNetworks { server }: PruneDockerNetworks,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
@@ -146,7 +146,7 @@ impl Resolve<PruneDockerImages, User> for State {
|
||||
PruneDockerImages { server }: PruneDockerImages,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::async_trait;
|
||||
use monitor_client::{
|
||||
api::{execute::LaunchServer, write::CreateServer},
|
||||
@@ -10,15 +11,12 @@ use monitor_client::{
|
||||
Operation,
|
||||
},
|
||||
};
|
||||
use mungos::mongodb::bson::doc;
|
||||
use resolver_api::Resolve;
|
||||
use serror::serialize_error_pretty;
|
||||
|
||||
use crate::{
|
||||
cloud::aws::launch_ec2_instance,
|
||||
helpers::{
|
||||
resource::StateResource,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::State,
|
||||
cloud::aws::launch_ec2_instance, helpers::update::{add_update, make_update, update_update}, resource, state::{db_client, State}
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -32,20 +30,40 @@ impl Resolve<LaunchServer, User> for State {
|
||||
}: LaunchServer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let template = ServerTemplate::get_resource_check_permissions(
|
||||
// validate name isn't already taken by another server
|
||||
if db_client()
|
||||
.await
|
||||
.servers
|
||||
.find_one(
|
||||
doc! {
|
||||
"name": &name
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query db for servers")?
|
||||
.is_some()
|
||||
{
|
||||
return Err(anyhow!("name is already taken"));
|
||||
}
|
||||
|
||||
let template = resource::get_check_permissions::<ServerTemplate>(
|
||||
&server_template,
|
||||
&user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut update =
|
||||
make_update(&template, Operation::LaunchServer, &user);
|
||||
update.in_progress();
|
||||
update.push_simple_log(
|
||||
"launching server",
|
||||
format!("{:#?}", template.config),
|
||||
);
|
||||
update.id = add_update(update.clone()).await?;
|
||||
match template.config {
|
||||
|
||||
let config = match template.config {
|
||||
ServerTemplateConfig::Aws(config) => {
|
||||
let region = config.region.clone();
|
||||
let instance = launch_ec2_instance(&name, config).await;
|
||||
@@ -66,22 +84,32 @@ impl Resolve<LaunchServer, User> for State {
|
||||
instance.ip
|
||||
),
|
||||
);
|
||||
let _ = self
|
||||
.resolve(
|
||||
CreateServer {
|
||||
name,
|
||||
config: PartialServerConfig {
|
||||
address: format!("http://{}:8120", instance.ip)
|
||||
.into(),
|
||||
region: region.into(),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
user,
|
||||
)
|
||||
.await;
|
||||
PartialServerConfig {
|
||||
address: format!("http://{}:8120", instance.ip).into(),
|
||||
region: region.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match self.resolve(CreateServer { name, config }, user).await {
|
||||
Ok(server) => {
|
||||
update.push_simple_log(
|
||||
"create server",
|
||||
format!("created server {} ({})", server.name, server.id),
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
update.push_error_log(
|
||||
"create server",
|
||||
format!(
|
||||
"failed to create server\n\n{}",
|
||||
serialize_error_pretty(&e)
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
Ok(update)
|
||||
|
||||
@@ -4,7 +4,7 @@ use monitor_client::{
|
||||
api::read::{
|
||||
GetAlert, GetAlertResponse, ListAlerts, ListAlertsResponse,
|
||||
},
|
||||
entities::{deployment::Deployment, server::Server, user::User},
|
||||
entities::{update::ResourceTargetVariant, user::User},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::find_one_by_id,
|
||||
@@ -14,7 +14,7 @@ use mungos::{
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::resource::StateResource,
|
||||
helpers::query::get_resource_ids_for_non_admin,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
@@ -29,10 +29,16 @@ impl Resolve<ListAlerts, User> for State {
|
||||
) -> anyhow::Result<ListAlertsResponse> {
|
||||
let mut query = query.unwrap_or_default();
|
||||
if !user.admin {
|
||||
let server_ids =
|
||||
Server::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let deployment_ids =
|
||||
Deployment::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let server_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Server,
|
||||
)
|
||||
.await?;
|
||||
let deployment_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Deployment,
|
||||
)
|
||||
.await?;
|
||||
query.extend(doc! {
|
||||
"$or": [
|
||||
{ "target.type": "Server", "target.id": { "$in": &server_ids } },
|
||||
|
||||
@@ -15,9 +15,8 @@ use mungos::mongodb::bson::{doc, oid::ObjectId};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::resource::{
|
||||
get_resource_ids_for_non_admin, StateResource,
|
||||
},
|
||||
helpers::query::get_resource_ids_for_non_admin,
|
||||
resource,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
@@ -28,7 +27,7 @@ impl Resolve<GetAlerter, User> for State {
|
||||
GetAlerter { alerter }: GetAlerter,
|
||||
user: User,
|
||||
) -> anyhow::Result<Alerter> {
|
||||
Alerter::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Alerter>(
|
||||
&alerter,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -44,7 +43,7 @@ impl Resolve<ListAlerters, User> for State {
|
||||
ListAlerters { query }: ListAlerters,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<AlerterListItem>> {
|
||||
Alerter::list_resource_list_items_for_user(query, &user).await
|
||||
resource::list_for_user::<Alerter>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,8 @@ use resolver_api::{Resolve, ResolveToString};
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::resource::{
|
||||
get_resource_ids_for_non_admin, StateResource,
|
||||
},
|
||||
helpers::query::get_resource_ids_for_non_admin,
|
||||
resource,
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
@@ -38,7 +37,7 @@ impl Resolve<GetBuild, User> for State {
|
||||
GetBuild { build }: GetBuild,
|
||||
user: User,
|
||||
) -> anyhow::Result<Build> {
|
||||
Build::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Build>(
|
||||
&build,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -54,7 +53,7 @@ impl Resolve<ListBuilds, User> for State {
|
||||
ListBuilds { query }: ListBuilds,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<BuildListItem>> {
|
||||
Build::list_resource_list_items_for_user(query, &user).await
|
||||
resource::list_for_user::<Build>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +64,7 @@ impl Resolve<GetBuildActionState, User> for State {
|
||||
GetBuildActionState { build }: GetBuildActionState,
|
||||
user: User,
|
||||
) -> anyhow::Result<BuildActionState> {
|
||||
let build = Build::get_resource_check_permissions(
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
&build,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -196,7 +195,7 @@ impl Resolve<GetBuildVersions, User> for State {
|
||||
}: GetBuildVersions,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<BuildVersionResponseItem>> {
|
||||
let build = Build::get_resource_check_permissions(
|
||||
let build = resource::get_check_permissions::<Build>(
|
||||
&build,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
|
||||
@@ -16,9 +16,8 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::resource::{
|
||||
get_resource_ids_for_non_admin, StateResource,
|
||||
},
|
||||
helpers::query::get_resource_ids_for_non_admin,
|
||||
resource,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
@@ -29,7 +28,7 @@ impl Resolve<GetBuilder, User> for State {
|
||||
GetBuilder { builder }: GetBuilder,
|
||||
user: User,
|
||||
) -> anyhow::Result<Builder> {
|
||||
Builder::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Builder>(
|
||||
&builder,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -45,7 +44,7 @@ impl Resolve<ListBuilders, User> for State {
|
||||
ListBuilders { query }: ListBuilders,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<BuilderListItem>> {
|
||||
Builder::list_resource_list_items_for_user(query, &user).await
|
||||
resource::list_for_user::<Builder>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +91,7 @@ impl Resolve<GetBuilderAvailableAccounts, User> for State {
|
||||
GetBuilderAvailableAccounts { builder }: GetBuilderAvailableAccounts,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetBuilderAvailableAccountsResponse> {
|
||||
let builder = Builder::get_resource_check_permissions(
|
||||
let builder = resource::get_check_permissions::<Builder>(
|
||||
&builder,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
|
||||
@@ -24,9 +24,9 @@ use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
periphery_client,
|
||||
resource::{get_resource_ids_for_non_admin, StateResource},
|
||||
periphery_client, query::get_resource_ids_for_non_admin,
|
||||
},
|
||||
resource,
|
||||
state::{action_states, db_client, deployment_status_cache, State},
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Resolve<GetDeployment, User> for State {
|
||||
GetDeployment { deployment }: GetDeployment,
|
||||
user: User,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
Deployment::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -53,7 +53,7 @@ impl Resolve<ListDeployments, User> for State {
|
||||
ListDeployments { query }: ListDeployments,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<DeploymentListItem>> {
|
||||
Deployment::list_resource_list_items_for_user(query, &user).await
|
||||
resource::list_for_user::<Deployment>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ impl Resolve<GetDeploymentContainer, User> for State {
|
||||
GetDeploymentContainer { deployment }: GetDeploymentContainer,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetDeploymentContainerResponse> {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -95,7 +95,7 @@ impl Resolve<GetLog, User> for State {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
..
|
||||
} = Deployment::get_resource_check_permissions(
|
||||
} = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -104,7 +104,7 @@ impl Resolve<GetLog, User> for State {
|
||||
if server_id.is_empty() {
|
||||
return Ok(Log::default());
|
||||
}
|
||||
let server = Server::get_resource(&server_id).await?;
|
||||
let server = resource::get::<Server>(&server_id).await?;
|
||||
periphery_client(&server)?
|
||||
.request(api::container::GetContainerLog {
|
||||
name,
|
||||
@@ -130,7 +130,7 @@ impl Resolve<SearchLog, User> for State {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
..
|
||||
} = Deployment::get_resource_check_permissions(
|
||||
} = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -139,7 +139,7 @@ impl Resolve<SearchLog, User> for State {
|
||||
if server_id.is_empty() {
|
||||
return Ok(Log::default());
|
||||
}
|
||||
let server = Server::get_resource(&server_id).await?;
|
||||
let server = resource::get::<Server>(&server_id).await?;
|
||||
periphery_client(&server)?
|
||||
.request(api::container::GetContainerLogSearch {
|
||||
name,
|
||||
@@ -162,7 +162,7 @@ impl Resolve<GetDeploymentStats, User> for State {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
..
|
||||
} = Deployment::get_resource_check_permissions(
|
||||
} = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -171,7 +171,7 @@ impl Resolve<GetDeploymentStats, User> for State {
|
||||
if server_id.is_empty() {
|
||||
return Err(anyhow!("deployment has no server attached"));
|
||||
}
|
||||
let server = Server::get_resource(&server_id).await?;
|
||||
let server = resource::get::<Server>(&server_id).await?;
|
||||
periphery_client(&server)?
|
||||
.request(api::container::GetContainerStats { name })
|
||||
.await
|
||||
@@ -186,7 +186,7 @@ impl Resolve<GetDeploymentActionState, User> for State {
|
||||
GetDeploymentActionState { deployment }: GetDeploymentActionState,
|
||||
user: User,
|
||||
) -> anyhow::Result<DeploymentActionState> {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
&deployment,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -263,7 +263,7 @@ impl Resolve<ListCommonExtraArgs, User> for State {
|
||||
user: User,
|
||||
) -> anyhow::Result<ListCommonExtraArgsResponse> {
|
||||
let deployments =
|
||||
Deployment::list_resources_for_user(query, &user)
|
||||
resource::list_full_for_user::<Deployment>(query, &user)
|
||||
.await
|
||||
.context("failed to get resources matching query")?;
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ enum ReadRequest {
|
||||
// ==== SERVER TEMPLATE ====
|
||||
GetServerTemplate(GetServerTemplate),
|
||||
ListServerTemplates(ListServerTemplates),
|
||||
GetServerTemplateSummary(GetServerTemplatesSummary),
|
||||
GetServerTemplatesSummary(GetServerTemplatesSummary),
|
||||
|
||||
// ==== SERVER ====
|
||||
GetServersSummary(GetServersSummary),
|
||||
|
||||
@@ -12,7 +12,7 @@ use mungos::{find::find_collect, mongodb::bson::doc};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::resource::get_user_permission_on_resource,
|
||||
helpers::query::get_user_permission_on_resource,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
|
||||
@@ -18,9 +18,8 @@ use mungos::mongodb::bson::{doc, oid::ObjectId};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::resource::{
|
||||
get_resource_ids_for_non_admin, StateResource,
|
||||
},
|
||||
helpers::query::get_resource_ids_for_non_admin,
|
||||
resource,
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
@@ -31,7 +30,7 @@ impl Resolve<GetProcedure, User> for State {
|
||||
GetProcedure { procedure }: GetProcedure,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetProcedureResponse> {
|
||||
Procedure::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
&procedure,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -47,7 +46,7 @@ impl Resolve<ListProcedures, User> for State {
|
||||
ListProcedures { query }: ListProcedures,
|
||||
user: User,
|
||||
) -> anyhow::Result<ListProceduresResponse> {
|
||||
Procedure::list_resource_list_items_for_user(query, &user).await
|
||||
resource::list_for_user::<Procedure>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +93,7 @@ impl Resolve<GetProcedureActionState, User> for State {
|
||||
GetProcedureActionState { procedure }: GetProcedureActionState,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetProcedureActionStateResponse> {
|
||||
let procedure = Procedure::get_resource_check_permissions(
|
||||
let procedure = resource::get_check_permissions::<Procedure>(
|
||||
&procedure,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
|
||||
@@ -15,9 +15,8 @@ use mungos::mongodb::bson::{doc, oid::ObjectId};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::resource::{
|
||||
get_resource_ids_for_non_admin, StateResource,
|
||||
},
|
||||
helpers::query::get_resource_ids_for_non_admin,
|
||||
resource,
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
@@ -28,7 +27,7 @@ impl Resolve<GetRepo, User> for State {
|
||||
GetRepo { repo }: GetRepo,
|
||||
user: User,
|
||||
) -> anyhow::Result<Repo> {
|
||||
Repo::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Repo>(
|
||||
&repo,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -44,7 +43,7 @@ impl Resolve<ListRepos, User> for State {
|
||||
ListRepos { query }: ListRepos,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<RepoListItem>> {
|
||||
Repo::list_resource_list_items_for_user(query, &user).await
|
||||
resource::list_for_user::<Repo>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +54,7 @@ impl Resolve<GetRepoActionState, User> for State {
|
||||
GetRepoActionState { repo }: GetRepoActionState,
|
||||
user: User,
|
||||
) -> anyhow::Result<RepoActionState> {
|
||||
let repo = Repo::get_resource_check_permissions(
|
||||
let repo = resource::get_check_permissions::<Repo>(
|
||||
&repo,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
|
||||
@@ -2,17 +2,22 @@ use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::read::{FindResources, FindResourcesResponse},
|
||||
entities::{
|
||||
build, deployment, procedure, repo, server,
|
||||
update::ResourceTargetVariant::{self, *},
|
||||
build::Build, deployment::Deployment, procedure::Procedure,
|
||||
repo::Repo, server::Server, update::ResourceTargetVariant,
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{helpers::resource::StateResource, state::State};
|
||||
use crate::{resource, state::State};
|
||||
|
||||
const FIND_RESOURCE_TYPES: [ResourceTargetVariant; 5] =
|
||||
[Server, Build, Deployment, Repo, Procedure];
|
||||
const FIND_RESOURCE_TYPES: [ResourceTargetVariant; 5] = [
|
||||
ResourceTargetVariant::Server,
|
||||
ResourceTargetVariant::Build,
|
||||
ResourceTargetVariant::Deployment,
|
||||
ResourceTargetVariant::Repo,
|
||||
ResourceTargetVariant::Procedure,
|
||||
];
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<FindResources, User> for State {
|
||||
@@ -27,49 +32,50 @@ impl Resolve<FindResources, User> for State {
|
||||
} else {
|
||||
resources
|
||||
.into_iter()
|
||||
.filter(|r| !matches!(r, System | Builder | Alerter))
|
||||
.filter(|r| {
|
||||
!matches!(
|
||||
r,
|
||||
ResourceTargetVariant::System
|
||||
| ResourceTargetVariant::Builder
|
||||
| ResourceTargetVariant::Alerter
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
for resource_type in resource_types {
|
||||
match resource_type {
|
||||
Server => {
|
||||
res.servers =
|
||||
server::Server::query_resource_list_items_for_user(
|
||||
query.clone(),
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
ResourceTargetVariant::Server => {
|
||||
res.servers = resource::list_for_user_using_document::<
|
||||
Server,
|
||||
>(query.clone(), &user)
|
||||
.await?;
|
||||
}
|
||||
Deployment => {
|
||||
res.deployments =
|
||||
deployment::Deployment::query_resource_list_items_for_user(
|
||||
query.clone(),
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
ResourceTargetVariant::Deployment => {
|
||||
res.deployments = resource::list_for_user_using_document::<
|
||||
Deployment,
|
||||
>(query.clone(), &user)
|
||||
.await?;
|
||||
}
|
||||
Build => {
|
||||
ResourceTargetVariant::Build => {
|
||||
res.builds =
|
||||
build::Build::query_resource_list_items_for_user(
|
||||
resource::list_for_user_using_document::<Build>(
|
||||
query.clone(),
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Repo => {
|
||||
res.repos = repo::Repo::query_resource_list_items_for_user(
|
||||
ResourceTargetVariant::Repo => {
|
||||
res.repos = resource::list_for_user_using_document::<Repo>(
|
||||
query.clone(),
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Procedure => {
|
||||
res.procedures =
|
||||
procedure::Procedure::query_resource_list_items_for_user(
|
||||
query.clone(),
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
ResourceTargetVariant::Procedure => {
|
||||
res.procedures = resource::list_for_user_using_document::<
|
||||
Procedure,
|
||||
>(query.clone(), &user)
|
||||
.await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,10 @@ use resolver_api::{Resolve, ResolveToString};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
config::core_config, helpers::{periphery_client, resource::StateResource}, state::{action_states, db_client, server_status_cache, State}
|
||||
config::core_config,
|
||||
helpers::periphery_client,
|
||||
resource,
|
||||
state::{action_states, db_client, server_status_cache, State},
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -39,11 +42,9 @@ impl Resolve<GetServersSummary, User> for State {
|
||||
GetServersSummary {}: GetServersSummary,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetServersSummaryResponse> {
|
||||
let servers = Server::list_resource_list_items_for_user(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
let servers =
|
||||
resource::list_for_user::<Server>(Default::default(), &user)
|
||||
.await?;
|
||||
let mut res = GetServersSummaryResponse::default();
|
||||
for server in servers {
|
||||
res.total += 1;
|
||||
@@ -70,7 +71,7 @@ impl Resolve<GetPeripheryVersion, User> for State {
|
||||
req: GetPeripheryVersion,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetPeripheryVersionResponse> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&req.server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -92,7 +93,7 @@ impl Resolve<GetServer, User> for State {
|
||||
req: GetServer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Server> {
|
||||
Server::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Server>(
|
||||
&req.server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -108,7 +109,7 @@ impl Resolve<ListServers, User> for State {
|
||||
ListServers { query }: ListServers,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<ServerListItem>> {
|
||||
Server::list_resource_list_items_for_user(query, &user).await
|
||||
resource::list_for_user::<Server>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +120,7 @@ impl Resolve<GetServerStatus, User> for State {
|
||||
GetServerStatus { server }: GetServerStatus,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetServerStatusResponse> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -143,7 +144,7 @@ impl Resolve<GetServerActionState, User> for State {
|
||||
GetServerActionState { server }: GetServerActionState,
|
||||
user: User,
|
||||
) -> anyhow::Result<ServerActionState> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -175,7 +176,7 @@ impl ResolveToString<GetSystemInformation, User> for State {
|
||||
GetSystemInformation { server }: GetSystemInformation,
|
||||
user: User,
|
||||
) -> anyhow::Result<String> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -211,7 +212,7 @@ impl ResolveToString<GetSystemStats, User> for State {
|
||||
GetSystemStats { server }: GetSystemStats,
|
||||
user: User,
|
||||
) -> anyhow::Result<String> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -245,7 +246,7 @@ impl ResolveToString<GetSystemProcesses, User> for State {
|
||||
GetSystemProcesses { server }: GetSystemProcesses,
|
||||
user: User,
|
||||
) -> anyhow::Result<String> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -286,7 +287,7 @@ impl Resolve<GetHistoricalServerStats, User> for State {
|
||||
}: GetHistoricalServerStats,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetHistoricalServerStatsResponse> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -336,7 +337,7 @@ impl Resolve<GetDockerImages, User> for State {
|
||||
GetDockerImages { server }: GetDockerImages,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<ImageSummary>> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -355,7 +356,7 @@ impl Resolve<GetDockerNetworks, User> for State {
|
||||
GetDockerNetworks { server }: GetDockerNetworks,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<DockerNetwork>> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -374,7 +375,7 @@ impl Resolve<GetDockerContainers, User> for State {
|
||||
GetDockerContainers { server }: GetDockerContainers,
|
||||
user: User,
|
||||
) -> anyhow::Result<Vec<ContainerSummary>> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -393,13 +394,13 @@ impl Resolve<GetAvailableAccounts, User> for State {
|
||||
GetAvailableAccounts { server }: GetAvailableAccounts,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetAvailableAccountsResponse> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
let GetAccountsResponse { github, docker } =
|
||||
periphery_client(&server)?
|
||||
.request(api::GetAccounts {})
|
||||
@@ -407,10 +408,10 @@ impl Resolve<GetAvailableAccounts, User> for State {
|
||||
.context("failed to get accounts from periphery")?;
|
||||
|
||||
let mut github_set = HashSet::<String>::new();
|
||||
|
||||
|
||||
github_set.extend(core_config().github_accounts.keys().cloned());
|
||||
github_set.extend(github);
|
||||
|
||||
|
||||
let mut github = github_set.into_iter().collect::<Vec<_>>();
|
||||
github.sort();
|
||||
|
||||
@@ -434,7 +435,7 @@ impl Resolve<GetAvailableSecrets, User> for State {
|
||||
GetAvailableSecrets { server }: GetAvailableSecrets,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetAvailableSecretsResponse> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
|
||||
@@ -17,10 +17,7 @@ use mungos::mongodb::bson::{doc, oid::ObjectId};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::resource::{
|
||||
get_resource_ids_for_non_admin, StateResource,
|
||||
},
|
||||
state::{db_client, State},
|
||||
helpers::query::get_resource_ids_for_non_admin, resource, state::{db_client, State}
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -30,7 +27,7 @@ impl Resolve<GetServerTemplate, User> for State {
|
||||
GetServerTemplate { server_template }: GetServerTemplate,
|
||||
user: User,
|
||||
) -> anyhow::Result<GetServerTemplateResponse> {
|
||||
ServerTemplate::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<ServerTemplate>(
|
||||
&server_template,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -46,8 +43,7 @@ impl Resolve<ListServerTemplates, User> for State {
|
||||
ListServerTemplates { query }: ListServerTemplates,
|
||||
user: User,
|
||||
) -> anyhow::Result<ListServerTemplatesResponse> {
|
||||
ServerTemplate::list_resource_list_items_for_user(query, &user)
|
||||
.await
|
||||
resource::list_for_user::<ServerTemplate>(query, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,9 +33,8 @@ use mungos::find::find_collect;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
query::get_user_user_group_ids, resource::StateResource,
|
||||
},
|
||||
helpers::query::get_user_user_group_ids,
|
||||
resource,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
@@ -49,34 +48,25 @@ impl Resolve<ExportAllResourcesToToml, User> for State {
|
||||
let mut targets = Vec::<ResourceTarget>::new();
|
||||
|
||||
targets.extend(
|
||||
Alerter::list_resource_list_items_for_user(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Alerter(resource.id)),
|
||||
resource::list_for_user::<Alerter>(Default::default(), &user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Alerter(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
Builder::list_resource_list_items_for_user(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Builder(resource.id)),
|
||||
resource::list_for_user::<Builder>(Default::default(), &user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Builder(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
Server::list_resource_list_items_for_user(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Server(resource.id)),
|
||||
resource::list_for_user::<Server>(Default::default(), &user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Server(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
Deployment::list_resource_list_items_for_user(
|
||||
resource::list_for_user::<Deployment>(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
@@ -85,31 +75,31 @@ impl Resolve<ExportAllResourcesToToml, User> for State {
|
||||
.map(|resource| ResourceTarget::Deployment(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
Build::list_resource_list_items_for_user(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Build(resource.id)),
|
||||
resource::list_for_user::<Build>(Default::default(), &user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Build(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
Repo::list_resource_list_items_for_user(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Repo(resource.id)),
|
||||
resource::list_for_user::<Repo>(Default::default(), &user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Repo(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
Procedure::list_resource_list_items_for_user(
|
||||
resource::list_for_user::<Procedure>(Default::default(), &user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Procedure(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_for_user::<ServerTemplate>(
|
||||
Default::default(),
|
||||
&user,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Procedure(resource.id)),
|
||||
.map(|resource| ResourceTarget::ServerTemplate(resource.id)),
|
||||
);
|
||||
|
||||
let user_groups = if user.admin {
|
||||
@@ -152,7 +142,7 @@ impl Resolve<ExportResourcesToToml, User> for State {
|
||||
for target in targets {
|
||||
match target {
|
||||
ResourceTarget::Alerter(id) => {
|
||||
let alerter = Alerter::get_resource_check_permissions(
|
||||
let alerter = resource::get_check_permissions::<Alerter>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -161,19 +151,18 @@ impl Resolve<ExportResourcesToToml, User> for State {
|
||||
res.alerters.push(convert_resource(alerter, &names.tags))
|
||||
}
|
||||
ResourceTarget::ServerTemplate(id) => {
|
||||
let template =
|
||||
ServerTemplate::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
)
|
||||
.await?;
|
||||
let template = resource::get_check_permissions::<
|
||||
ServerTemplate,
|
||||
>(
|
||||
&id, &user, PermissionLevel::Read
|
||||
)
|
||||
.await?;
|
||||
res
|
||||
.server_templates
|
||||
.push(convert_resource(template, &names.tags))
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -182,12 +171,13 @@ impl Resolve<ExportResourcesToToml, User> for State {
|
||||
res.servers.push(convert_resource(server, &names.tags))
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
let mut builder = Builder::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
)
|
||||
.await?;
|
||||
let mut builder =
|
||||
resource::get_check_permissions::<Builder>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
)
|
||||
.await?;
|
||||
// replace server id of builder
|
||||
if let BuilderConfig::Server(config) = &mut builder.config {
|
||||
config.server_id.clone_from(
|
||||
@@ -197,7 +187,7 @@ impl Resolve<ExportResourcesToToml, User> for State {
|
||||
res.builders.push(convert_resource(builder, &names.tags))
|
||||
}
|
||||
ResourceTarget::Build(id) => {
|
||||
let mut build = Build::get_resource_check_permissions(
|
||||
let mut build = resource::get_check_permissions::<Build>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -213,13 +203,12 @@ impl Resolve<ExportResourcesToToml, User> for State {
|
||||
res.builds.push(convert_resource(build, &names.tags))
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
let mut deployment =
|
||||
Deployment::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
)
|
||||
.await?;
|
||||
let mut deployment = resource::get_check_permissions::<
|
||||
Deployment,
|
||||
>(
|
||||
&id, &user, PermissionLevel::Read
|
||||
)
|
||||
.await?;
|
||||
// replace deployment server with name
|
||||
deployment.config.server_id.clone_from(
|
||||
names
|
||||
@@ -240,7 +229,7 @@ impl Resolve<ExportResourcesToToml, User> for State {
|
||||
.push(convert_resource(deployment, &names.tags))
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
let mut repo = Repo::get_resource_check_permissions(
|
||||
let mut repo = resource::get_check_permissions::<Repo>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -270,7 +259,7 @@ impl Resolve<ExportResourcesToToml, User> for State {
|
||||
.await
|
||||
.context("failed to add user groups")?;
|
||||
|
||||
let toml = toml::to_string_pretty(&res)
|
||||
let toml = toml::to_string(&res)
|
||||
.context("failed to serialize resources to toml")?;
|
||||
|
||||
Ok(ExportResourcesToTomlResponse { toml })
|
||||
@@ -283,7 +272,7 @@ async fn add_procedure(
|
||||
user: &User,
|
||||
names: &ResourceNames,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut procedure = Procedure::get_resource_check_permissions(
|
||||
let mut procedure = resource::get_check_permissions::<Procedure>(
|
||||
id,
|
||||
user,
|
||||
PermissionLevel::Read,
|
||||
|
||||
@@ -14,7 +14,9 @@ use monitor_client::{
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
server_template::ServerTemplate,
|
||||
update::{ResourceTarget, Update, UpdateListItem},
|
||||
update::{
|
||||
ResourceTarget, ResourceTargetVariant, Update, UpdateListItem,
|
||||
},
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
@@ -26,7 +28,8 @@ use mungos::{
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::resource::StateResource,
|
||||
helpers::query::get_resource_ids_for_non_admin,
|
||||
resource,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
@@ -42,20 +45,47 @@ impl Resolve<ListUpdates, User> for State {
|
||||
let query = if user.admin {
|
||||
query
|
||||
} else {
|
||||
let server_ids =
|
||||
Server::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let deployment_ids =
|
||||
Deployment::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let build_ids =
|
||||
Build::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let repo_ids =
|
||||
Repo::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let procedure_ids =
|
||||
Procedure::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let builder_ids =
|
||||
Builder::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let alerter_ids =
|
||||
Alerter::get_resource_ids_for_non_admin(&user.id).await?;
|
||||
let server_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Server,
|
||||
)
|
||||
.await?;
|
||||
let deployment_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Deployment,
|
||||
)
|
||||
.await?;
|
||||
let build_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Build,
|
||||
)
|
||||
.await?;
|
||||
let repo_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Repo,
|
||||
)
|
||||
.await?;
|
||||
let procedure_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Procedure,
|
||||
)
|
||||
.await?;
|
||||
let builder_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Builder,
|
||||
)
|
||||
.await?;
|
||||
let alerter_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::Alerter,
|
||||
)
|
||||
.await?;
|
||||
let server_template_ids = get_resource_ids_for_non_admin(
|
||||
&user.id,
|
||||
ResourceTargetVariant::ServerTemplate,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut query = query.unwrap_or_default();
|
||||
query.extend(doc! {
|
||||
"$or": [
|
||||
@@ -66,6 +96,7 @@ impl Resolve<ListUpdates, User> for State {
|
||||
{ "target.type": "Procedure", "target.id": { "$in": &procedure_ids } },
|
||||
{ "target.type": "Builder", "target.id": { "$in": &builder_ids } },
|
||||
{ "target.type": "Alerter", "target.id": { "$in": &alerter_ids } },
|
||||
{ "target.type": "ServerTemplate", "target.id": { "$in": &server_template_ids } },
|
||||
]
|
||||
});
|
||||
query.into()
|
||||
@@ -145,7 +176,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
Server::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Server>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -153,7 +184,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
Deployment::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -161,7 +192,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Build(id) => {
|
||||
Build::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Build>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -169,7 +200,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
Repo::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Repo>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -177,7 +208,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
Builder::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Builder>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -185,7 +216,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Alerter(id) => {
|
||||
Alerter::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Alerter>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -193,7 +224,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Procedure(id) => {
|
||||
Procedure::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
@@ -201,7 +232,7 @@ impl Resolve<GetUpdate, User> for State {
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::ServerTemplate(id) => {
|
||||
ServerTemplate::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<ServerTemplate>(
|
||||
id,
|
||||
&user,
|
||||
PermissionLevel::Read,
|
||||
|
||||
@@ -1,33 +1,15 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::write::{
|
||||
CopyAlerter, CreateAlerter, DeleteAlerter, UpdateAlerter,
|
||||
},
|
||||
entities::{
|
||||
alerter::{Alerter, AlerterInfo},
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
user::User,
|
||||
Operation,
|
||||
alerter::Alerter, permission::PermissionLevel, user::User,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_bson},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
state::{db_client, State},
|
||||
};
|
||||
use crate::{resource, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateAlerter, User> for State {
|
||||
@@ -37,57 +19,7 @@ impl Resolve<CreateAlerter, User> for State {
|
||||
CreateAlerter { name, config }: CreateAlerter,
|
||||
user: User,
|
||||
) -> anyhow::Result<Alerter> {
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let is_default = db_client()
|
||||
.await
|
||||
.alerters
|
||||
.find_one(None, None)
|
||||
.await?
|
||||
.is_none();
|
||||
let alerter = Alerter {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: AlerterInfo { is_default },
|
||||
};
|
||||
let alerter_id = db_client()
|
||||
.await
|
||||
.alerters
|
||||
.insert_one(alerter, None)
|
||||
.await
|
||||
.context("failed to add alerter to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let alerter = Alerter::get_resource(&alerter_id).await?;
|
||||
|
||||
create_permission(&user, &alerter, PermissionLevel::Write).await;
|
||||
|
||||
let mut update =
|
||||
make_update(&alerter, Operation::CreateAlerter, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"create alerter",
|
||||
format!(
|
||||
"created alerter\nid: {}\nname: {}",
|
||||
alerter.id, alerter.name
|
||||
),
|
||||
);
|
||||
update
|
||||
.push_simple_log("config", format!("{:#?}", alerter.config));
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(alerter)
|
||||
resource::create::<Alerter>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,57 +33,14 @@ impl Resolve<CopyAlerter, User> for State {
|
||||
) -> anyhow::Result<Alerter> {
|
||||
let Alerter {
|
||||
config,
|
||||
description,
|
||||
..
|
||||
} = Alerter::get_resource_check_permissions(
|
||||
} = resource::get_check_permissions::<Alerter>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let alerter = Alerter {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description,
|
||||
config,
|
||||
tags: Default::default(),
|
||||
info: Default::default(),
|
||||
};
|
||||
let alerter_id = db_client()
|
||||
.await
|
||||
.alerters
|
||||
.insert_one(alerter, None)
|
||||
.await
|
||||
.context("failed to add alerter to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let alerter = Alerter::get_resource(&alerter_id).await?;
|
||||
|
||||
create_permission(&user, &alerter, PermissionLevel::Write).await;
|
||||
|
||||
let mut update =
|
||||
make_update(&alerter, Operation::CreateAlerter, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"create alerter",
|
||||
format!(
|
||||
"created alerter\nid: {}\nname: {}",
|
||||
alerter.id, alerter.name
|
||||
),
|
||||
);
|
||||
|
||||
update
|
||||
.push_simple_log("config", format!("{:#?}", alerter.config));
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(alerter)
|
||||
resource::create::<Alerter>(&name, config.into(), &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,34 +52,7 @@ impl Resolve<DeleteAlerter, User> for State {
|
||||
DeleteAlerter { id }: DeleteAlerter,
|
||||
user: User,
|
||||
) -> anyhow::Result<Alerter> {
|
||||
let alerter = Alerter::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut update =
|
||||
make_update(&alerter, Operation::DeleteAlerter, &user);
|
||||
|
||||
delete_one_by_id(&db_client().await.alerters, &id, None)
|
||||
.await
|
||||
.context("failed to delete alerter from database")?;
|
||||
|
||||
delete_all_permissions_on_resource(&alerter).await;
|
||||
|
||||
update.push_simple_log(
|
||||
"delete alerter",
|
||||
format!("deleted alerter {}", alerter.name),
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
remove_from_recently_viewed(&alerter).await?;
|
||||
|
||||
Ok(alerter)
|
||||
resource::delete::<Alerter>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,39 +64,6 @@ impl Resolve<UpdateAlerter, User> for State {
|
||||
UpdateAlerter { id, config }: UpdateAlerter,
|
||||
user: User,
|
||||
) -> anyhow::Result<Alerter> {
|
||||
let alerter = Alerter::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut update =
|
||||
make_update(&alerter, Operation::UpdateAlerter, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"alerter update",
|
||||
serde_json::to_string_pretty(&config)?,
|
||||
);
|
||||
|
||||
let config = alerter.config.merge_partial(config);
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().await.alerters,
|
||||
&id,
|
||||
mungos::update::Update::FlattenSet(
|
||||
doc! { "config": to_bson(&config)? },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("failed to update alerter {id}"))?;
|
||||
|
||||
let alerter = Alerter::get_resource(&id).await?;
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(alerter)
|
||||
resource::update::<Alerter>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +1,21 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::write::*,
|
||||
entities::{
|
||||
build::Build,
|
||||
builder::Builder,
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
to_monitor_name,
|
||||
update::{Log, UpdateStatus},
|
||||
user::User,
|
||||
Operation,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::update_one_by_id,
|
||||
mongodb::bson::{doc, oid::ObjectId, to_document},
|
||||
entities::{build::Build, permission::PermissionLevel, user::User},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, empty_or_only_spaces,
|
||||
remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
use crate::{resource, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateBuild, User> for State {
|
||||
#[instrument(name = "CreateBuild", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
CreateBuild { name, mut config }: CreateBuild,
|
||||
CreateBuild { name, config }: CreateBuild,
|
||||
user: User,
|
||||
) -> anyhow::Result<Build> {
|
||||
if !user.admin && !user.create_build_permissions {
|
||||
return Err(anyhow!(
|
||||
"User does not have create build permissions."
|
||||
));
|
||||
}
|
||||
let name = to_monitor_name(&name);
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
if let Some(builder_id) = &config.builder_id {
|
||||
let builder = Builder::get_resource_check_permissions(builder_id, &user, PermissionLevel::Read)
|
||||
.await
|
||||
.context("cannot create build using this builder. user must have at least read permissions on the builder.")?;
|
||||
config.builder_id = Some(builder.id)
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let build = Build {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: Default::default(),
|
||||
};
|
||||
let build_id = db_client()
|
||||
.await
|
||||
.builds
|
||||
.insert_one(build, None)
|
||||
.await
|
||||
.context("failed to add build to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let build = Build::get_resource(&build_id).await?;
|
||||
|
||||
create_permission(&user, &build, PermissionLevel::Write).await;
|
||||
|
||||
let mut update =
|
||||
make_update(&build, Operation::CreateBuild, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"create build",
|
||||
format!(
|
||||
"created build\nid: {}\nname: {}",
|
||||
build.id, build.name
|
||||
),
|
||||
);
|
||||
|
||||
update.push_simple_log("config", format!("{:#?}", build.config));
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(build)
|
||||
resource::create::<Build>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,70 +27,16 @@ impl Resolve<CopyBuild, User> for State {
|
||||
CopyBuild { name, id }: CopyBuild,
|
||||
user: User,
|
||||
) -> anyhow::Result<Build> {
|
||||
if !user.admin && !user.create_build_permissions {
|
||||
return Err(anyhow!(
|
||||
"User does not have create build permissions."
|
||||
));
|
||||
}
|
||||
let name = to_monitor_name(&name);
|
||||
let Build {
|
||||
config,
|
||||
description,
|
||||
tags,
|
||||
..
|
||||
} = Build::get_resource_check_permissions(
|
||||
} = resource::get_check_permissions::<Build>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Builder::get_resource_check_permissions(&config.builder_id, &user, PermissionLevel::Read)
|
||||
.await
|
||||
.context("cannot create build using this builder. user must have at least read permissions on the builder.")?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let build = Build {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description,
|
||||
tags,
|
||||
config,
|
||||
info: Default::default(),
|
||||
};
|
||||
let build_id = db_client()
|
||||
.await
|
||||
.builds
|
||||
.insert_one(build, None)
|
||||
.await
|
||||
.context("failed to add build to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let build = Build::get_resource(&build_id).await?;
|
||||
|
||||
create_permission(&user, &build, PermissionLevel::Write).await;
|
||||
|
||||
let mut update =
|
||||
make_update(&build, Operation::CreateBuild, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"create build",
|
||||
format!(
|
||||
"created build\nid: {}\nname: {}",
|
||||
build.id, build.name
|
||||
),
|
||||
);
|
||||
update.push_simple_log(
|
||||
"config",
|
||||
serde_json::to_string_pretty(&build)?,
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(build)
|
||||
resource::create::<Build>(&name, config.into(), &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,55 +48,7 @@ impl Resolve<DeleteBuild, User> for State {
|
||||
DeleteBuild { id }: DeleteBuild,
|
||||
user: User,
|
||||
) -> anyhow::Result<Build> {
|
||||
if action_states()
|
||||
.build
|
||||
.get(&id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()?
|
||||
{
|
||||
return Err(anyhow!("build busy"));
|
||||
}
|
||||
|
||||
let build = Build::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut update =
|
||||
make_update(&build, Operation::DeleteBuild, &user);
|
||||
update.status = UpdateStatus::InProgress;
|
||||
update.id = add_update(update.clone()).await?;
|
||||
|
||||
let res = db_client()
|
||||
.await
|
||||
.builds
|
||||
.delete_one(doc! { "_id": ObjectId::from_str(&id)? }, None)
|
||||
.await
|
||||
.context("failed to delete build from database");
|
||||
|
||||
delete_all_permissions_on_resource(&build).await;
|
||||
|
||||
let log = match res {
|
||||
Ok(_) => Log::simple(
|
||||
"delete build",
|
||||
format!("deleted build {}", build.name),
|
||||
),
|
||||
Err(e) => Log::error(
|
||||
"delete build",
|
||||
format!("failed to delete build\n{e:#?}"),
|
||||
),
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
update.finalize();
|
||||
update_update(update).await?;
|
||||
|
||||
remove_from_recently_viewed(&build).await?;
|
||||
|
||||
Ok(build)
|
||||
resource::delete::<Build>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,71 +57,9 @@ impl Resolve<UpdateBuild, User> for State {
|
||||
#[instrument(name = "UpdateBuild", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
UpdateBuild { id, mut config }: UpdateBuild,
|
||||
UpdateBuild { id, config }: UpdateBuild,
|
||||
user: User,
|
||||
) -> anyhow::Result<Build> {
|
||||
if action_states()
|
||||
.build
|
||||
.get(&id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()?
|
||||
{
|
||||
return Err(anyhow!("build busy"));
|
||||
}
|
||||
|
||||
let build = Build::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(builder_id) = &config.builder_id {
|
||||
let builder = Builder::get_resource_check_permissions(builder_id, &user, PermissionLevel::Read)
|
||||
.await
|
||||
.context("cannot create build using this builder. user must have at least read permissions on the builder.")?;
|
||||
config.builder_id = Some(builder.id)
|
||||
}
|
||||
|
||||
if let Some(build_args) = &mut config.build_args {
|
||||
build_args.retain(|v| {
|
||||
!empty_or_only_spaces(&v.variable)
|
||||
&& !empty_or_only_spaces(&v.value)
|
||||
})
|
||||
}
|
||||
if let Some(extra_args) = &mut config.extra_args {
|
||||
extra_args.retain(|v| !empty_or_only_spaces(v))
|
||||
}
|
||||
|
||||
let config_doc = to_document(&config)
|
||||
.context("failed to serialize config to bson document")?;
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().await.builds,
|
||||
&build.id,
|
||||
mungos::update::Update::FlattenSet(
|
||||
doc! { "config": config_doc },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to update build on database")?;
|
||||
|
||||
let mut update =
|
||||
make_update(&build, Operation::UpdateBuild, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"build update",
|
||||
serde_json::to_string_pretty(&config)
|
||||
.context("failed to serialize config to json")?,
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
let build = Build::get_resource(&build.id).await?;
|
||||
Ok(build)
|
||||
resource::update::<Build>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,116 +1,23 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::write::*,
|
||||
entities::{
|
||||
builder::{
|
||||
Builder, PartialBuilderConfig, PartialServerBuilderConfig,
|
||||
},
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
update::{Log, ResourceTarget, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
builder::Builder, permission::PermissionLevel, user::User,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_document},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialBuilderConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
match config {
|
||||
PartialBuilderConfig::Server(PartialServerBuilderConfig {
|
||||
server_id: Some(server_id),
|
||||
}) if !server_id.is_empty() => {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
*server_id = server.id;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
use crate::{resource, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateBuilder, User> for State {
|
||||
#[instrument(name = "CreateBuilder", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
CreateBuilder { name, mut config }: CreateBuilder,
|
||||
CreateBuilder { name, config }: CreateBuilder,
|
||||
user: User,
|
||||
) -> anyhow::Result<Builder> {
|
||||
let start_ts = monitor_timestamp();
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
validate_config(&mut config, &user).await?;
|
||||
|
||||
let builder = Builder {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: Default::default(),
|
||||
};
|
||||
let builder_id = db_client()
|
||||
.await
|
||||
.builders
|
||||
.insert_one(builder, None)
|
||||
.await
|
||||
.context("failed to add builder to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let builder = Builder::get_resource(&builder_id).await?;
|
||||
create_permission(&user, &builder, PermissionLevel::Write).await;
|
||||
let update = Update {
|
||||
target: ResourceTarget::Builder(builder_id),
|
||||
operation: Operation::CreateBuilder,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
logs: vec![
|
||||
Log::simple(
|
||||
"create builder",
|
||||
format!(
|
||||
"created builder\nid: {}\nname: {}",
|
||||
builder.id, builder.name
|
||||
),
|
||||
),
|
||||
Log::simple("config", format!("{:#?}", builder.config)),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(builder)
|
||||
resource::create::<Builder>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,61 +29,13 @@ impl Resolve<CopyBuilder, User> for State {
|
||||
CopyBuilder { name, id }: CopyBuilder,
|
||||
user: User,
|
||||
) -> anyhow::Result<Builder> {
|
||||
let Builder {
|
||||
config,
|
||||
description,
|
||||
..
|
||||
} = Builder::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
let Builder { config, .. } = resource::get_check_permissions::<
|
||||
Builder,
|
||||
>(
|
||||
&id, &user, PermissionLevel::Write
|
||||
)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let builder = Builder {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description,
|
||||
tags: Default::default(),
|
||||
config,
|
||||
info: (),
|
||||
};
|
||||
let builder_id = db_client()
|
||||
.await
|
||||
.builders
|
||||
.insert_one(builder, None)
|
||||
.await
|
||||
.context("failed to add builder to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let builder = Builder::get_resource(&builder_id).await?;
|
||||
create_permission(&user, &builder, PermissionLevel::Write).await;
|
||||
let update = Update {
|
||||
target: ResourceTarget::Builder(builder_id),
|
||||
operation: Operation::CreateBuilder,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
logs: vec![
|
||||
Log::simple(
|
||||
"create builder",
|
||||
format!(
|
||||
"created builder\nid: {}\nname: {}",
|
||||
builder.id, builder.name
|
||||
),
|
||||
),
|
||||
Log::simple("config", format!("{:#?}", builder.config)),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(builder)
|
||||
resource::create::<Builder>(&name, config.into(), &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,46 +47,7 @@ impl Resolve<DeleteBuilder, User> for State {
|
||||
DeleteBuilder { id }: DeleteBuilder,
|
||||
user: User,
|
||||
) -> anyhow::Result<Builder> {
|
||||
let builder = Builder::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// remove the builder from any attached builds
|
||||
db_client()
|
||||
.await
|
||||
.builds
|
||||
.update_many(
|
||||
doc! { "config.builder.params.builder_id": &id },
|
||||
mungos::update::Update::Set(
|
||||
doc! { "config.builder.params.builder_id": "" },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
delete_one_by_id(&db_client().await.builders, &id, None)
|
||||
.await
|
||||
.context("failed to delete builder from database")?;
|
||||
|
||||
delete_all_permissions_on_resource(&builder).await;
|
||||
|
||||
let mut update =
|
||||
make_update(&builder, Operation::DeleteBuilder, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"delete builder",
|
||||
format!("deleted builder {}", builder.name),
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
remove_from_recently_viewed(&builder).await?;
|
||||
|
||||
Ok(builder)
|
||||
resource::delete::<Builder>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,48 +56,9 @@ impl Resolve<UpdateBuilder, User> for State {
|
||||
#[instrument(name = "UpdateBuilder", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
UpdateBuilder { id, mut config }: UpdateBuilder,
|
||||
UpdateBuilder { id, config }: UpdateBuilder,
|
||||
user: User,
|
||||
) -> anyhow::Result<Builder> {
|
||||
let builder = Builder::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
validate_config(&mut config, &user).await?;
|
||||
|
||||
let mut update = Update {
|
||||
target: ResourceTarget::Builder(id.clone()),
|
||||
operation: Operation::UpdateBuilder,
|
||||
start_ts: monitor_timestamp(),
|
||||
logs: vec![Log::simple(
|
||||
"builder update",
|
||||
serde_json::to_string_pretty(&config)
|
||||
.context("failed to serialize config update")?,
|
||||
)],
|
||||
operator: user.id.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let config = builder.config.merge_partial(config);
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().await.builders,
|
||||
&id,
|
||||
mungos::update::Update::FlattenSet(
|
||||
doc! { "config": to_document(&config)? },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let builder = Builder::get_resource(&id).await?;
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(builder)
|
||||
resource::update::<Builder>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +1,41 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::write::*,
|
||||
entities::{
|
||||
all_logs_success,
|
||||
build::Build,
|
||||
deployment::{
|
||||
Deployment, DeploymentImage, DockerContainerState,
|
||||
PartialDeploymentConfig,
|
||||
},
|
||||
deployment::{Deployment, DockerContainerState},
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
to_monitor_name,
|
||||
update::{Log, Update, UpdateStatus},
|
||||
update::Update,
|
||||
user::User,
|
||||
Operation,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_document},
|
||||
};
|
||||
use mungos::{by_id::update_one_by_id, mongodb::bson::doc};
|
||||
use periphery_client::api;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, empty_or_only_spaces, periphery_client,
|
||||
periphery_client,
|
||||
query::get_deployment_state,
|
||||
remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update, update_update},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
resource,
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialDeploymentConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(server_id) = &config.server_id {
|
||||
if !server_id.is_empty() {
|
||||
let server = Server::get_resource_check_permissions(server_id, user, PermissionLevel::Write)
|
||||
.await
|
||||
.context("cannot create deployment on this server. user must have update permissions on the server to perform this action.")?;
|
||||
config.server_id = Some(server.id);
|
||||
}
|
||||
}
|
||||
if let Some(DeploymentImage::Build { build_id, version }) =
|
||||
&config.image
|
||||
{
|
||||
if !build_id.is_empty() {
|
||||
let build = Build::get_resource_check_permissions(build_id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.context("cannot create deployment with this build attached. user must have at least read permissions on the build to perform this action.")?;
|
||||
config.image = Some(DeploymentImage::Build {
|
||||
build_id: build.id,
|
||||
version: version.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(volumes) = &mut config.volumes {
|
||||
volumes.retain(|v| {
|
||||
!empty_or_only_spaces(&v.local)
|
||||
&& !empty_or_only_spaces(&v.container)
|
||||
})
|
||||
}
|
||||
if let Some(ports) = &mut config.ports {
|
||||
ports.retain(|v| {
|
||||
!empty_or_only_spaces(&v.local)
|
||||
&& !empty_or_only_spaces(&v.container)
|
||||
})
|
||||
}
|
||||
if let Some(environment) = &mut config.environment {
|
||||
environment.retain(|v| {
|
||||
!empty_or_only_spaces(&v.variable)
|
||||
&& !empty_or_only_spaces(&v.value)
|
||||
})
|
||||
}
|
||||
if let Some(extra_args) = &mut config.extra_args {
|
||||
extra_args.retain(|v| !empty_or_only_spaces(v))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateDeployment, User> for State {
|
||||
#[instrument(name = "CreateDeployment", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
CreateDeployment { name, mut config }: CreateDeployment,
|
||||
CreateDeployment { name, config }: CreateDeployment,
|
||||
user: User,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
let name = to_monitor_name(&name);
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
validate_config(&mut config, &user).await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = Deployment {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: (),
|
||||
};
|
||||
let deployment_id = db_client()
|
||||
.await
|
||||
.deployments
|
||||
.insert_one(&deployment, None)
|
||||
.await
|
||||
.context("failed to add deployment to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let deployment = Deployment::get_resource(&deployment_id).await?;
|
||||
create_permission(&user, &deployment, PermissionLevel::Write)
|
||||
.await;
|
||||
|
||||
let mut update =
|
||||
make_update(&deployment, Operation::CreateDeployment, &user);
|
||||
update.push_simple_log(
|
||||
"create deployment",
|
||||
format!(
|
||||
"created deployment\nid: {}\nname: {}",
|
||||
deployment.id, deployment.name
|
||||
),
|
||||
);
|
||||
update
|
||||
.push_simple_log("config", format!("{:#?}", deployment.config));
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(deployment)
|
||||
resource::create::<Deployment>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,71 +47,14 @@ impl Resolve<CopyDeployment, User> for State {
|
||||
CopyDeployment { name, id }: CopyDeployment,
|
||||
user: User,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
let name = to_monitor_name(&name);
|
||||
let Deployment {
|
||||
config,
|
||||
description,
|
||||
tags,
|
||||
..
|
||||
} = Deployment::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
if !config.server_id.is_empty() {
|
||||
Server::get_resource_check_permissions(&config.server_id, &user, PermissionLevel::Write)
|
||||
.await
|
||||
.context("cannot create deployment on this server. user must have update permissions on the server to perform this action.")?;
|
||||
}
|
||||
if let DeploymentImage::Build { build_id, .. } = &config.image {
|
||||
if !build_id.is_empty() {
|
||||
Build::get_resource_check_permissions(build_id, &user, PermissionLevel::Read)
|
||||
.await
|
||||
.context("cannot create deployment with this build attached. user must have at least read permissions on the build to perform this action.")?;
|
||||
}
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let deployment = Deployment {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description,
|
||||
tags,
|
||||
config,
|
||||
info: (),
|
||||
};
|
||||
let deployment_id = db_client()
|
||||
.await
|
||||
.deployments
|
||||
.insert_one(&deployment, None)
|
||||
.await
|
||||
.context("failed to add deployment to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let deployment = Deployment::get_resource(&deployment_id).await?;
|
||||
|
||||
create_permission(&user, &deployment, PermissionLevel::Write)
|
||||
.await;
|
||||
|
||||
let mut update =
|
||||
make_update(&deployment, Operation::CreateDeployment, &user);
|
||||
update.push_simple_log(
|
||||
"create deployment",
|
||||
format!(
|
||||
"created deployment\nid: {}\nname: {}",
|
||||
deployment.id, deployment.name
|
||||
),
|
||||
);
|
||||
update
|
||||
.push_simple_log("config", format!("{:#?}", deployment.config));
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(deployment)
|
||||
let Deployment { config, .. } =
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
resource::create::<Deployment>(&name, config.into(), &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,109 +66,7 @@ impl Resolve<DeleteDeployment, User> for State {
|
||||
DeleteDeployment { id }: DeleteDeployment,
|
||||
user: User,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// get the action state for the deployment (or insert default).
|
||||
let action_state = action_states()
|
||||
.deployment
|
||||
.get_or_insert_default(&deployment.id)
|
||||
.await;
|
||||
|
||||
// Will check to ensure deployment not already busy before updating, and return Err if so.
|
||||
// The returned guard will set the action state back to default when dropped.
|
||||
let _action_guard =
|
||||
action_state.update(|state| state.deleting = true)?;
|
||||
|
||||
let state = get_deployment_state(&deployment)
|
||||
.await
|
||||
.context("failed to get container state")?;
|
||||
|
||||
let mut update =
|
||||
make_update(&deployment, Operation::DeleteDeployment, &user);
|
||||
update.in_progress();
|
||||
|
||||
update.id = add_update(update.clone()).await?;
|
||||
|
||||
if !matches!(
|
||||
state,
|
||||
DockerContainerState::NotDeployed
|
||||
| DockerContainerState::Unknown
|
||||
) {
|
||||
// container needs to be destroyed
|
||||
let server =
|
||||
Server::get_resource(&deployment.config.server_id).await;
|
||||
if let Err(e) = server {
|
||||
update.logs.push(Log::error(
|
||||
"remove container",
|
||||
format!(
|
||||
"failed to retrieve server at {} from db | {e:#?}",
|
||||
deployment.config.server_id
|
||||
),
|
||||
));
|
||||
} else if let Ok(server) = server {
|
||||
match periphery_client(&server) {
|
||||
Ok(periphery) => match periphery
|
||||
.request(api::container::RemoveContainer {
|
||||
name: deployment.name.clone(),
|
||||
signal: deployment.config.termination_signal.into(),
|
||||
time: deployment.config.termination_timeout.into(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(log) => update.logs.push(log),
|
||||
Err(e) => update.push_error_log(
|
||||
"remove container",
|
||||
format!(
|
||||
"failed to remove container on periphery | {e:#?}"
|
||||
),
|
||||
),
|
||||
},
|
||||
Err(e) => update.push_error_log(
|
||||
"remove container",
|
||||
format!(
|
||||
"failed to remove container on periphery | {e:#?}"
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let res = delete_one_by_id(
|
||||
&db_client().await.deployments,
|
||||
&deployment.id,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to delete deployment from mongo");
|
||||
|
||||
let log = match res {
|
||||
Ok(_) => Log::simple(
|
||||
"delete deployment",
|
||||
format!("deleted deployment {}", deployment.name),
|
||||
),
|
||||
Err(e) => Log::error(
|
||||
"delete deployment",
|
||||
format!("failed to delete deployment\n{e:#?}"),
|
||||
),
|
||||
};
|
||||
|
||||
delete_all_permissions_on_resource(&deployment).await;
|
||||
|
||||
update.logs.push(log);
|
||||
update.end_ts = Some(monitor_timestamp());
|
||||
update.status = UpdateStatus::Complete;
|
||||
update.success = all_logs_success(&update.logs);
|
||||
|
||||
update_update(update).await?;
|
||||
|
||||
remove_from_recently_viewed(&deployment).await?;
|
||||
|
||||
Ok(deployment)
|
||||
resource::delete::<Deployment>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,57 +75,10 @@ impl Resolve<UpdateDeployment, User> for State {
|
||||
#[instrument(name = "UpdateDeployment", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
UpdateDeployment { id, mut config }: UpdateDeployment,
|
||||
UpdateDeployment { id, config }: UpdateDeployment,
|
||||
user: User,
|
||||
) -> anyhow::Result<Deployment> {
|
||||
if action_states()
|
||||
.deployment
|
||||
.get(&id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()?
|
||||
{
|
||||
return Err(anyhow!("deployment busy"));
|
||||
}
|
||||
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut update =
|
||||
make_update(&deployment, Operation::UpdateDeployment, &user);
|
||||
|
||||
validate_config(&mut config, &user).await?;
|
||||
|
||||
let config_doc = to_document(&config)
|
||||
.context("failed to serialize config to bson")?;
|
||||
update_one_by_id(
|
||||
&db_client().await.deployments,
|
||||
&id,
|
||||
mungos::update::Update::FlattenSet(
|
||||
doc! { "config": config_doc },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to update server on mongo")?;
|
||||
|
||||
update.push_simple_log(
|
||||
"deployment update",
|
||||
serde_json::to_string_pretty(&config)
|
||||
.context("failed to serialize config to json")?,
|
||||
);
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
let deployment: Deployment =
|
||||
Deployment::get_resource(&id).await?;
|
||||
|
||||
Ok(deployment)
|
||||
resource::update::<Deployment>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +90,7 @@ impl Resolve<RenameDeployment, User> for State {
|
||||
RenameDeployment { id, name }: RenameDeployment,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
let deployment = resource::get_check_permissions::<Deployment>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
@@ -445,7 +134,7 @@ impl Resolve<RenameDeployment, User> for State {
|
||||
|
||||
if container_state != DockerContainerState::NotDeployed {
|
||||
let server =
|
||||
Server::get_resource(&deployment.config.server_id).await?;
|
||||
resource::get::<Server>(&deployment.config.server_id).await?;
|
||||
let log = periphery_client(&server)?
|
||||
.request(api::container::RenameContainer {
|
||||
curr_name: deployment.name.clone(),
|
||||
|
||||
@@ -11,7 +11,7 @@ use monitor_client::{
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{helpers::resource::StateResource, state::State};
|
||||
use crate::{resource, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<UpdateDescription, User> for State {
|
||||
@@ -31,31 +31,68 @@ impl Resolve<UpdateDescription, User> for State {
|
||||
))
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
Server::update_description(&id, &description, &user).await?;
|
||||
resource::update_description::<Server>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
Deployment::update_description(&id, &description, &user)
|
||||
.await?;
|
||||
resource::update_description::<Deployment>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Build(id) => {
|
||||
Build::update_description(&id, &description, &user).await?;
|
||||
resource::update_description::<Build>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
Repo::update_description(&id, &description, &user).await?;
|
||||
resource::update_description::<Repo>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
Builder::update_description(&id, &description, &user).await?;
|
||||
resource::update_description::<Builder>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Alerter(id) => {
|
||||
Alerter::update_description(&id, &description, &user).await?;
|
||||
resource::update_description::<Alerter>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Procedure(id) => {
|
||||
Procedure::update_description(&id, &description, &user)
|
||||
.await?;
|
||||
resource::update_description::<Procedure>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::ServerTemplate(id) => {
|
||||
ServerTemplate::update_description(&id, &description, &user)
|
||||
.await?;
|
||||
resource::update_description::<ServerTemplate>(
|
||||
&id,
|
||||
&description,
|
||||
&user,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(UpdateDescriptionResponse {})
|
||||
|
||||
@@ -1,232 +1,26 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::{execute::Execution, write::*},
|
||||
api::write::*,
|
||||
entities::{
|
||||
build::Build,
|
||||
deployment::Deployment,
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
procedure::{PartialProcedureConfig, Procedure},
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
update::Log,
|
||||
user::User,
|
||||
Operation,
|
||||
permission::PermissionLevel, procedure::Procedure, user::User,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_document},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
use crate::{resource, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateProcedure, User> for State {
|
||||
#[instrument(name = "CreateProcedure", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
CreateProcedure { name, mut config }: CreateProcedure,
|
||||
CreateProcedure { name, config }: CreateProcedure,
|
||||
user: User,
|
||||
) -> anyhow::Result<CreateProcedureResponse> {
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
validate_procedure_config(&mut config, &user, None).await?;
|
||||
|
||||
let procedure = Procedure {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
info: Default::default(),
|
||||
config: config.into(),
|
||||
};
|
||||
let procedure_id = db_client()
|
||||
.await
|
||||
.procedures
|
||||
.insert_one(procedure, None)
|
||||
.await
|
||||
.context("failed to add procedure to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let procedure = Procedure::get_resource(&procedure_id).await?;
|
||||
|
||||
create_permission(&user, &procedure, PermissionLevel::Write)
|
||||
.await;
|
||||
|
||||
let mut update =
|
||||
make_update(&procedure, Operation::CreateProcedure, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"create procedure",
|
||||
format!(
|
||||
"created procedure\nid: {}\nname: {}",
|
||||
procedure.id, procedure.name
|
||||
),
|
||||
);
|
||||
|
||||
update
|
||||
.push_simple_log("config", format!("{:#?}", procedure.config));
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(procedure)
|
||||
resource::create::<Procedure>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
async fn validate_procedure_config(
|
||||
config: &mut PartialProcedureConfig,
|
||||
user: &User,
|
||||
id: Option<&str>,
|
||||
) -> anyhow::Result<()> {
|
||||
let Some(executions) = &mut config.executions else {
|
||||
return Ok(());
|
||||
};
|
||||
for exec in executions {
|
||||
match &mut exec.execution {
|
||||
Execution::None(_) => {}
|
||||
Execution::RunProcedure(params) => {
|
||||
let procedure = Procedure::get_resource_check_permissions(
|
||||
¶ms.procedure,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
match id {
|
||||
Some(id) if procedure.id == id => {
|
||||
return Err(anyhow!(
|
||||
"Cannot have self-referential procedure"
|
||||
))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
params.procedure = procedure.id;
|
||||
}
|
||||
Execution::RunBuild(params) => {
|
||||
let build = Build::get_resource_check_permissions(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
}
|
||||
Execution::Deploy(params) => {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StartContainer(params) => {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StopContainer(params) => {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StopAllContainers(params) => {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RemoveContainer(params) => {
|
||||
let deployment = Deployment::get_resource_check_permissions(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::CloneRepo(params) => {
|
||||
let repo = Repo::get_resource_check_permissions(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::PullRepo(params) => {
|
||||
let repo = Repo::get_resource_check_permissions(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::PruneDockerNetworks(params) => {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneDockerImages(params) => {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneDockerContainers(params) => {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CopyProcedure, User> for State {
|
||||
#[instrument(name = "CopyProcedure", skip(self, user))]
|
||||
@@ -235,62 +29,14 @@ impl Resolve<CopyProcedure, User> for State {
|
||||
CopyProcedure { name, id }: CopyProcedure,
|
||||
user: User,
|
||||
) -> anyhow::Result<CopyProcedureResponse> {
|
||||
let Procedure {
|
||||
config,
|
||||
description,
|
||||
tags,
|
||||
..
|
||||
} = Procedure::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let build = Procedure {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description,
|
||||
tags,
|
||||
config,
|
||||
info: Default::default(),
|
||||
};
|
||||
let procedure_id = db_client()
|
||||
.await
|
||||
.procedures
|
||||
.insert_one(build, None)
|
||||
.await
|
||||
.context("failed to add build to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let procedure = Procedure::get_resource(&procedure_id).await?;
|
||||
|
||||
create_permission(&user, &procedure, PermissionLevel::Write)
|
||||
.await;
|
||||
|
||||
let mut update =
|
||||
make_update(&procedure, Operation::CreateProcedure, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"create procedure",
|
||||
format!(
|
||||
"created procedure\nid: {}\nname: {}",
|
||||
procedure.id, procedure.name
|
||||
),
|
||||
);
|
||||
update.push_simple_log(
|
||||
"config",
|
||||
serde_json::to_string_pretty(&procedure)?,
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(procedure)
|
||||
let Procedure { config, .. } =
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
resource::create::<Procedure>(&name, config.into(), &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,49 +45,10 @@ impl Resolve<UpdateProcedure, User> for State {
|
||||
#[instrument(name = "UpdateProcedure", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
UpdateProcedure { id, mut config }: UpdateProcedure,
|
||||
UpdateProcedure { id, config }: UpdateProcedure,
|
||||
user: User,
|
||||
) -> anyhow::Result<UpdateProcedureResponse> {
|
||||
let procedure = Procedure::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
validate_procedure_config(
|
||||
&mut config,
|
||||
&user,
|
||||
Some(&procedure.id),
|
||||
)
|
||||
.await?;
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().await.procedures,
|
||||
&procedure.id,
|
||||
mungos::update::Update::FlattenSet(
|
||||
doc! { "config": to_document(&config)? },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to update procedure on database")?;
|
||||
|
||||
let mut update =
|
||||
make_update(&procedure, Operation::UpdateProcedure, &user);
|
||||
|
||||
update.push_simple_log(
|
||||
"procedure update",
|
||||
serde_json::to_string_pretty(&config)?,
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
let procedure = Procedure::get_resource(&procedure.id).await?;
|
||||
|
||||
Ok(procedure)
|
||||
resource::update::<Procedure>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,53 +60,6 @@ impl Resolve<DeleteProcedure, User> for State {
|
||||
DeleteProcedure { id }: DeleteProcedure,
|
||||
user: User,
|
||||
) -> anyhow::Result<DeleteProcedureResponse> {
|
||||
// needs to pull its id from all container procedures
|
||||
if action_states()
|
||||
.procedure
|
||||
.get(&id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()?
|
||||
{
|
||||
return Err(anyhow!("procedure busy"));
|
||||
}
|
||||
|
||||
let procedure = Procedure::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut update =
|
||||
make_update(&procedure, Operation::DeleteProcedure, &user);
|
||||
update.in_progress();
|
||||
update.id = add_update(update.clone()).await?;
|
||||
|
||||
let res =
|
||||
delete_one_by_id(&db_client().await.procedures, &id, None)
|
||||
.await
|
||||
.context("failed to delete build from database");
|
||||
|
||||
delete_all_permissions_on_resource(&procedure).await;
|
||||
|
||||
let log = match res {
|
||||
Ok(_) => Log::simple(
|
||||
"delete procedure",
|
||||
format!("deleted procedure {}", procedure.name),
|
||||
),
|
||||
Err(e) => Log::error(
|
||||
"delete procedure",
|
||||
format!("failed to delete procedure\n{e:#?}"),
|
||||
),
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
update.finalize();
|
||||
update_update(update).await?;
|
||||
|
||||
remove_from_recently_viewed(&procedure).await?;
|
||||
|
||||
Ok(procedure)
|
||||
resource::delete::<Procedure>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
+16
-311
@@ -1,132 +1,21 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::{execute, write::*},
|
||||
entities::{
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
repo::{PartialRepoConfig, Repo},
|
||||
server::Server,
|
||||
to_monitor_name,
|
||||
update::{Log, ResourceTarget, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
},
|
||||
api::write::*,
|
||||
entities::{permission::PermissionLevel, repo::Repo, user::User},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_bson},
|
||||
};
|
||||
use periphery_client::api;
|
||||
use resolver_api::Resolve;
|
||||
use serror::serialize_error_pretty;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, periphery_client, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
state::{action_states, db_client, State},
|
||||
};
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialRepoConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
match &config.server_id {
|
||||
Some(server_id) if !server_id.is_empty() => {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await
|
||||
.context("cannot create repo on this server. user must have update permissions on the server.")?;
|
||||
config.server_id = Some(server.id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
use crate::{resource, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateRepo, User> for State {
|
||||
#[instrument(name = "CreateRepo", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
CreateRepo { name, mut config }: CreateRepo,
|
||||
CreateRepo { name, config }: CreateRepo,
|
||||
user: User,
|
||||
) -> anyhow::Result<Repo> {
|
||||
let name = to_monitor_name(&name);
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
validate_config(&mut config, &user).await?;
|
||||
let start_ts = monitor_timestamp();
|
||||
let repo = Repo {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: Default::default(),
|
||||
};
|
||||
let repo_id = db_client()
|
||||
.await
|
||||
.repos
|
||||
.insert_one(repo, None)
|
||||
.await
|
||||
.context("failed to add repo to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
|
||||
let repo = Repo::get_resource(&repo_id).await?;
|
||||
|
||||
create_permission(&user, &repo, PermissionLevel::Write).await;
|
||||
|
||||
let update = Update {
|
||||
target: ResourceTarget::Repo(repo_id),
|
||||
operation: Operation::CreateRepo,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
logs: vec![
|
||||
Log::simple(
|
||||
"create repo",
|
||||
format!(
|
||||
"created repo\nid: {}\nname: {}",
|
||||
repo.id, repo.name
|
||||
),
|
||||
),
|
||||
Log::simple("config", format!("{:#?}", repo.config)),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
if !repo.config.repo.is_empty()
|
||||
&& !repo.config.server_id.is_empty()
|
||||
{
|
||||
let _ = self
|
||||
.resolve(
|
||||
execute::CloneRepo {
|
||||
repo: repo.id.clone(),
|
||||
},
|
||||
user,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(repo)
|
||||
resource::create::<Repo>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,59 +27,14 @@ impl Resolve<CopyRepo, User> for State {
|
||||
CopyRepo { name, id }: CopyRepo,
|
||||
user: User,
|
||||
) -> anyhow::Result<Repo> {
|
||||
let Repo {
|
||||
config,
|
||||
description,
|
||||
tags,
|
||||
..
|
||||
} = Repo::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
if !config.server_id.is_empty() {
|
||||
Server::get_resource_check_permissions(
|
||||
&config.server_id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await
|
||||
.context("cannot create repo on this server. user must have update permissions on the server.")?;
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let repo = Repo {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description,
|
||||
tags,
|
||||
config,
|
||||
info: Default::default(),
|
||||
};
|
||||
let repo_id = db_client()
|
||||
.await
|
||||
.repos
|
||||
.insert_one(repo, None)
|
||||
.await
|
||||
.context("failed to add repo to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let repo = Repo::get_resource(&repo_id).await?;
|
||||
create_permission(&user, &repo, PermissionLevel::Write).await;
|
||||
let mut update = make_update(&repo, Operation::CreateRepo, &user);
|
||||
update.push_simple_log(
|
||||
"create repo",
|
||||
format!("created repo\nid: {}\nname: {}", repo.id, repo.name),
|
||||
);
|
||||
update.push_simple_log("config", format!("{:#?}", repo.config));
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(repo)
|
||||
let Repo { config, .. } =
|
||||
resource::get_check_permissions::<Repo>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
resource::create::<Repo>(&name, config.into(), &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,76 +46,7 @@ impl Resolve<DeleteRepo, User> for State {
|
||||
DeleteRepo { id }: DeleteRepo,
|
||||
user: User,
|
||||
) -> anyhow::Result<Repo> {
|
||||
let repo = Repo::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// get the action state for the repo (or insert default).
|
||||
let action_state =
|
||||
action_states().repo.get_or_insert_default(&repo.id).await;
|
||||
|
||||
// This will set action state back to default when dropped.
|
||||
// Will also check to ensure repo not already busy before updating.
|
||||
let _action_guard =
|
||||
action_state.update(|state| state.deleting = true)?;
|
||||
|
||||
let periphery = if repo.config.server_id.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let server =
|
||||
Server::get_resource(&repo.config.server_id).await?;
|
||||
let periphery = periphery_client(&server)?;
|
||||
Some(periphery)
|
||||
};
|
||||
|
||||
let mut update = make_update(&repo, Operation::DeleteRepo, &user);
|
||||
update.in_progress();
|
||||
update.id = add_update(update.clone()).await?;
|
||||
|
||||
let res =
|
||||
delete_one_by_id(&db_client().await.repos, &repo.id, None)
|
||||
.await
|
||||
.context("failed to delete repo from database");
|
||||
|
||||
delete_all_permissions_on_resource(&repo).await;
|
||||
|
||||
let log = match res {
|
||||
Ok(_) => Log::simple(
|
||||
"delete repo",
|
||||
format!("deleted repo {}", repo.name),
|
||||
),
|
||||
Err(e) => Log::error(
|
||||
"delete repo",
|
||||
format!("failed to delete repo\n{e:#?}"),
|
||||
),
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
|
||||
if let Some(periphery) = periphery {
|
||||
match periphery
|
||||
.request(api::git::DeleteRepo {
|
||||
name: repo.name.clone(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(log) => update.logs.push(log),
|
||||
Err(e) => update.logs.push(Log::error(
|
||||
"delete repo on periphery",
|
||||
serialize_error_pretty(&e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
update_update(update).await?;
|
||||
|
||||
remove_from_recently_viewed(&repo).await?;
|
||||
|
||||
Ok(repo)
|
||||
resource::delete::<Repo>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,79 +55,9 @@ impl Resolve<UpdateRepo, User> for State {
|
||||
#[instrument(name = "UpdateRepo", skip(self, user))]
|
||||
async fn resolve(
|
||||
&self,
|
||||
UpdateRepo { id, mut config }: UpdateRepo,
|
||||
UpdateRepo { id, config }: UpdateRepo,
|
||||
user: User,
|
||||
) -> anyhow::Result<Repo> {
|
||||
validate_config(&mut config, &user).await?;
|
||||
|
||||
let repo = Repo::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// get the action state for the repo (or insert default).
|
||||
let action_state =
|
||||
action_states().repo.get_or_insert_default(&repo.id).await;
|
||||
|
||||
// This will set action state back to default when dropped.
|
||||
// Will also check to ensure repo not already busy before updating.
|
||||
let _action_guard =
|
||||
action_state.update(|state| state.updating = true)?;
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().await.repos,
|
||||
&repo.id,
|
||||
mungos::update::Update::FlattenSet(
|
||||
doc! { "config": to_bson(&config)? },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to update repo on database")?;
|
||||
|
||||
let mut update = make_update(&repo, Operation::UpdateRepo, &user);
|
||||
update.in_progress();
|
||||
update.push_simple_log(
|
||||
"repo update",
|
||||
serde_json::to_string_pretty(&config).unwrap(),
|
||||
);
|
||||
update.id = add_update(update.clone()).await?;
|
||||
|
||||
if let Some(new_server_id) = config.server_id {
|
||||
if !repo.config.server_id.is_empty()
|
||||
&& new_server_id != repo.config.server_id
|
||||
{
|
||||
let old_server: anyhow::Result<Server> =
|
||||
Server::get_resource(&repo.config.server_id).await;
|
||||
let periphery =
|
||||
old_server.and_then(|server| periphery_client(&server));
|
||||
match periphery {
|
||||
Ok(periphery) => match periphery
|
||||
.request(api::git::DeleteRepo { name: repo.name })
|
||||
.await
|
||||
{
|
||||
Ok(mut log) => {
|
||||
log.stage = String::from("cleanup previous server");
|
||||
update.logs.push(log);
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"cleanup previous server",
|
||||
format!("failed to cleanup previous server | {e:#?}"),
|
||||
),
|
||||
},
|
||||
Err(e) => update.push_error_log(
|
||||
"cleanup previous server",
|
||||
format!("failed to cleanup previous server | {e:#?}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
update_update(update).await?;
|
||||
|
||||
Repo::get_resource(&repo.id).await
|
||||
resource::update::<Repo>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_timing_util::unix_timestamp_ms;
|
||||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use monitor_client::{
|
||||
api::write::*,
|
||||
@@ -9,27 +6,23 @@ use monitor_client::{
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
update::{Log, ResourceTarget, Update, UpdateStatus},
|
||||
update::{Update, UpdateStatus},
|
||||
user::User,
|
||||
Operation,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_bson},
|
||||
};
|
||||
use mungos::{by_id::update_one_by_id, mongodb::bson::doc};
|
||||
use periphery_client::api;
|
||||
use resolver_api::Resolve;
|
||||
use serror::serialize_error_pretty;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, periphery_client, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
periphery_client,
|
||||
update::{add_update, make_update, update_update},
|
||||
},
|
||||
monitor::update_cache_for_server,
|
||||
state::{action_states, db_client, server_status_cache, State},
|
||||
resource,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -40,61 +33,7 @@ impl Resolve<CreateServer, User> for State {
|
||||
CreateServer { name, config }: CreateServer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Server> {
|
||||
if !user.admin && !user.create_server_permissions {
|
||||
return Err(anyhow!(
|
||||
"user does not have create server permissions"
|
||||
));
|
||||
}
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let server = Server {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: (),
|
||||
};
|
||||
let server_id = db_client()
|
||||
.await
|
||||
.servers
|
||||
.insert_one(&server, None)
|
||||
.await
|
||||
.context("failed to add server to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let server = Server::get_resource(&server_id).await?;
|
||||
create_permission(&user, &server, PermissionLevel::Write).await;
|
||||
let update = Update {
|
||||
target: ResourceTarget::Server(server_id),
|
||||
operation: Operation::CreateServer,
|
||||
start_ts,
|
||||
end_ts: Some(monitor_timestamp()),
|
||||
operator: user.id.clone(),
|
||||
success: true,
|
||||
logs: vec![
|
||||
Log::simple(
|
||||
"create server",
|
||||
format!(
|
||||
"created server\nid: {}\nname: {}",
|
||||
server.id, server.name
|
||||
),
|
||||
),
|
||||
Log::simple("config", format!("{:#?}", server.config)),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
update_cache_for_server(&server).await;
|
||||
|
||||
Ok(server)
|
||||
resource::create::<Server>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,91 +45,7 @@ impl Resolve<DeleteServer, User> for State {
|
||||
DeleteServer { id }: DeleteServer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Server> {
|
||||
if action_states()
|
||||
.server
|
||||
.get(&id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()?
|
||||
{
|
||||
return Err(anyhow!("server busy"));
|
||||
}
|
||||
|
||||
let server = Server::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
db_client()
|
||||
.await
|
||||
.builders
|
||||
.update_many(
|
||||
doc! { "config.params.server_id": &id },
|
||||
doc! { "$set": { "config.params.server_id": "" } },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from builders")?;
|
||||
|
||||
db_client()
|
||||
.await
|
||||
.deployments
|
||||
.update_many(
|
||||
doc! { "config.server_id": &id },
|
||||
doc! { "$set": { "config.server_id": "" } },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from deployments")?;
|
||||
|
||||
db_client()
|
||||
.await
|
||||
.repos
|
||||
.update_many(
|
||||
doc! { "config.server_id": &id },
|
||||
doc! { "$set": { "config.server_id": "" } },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from repos")?;
|
||||
|
||||
db_client()
|
||||
.await
|
||||
.alerts
|
||||
.update_many(
|
||||
doc! { "target.type": "Server", "target.id": &id },
|
||||
doc! { "$set": {
|
||||
"resolved": true,
|
||||
"resolved_ts": unix_timestamp_ms() as i64
|
||||
} },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from repos")?;
|
||||
|
||||
delete_one_by_id(&db_client().await.servers, &id, None)
|
||||
.await
|
||||
.context("failed to delete server from mongo")?;
|
||||
|
||||
delete_all_permissions_on_resource(&server).await;
|
||||
|
||||
let mut update =
|
||||
make_update(&server, Operation::DeleteServer, &user);
|
||||
update.push_simple_log(
|
||||
"delete server",
|
||||
format!("deleted server {}", server.name),
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
server_status_cache().remove(&id).await;
|
||||
|
||||
remove_from_recently_viewed(&server).await?;
|
||||
|
||||
Ok(server)
|
||||
resource::delete::<Server>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,50 +57,7 @@ impl Resolve<UpdateServer, User> for State {
|
||||
UpdateServer { id, config }: UpdateServer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Server> {
|
||||
if action_states()
|
||||
.server
|
||||
.get(&id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()?
|
||||
{
|
||||
return Err(anyhow!("server busy"));
|
||||
}
|
||||
|
||||
let server = Server::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let mut update =
|
||||
make_update(&server, Operation::UpdateServer, &user);
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().await.servers,
|
||||
&id,
|
||||
mungos::update::Update::FlattenSet(
|
||||
doc! { "config": to_bson(&config)? },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to update server on mongo")?;
|
||||
|
||||
update.push_simple_log(
|
||||
"server update",
|
||||
serde_json::to_string_pretty(&config)?,
|
||||
);
|
||||
|
||||
let new_server = Server::get_resource(&id).await?;
|
||||
|
||||
update_cache_for_server(&new_server).await;
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(new_server)
|
||||
resource::update::<Server>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +69,7 @@ impl Resolve<RenameServer, User> for State {
|
||||
RenameServer { id, name }: RenameServer,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
@@ -287,7 +99,7 @@ impl Resolve<CreateNetwork, User> for State {
|
||||
CreateNetwork { server, name }: CreateNetwork,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
@@ -325,7 +137,7 @@ impl Resolve<DeleteNetwork, User> for State {
|
||||
DeleteNetwork { server, name }: DeleteNetwork,
|
||||
user: User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let server = Server::get_resource_check_permissions(
|
||||
let server = resource::get_check_permissions::<Server>(
|
||||
&server,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::async_trait;
|
||||
use monitor_client::{
|
||||
api::write::{
|
||||
@@ -8,24 +5,13 @@ use monitor_client::{
|
||||
UpdateServerTemplate,
|
||||
},
|
||||
entities::{
|
||||
monitor_timestamp, permission::PermissionLevel,
|
||||
server_template::ServerTemplate, user::User, Operation,
|
||||
permission::PermissionLevel, server_template::ServerTemplate,
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
mongodb::bson::{doc, oid::ObjectId, to_document},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission, remove_from_recently_viewed,
|
||||
resource::{delete_all_permissions_on_resource, StateResource},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
state::{db_client, State},
|
||||
};
|
||||
use crate::{resource, state::State};
|
||||
|
||||
#[async_trait]
|
||||
impl Resolve<CreateServerTemplate, User> for State {
|
||||
@@ -34,60 +20,7 @@ impl Resolve<CreateServerTemplate, User> for State {
|
||||
CreateServerTemplate { name, config }: CreateServerTemplate,
|
||||
user: User,
|
||||
) -> anyhow::Result<ServerTemplate> {
|
||||
if !user.admin {
|
||||
return Err(anyhow!("only admins can create server templates"));
|
||||
}
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
let server_template = ServerTemplate {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: monitor_timestamp(),
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: (),
|
||||
};
|
||||
let server_template_id = db_client()
|
||||
.await
|
||||
.server_templates
|
||||
.insert_one(server_template, None)
|
||||
.await
|
||||
.context("failed to add server_template to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let server_template =
|
||||
ServerTemplate::get_resource(&server_template_id).await?;
|
||||
create_permission(
|
||||
&user,
|
||||
&server_template,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await;
|
||||
let mut update = make_update(
|
||||
&server_template,
|
||||
Operation::CreateServerTemplate,
|
||||
&user,
|
||||
);
|
||||
update.push_simple_log(
|
||||
"create server template",
|
||||
format!(
|
||||
"created server template\nid: {}\nname: {}",
|
||||
server_template.id, server_template.name
|
||||
),
|
||||
);
|
||||
update.push_simple_log(
|
||||
"config",
|
||||
format!("{:#?}", server_template.config),
|
||||
);
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(server_template)
|
||||
resource::create::<ServerTemplate>(&name, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,64 +31,15 @@ impl Resolve<CopyServerTemplate, User> for State {
|
||||
CopyServerTemplate { name, id }: CopyServerTemplate,
|
||||
user: User,
|
||||
) -> anyhow::Result<ServerTemplate> {
|
||||
let ServerTemplate {
|
||||
config,
|
||||
description,
|
||||
..
|
||||
} = ServerTemplate::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let server_template = ServerTemplate {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: monitor_timestamp(),
|
||||
description,
|
||||
tags: Default::default(),
|
||||
config,
|
||||
info: (),
|
||||
};
|
||||
let server_template_id = db_client()
|
||||
let ServerTemplate { config, .. } =
|
||||
resource::get_check_permissions::<ServerTemplate>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
resource::create::<ServerTemplate>(&name, config.into(), &user)
|
||||
.await
|
||||
.server_templates
|
||||
.insert_one(server_template, None)
|
||||
.await
|
||||
.context("failed to add server_template to db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
let server_template =
|
||||
ServerTemplate::get_resource(&server_template_id).await?;
|
||||
create_permission(
|
||||
&user,
|
||||
&server_template,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await;
|
||||
let mut update = make_update(
|
||||
&server_template,
|
||||
Operation::CreateServerTemplate,
|
||||
&user,
|
||||
);
|
||||
update.push_simple_log(
|
||||
"create server template",
|
||||
format!(
|
||||
"created server template\nid: {}\nname: {}",
|
||||
server_template.id, server_template.name
|
||||
),
|
||||
);
|
||||
update.push_simple_log(
|
||||
"config",
|
||||
format!("{:#?}", server_template.config),
|
||||
);
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(server_template)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,37 +50,7 @@ impl Resolve<DeleteServerTemplate, User> for State {
|
||||
DeleteServerTemplate { id }: DeleteServerTemplate,
|
||||
user: User,
|
||||
) -> anyhow::Result<ServerTemplate> {
|
||||
let server_template =
|
||||
ServerTemplate::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
delete_one_by_id(&db_client().await.server_templates, &id, None)
|
||||
.await
|
||||
.context("failed to delete server templates from database")?;
|
||||
|
||||
delete_all_permissions_on_resource(&server_template).await;
|
||||
|
||||
let mut update = make_update(
|
||||
&server_template,
|
||||
Operation::DeleteServerTemplate,
|
||||
&user,
|
||||
);
|
||||
|
||||
update.push_simple_log(
|
||||
"delete server template",
|
||||
format!("deleted server template {}", server_template.name),
|
||||
);
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
remove_from_recently_viewed(&server_template).await?;
|
||||
|
||||
Ok(server_template)
|
||||
resource::delete::<ServerTemplate>(&id, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,43 +61,6 @@ impl Resolve<UpdateServerTemplate, User> for State {
|
||||
UpdateServerTemplate { id, config }: UpdateServerTemplate,
|
||||
user: User,
|
||||
) -> anyhow::Result<ServerTemplate> {
|
||||
let server_template =
|
||||
ServerTemplate::get_resource_check_permissions(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut update = make_update(
|
||||
&server_template,
|
||||
Operation::UpdateServerTemplate,
|
||||
&user,
|
||||
);
|
||||
|
||||
update.push_simple_log(
|
||||
"server template update",
|
||||
serde_json::to_string_pretty(&config)
|
||||
.context("failed to serialize config update")?,
|
||||
);
|
||||
|
||||
let config = server_template.config.merge_partial(config);
|
||||
let config = to_document(&config)
|
||||
.context("failed to serialize update to bson document")?;
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().await.server_templates,
|
||||
&id,
|
||||
mungos::update::Update::FlattenSet(doc! { "config": config }),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let server_template = ServerTemplate::get_resource(&id).await?;
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(server_template)
|
||||
resource::update::<ServerTemplate>(&id, config, &user).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,8 @@ use mungos::{
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
query::{get_tag, get_tag_check_owner},
|
||||
resource::StateResource,
|
||||
},
|
||||
helpers::query::{get_tag, get_tag_check_owner},
|
||||
resource,
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
@@ -99,13 +97,14 @@ impl Resolve<DeleteTag, User> for State {
|
||||
let tag = get_tag_check_owner(&id, &user).await?;
|
||||
|
||||
tokio::try_join!(
|
||||
Server::remove_tag_from_resources(&id,),
|
||||
Deployment::remove_tag_from_resources(&id,),
|
||||
Build::remove_tag_from_resources(&id,),
|
||||
Repo::remove_tag_from_resources(&id,),
|
||||
Builder::remove_tag_from_resources(&id,),
|
||||
Alerter::remove_tag_from_resources(&id,),
|
||||
Procedure::remove_tag_from_resources(&id,),
|
||||
resource::remove_tag_from_all::<Server>(&id),
|
||||
resource::remove_tag_from_all::<Deployment>(&id),
|
||||
resource::remove_tag_from_all::<Build>(&id),
|
||||
resource::remove_tag_from_all::<Repo>(&id),
|
||||
resource::remove_tag_from_all::<Builder>(&id),
|
||||
resource::remove_tag_from_all::<Alerter>(&id),
|
||||
resource::remove_tag_from_all::<Procedure>(&id),
|
||||
resource::remove_tag_from_all::<ServerTemplate>(&id),
|
||||
)?;
|
||||
|
||||
delete_one_by_id(&db_client().await.tags, &id, None).await?;
|
||||
@@ -125,76 +124,76 @@ impl Resolve<UpdateTagsOnResource, User> for State {
|
||||
match target {
|
||||
ResourceTarget::System(_) => return Err(anyhow!("")),
|
||||
ResourceTarget::Build(id) => {
|
||||
Build::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Build>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Build::update_tags_on_resource(&id, tags, user).await?;
|
||||
resource::update_tags::<Build>(&id, tags, user).await?;
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
Builder::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Builder>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Builder::update_tags_on_resource(&id, tags, user).await?
|
||||
resource::update_tags::<Builder>(&id, tags, user).await?
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
Deployment::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Deployment>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Deployment::update_tags_on_resource(&id, tags, user).await?
|
||||
resource::update_tags::<Deployment>(&id, tags, user).await?
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
Server::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Server>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Server::update_tags_on_resource(&id, tags, user).await?
|
||||
resource::update_tags::<Server>(&id, tags, user).await?
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
Repo::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Repo>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Repo::update_tags_on_resource(&id, tags, user).await?
|
||||
resource::update_tags::<Repo>(&id, tags, user).await?
|
||||
}
|
||||
ResourceTarget::Alerter(id) => {
|
||||
Alerter::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Alerter>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Alerter::update_tags_on_resource(&id, tags, user).await?
|
||||
resource::update_tags::<Alerter>(&id, tags, user).await?
|
||||
}
|
||||
ResourceTarget::Procedure(id) => {
|
||||
Procedure::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<Procedure>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
Procedure::update_tags_on_resource(&id, tags, user).await?
|
||||
resource::update_tags::<Procedure>(&id, tags, user).await?
|
||||
}
|
||||
ResourceTarget::ServerTemplate(id) => {
|
||||
ServerTemplate::get_resource_check_permissions(
|
||||
resource::get_check_permissions::<ServerTemplate>(
|
||||
&id,
|
||||
&user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
ServerTemplate::update_tags_on_resource(&id, tags, user)
|
||||
resource::update_tags::<ServerTemplate>(&id, tags, user)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
|
||||
+15
-12
@@ -42,7 +42,7 @@ async fn create_ec2_client(region: String) -> Client {
|
||||
&core_config().aws.secret_access_key,
|
||||
);
|
||||
let region = Region::new(region);
|
||||
let config = aws_config::defaults(BehaviorVersion::v2023_11_09())
|
||||
let config = aws_config::defaults(BehaviorVersion::v2024_03_28())
|
||||
.region(region)
|
||||
.load()
|
||||
.await;
|
||||
@@ -64,6 +64,7 @@ pub async fn launch_ec2_instance(
|
||||
key_pair_name,
|
||||
assign_public_ip,
|
||||
use_public_ip,
|
||||
user_data,
|
||||
port: _,
|
||||
} = config;
|
||||
let instance_type = handle_unknown_instance_type(
|
||||
@@ -90,23 +91,25 @@ pub async fn launch_ec2_instance(
|
||||
.build(),
|
||||
)
|
||||
.min_count(1)
|
||||
.max_count(1);
|
||||
.max_count(1)
|
||||
.user_data(user_data);
|
||||
|
||||
for volume in volumes {
|
||||
let mut ebs = EbsBlockDevice::builder()
|
||||
let ebs = EbsBlockDevice::builder()
|
||||
.volume_size(volume.size_gb)
|
||||
.set_iops(volume.iops)
|
||||
.set_throughput(volume.throughput);
|
||||
if let Some(volume_type) = &volume.volume_type {
|
||||
ebs = ebs.volume_type(
|
||||
VolumeType::from_str(volume_type)
|
||||
.volume_type(
|
||||
VolumeType::from_str(volume.volume_type.as_ref())
|
||||
.context("invalid volume type")?,
|
||||
);
|
||||
}
|
||||
)
|
||||
.set_iops((volume.iops != 0).then_some(volume.iops))
|
||||
.set_throughput(
|
||||
(volume.throughput != 0).then_some(volume.throughput),
|
||||
)
|
||||
.build();
|
||||
req = req.block_device_mappings(
|
||||
BlockDeviceMapping::builder()
|
||||
.set_device_name(volume.device_name.clone().into())
|
||||
.set_ebs(ebs.build().into())
|
||||
.set_device_name(volume.device_name.into())
|
||||
.set_ebs(ebs.into())
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ pub mod channel;
|
||||
pub mod procedure;
|
||||
pub mod prune;
|
||||
pub mod query;
|
||||
pub mod resource;
|
||||
pub mod update;
|
||||
|
||||
// pub mod resource;
|
||||
|
||||
pub fn empty_or_only_spaces(word: &str) -> bool {
|
||||
if word.is_empty() {
|
||||
return true;
|
||||
@@ -40,15 +41,13 @@ pub fn random_duration(min_ms: u64, max_ms: u64) -> Duration {
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn remove_from_recently_viewed<T>(
|
||||
resource: T,
|
||||
) -> anyhow::Result<()>
|
||||
pub async fn remove_from_recently_viewed<T>(resource: T)
|
||||
where
|
||||
T: Into<ResourceTarget> + std::fmt::Debug,
|
||||
{
|
||||
let resource: ResourceTarget = resource.into();
|
||||
let (ty, id) = resource.extract_variant_id();
|
||||
db_client()
|
||||
if let Err(e) = db_client()
|
||||
.await
|
||||
.users
|
||||
.update_many(
|
||||
@@ -64,10 +63,10 @@ where
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context(
|
||||
"failed to remove resource from users recently viewed",
|
||||
)?;
|
||||
Ok(())
|
||||
.context("failed to remove resource from users recently viewed")
|
||||
{
|
||||
warn!("{e:#}");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use std::str::FromStr;
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use monitor_client::entities::{
|
||||
deployment::{Deployment, DockerContainerState},
|
||||
permission::PermissionLevel,
|
||||
server::{Server, ServerStatus},
|
||||
tag::Tag,
|
||||
update::ResourceTargetVariant,
|
||||
user::{admin_service_user, User},
|
||||
};
|
||||
use mungos::{
|
||||
@@ -13,9 +15,7 @@ use mungos::{
|
||||
mongodb::bson::{doc, oid::ObjectId, Document},
|
||||
};
|
||||
|
||||
use crate::state::db_client;
|
||||
|
||||
use super::resource::StateResource;
|
||||
use crate::{resource, state::db_client};
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_user(user_id: &str) -> anyhow::Result<User> {
|
||||
@@ -32,7 +32,7 @@ pub async fn get_user(user_id: &str) -> anyhow::Result<User> {
|
||||
pub async fn get_server_with_status(
|
||||
server_id_or_name: &str,
|
||||
) -> anyhow::Result<(Server, ServerStatus)> {
|
||||
let server = Server::get_resource(server_id_or_name).await?;
|
||||
let server = resource::get::<Server>(server_id_or_name).await?;
|
||||
if !server.config.enabled {
|
||||
return Ok((server, ServerStatus::Disabled));
|
||||
}
|
||||
@@ -72,8 +72,6 @@ pub async fn get_deployment_state(
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
// TAG
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_tag(id_or_name: &str) -> anyhow::Result<Tag> {
|
||||
let query = match ObjectId::from_str(id_or_name) {
|
||||
@@ -120,6 +118,9 @@ pub async fn get_user_user_group_ids(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Returns Vec of all queries on permissions that match against the user
|
||||
/// or any user groups that the user is a part of.
|
||||
/// Result used with Mongodb '$or'.
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn user_target_query(
|
||||
user_id: &str,
|
||||
@@ -138,3 +139,62 @@ pub async fn user_target_query(
|
||||
user_target_query.extend(user_groups);
|
||||
Ok(user_target_query)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_user_permission_on_resource(
|
||||
user_id: &str,
|
||||
resource_variant: ResourceTargetVariant,
|
||||
resource_id: &str,
|
||||
) -> anyhow::Result<PermissionLevel> {
|
||||
let permission = find_collect(
|
||||
&db_client().await.permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(user_id).await?,
|
||||
"resource_target.type": resource_variant.as_ref(),
|
||||
"resource_target.id": resource_id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query db for permissions")?
|
||||
.into_iter()
|
||||
// get the max permission user has between personal / any user groups
|
||||
.fold(PermissionLevel::None, |level, permission| {
|
||||
if permission.level > level {
|
||||
permission.level
|
||||
} else {
|
||||
level
|
||||
}
|
||||
});
|
||||
Ok(permission)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_resource_ids_for_non_admin(
|
||||
user_id: &str,
|
||||
resource_type: ResourceTargetVariant,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
let permissions = find_collect(
|
||||
&db_client().await.permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(user_id).await?,
|
||||
"resource_target.type": resource_type.as_ref(),
|
||||
"level": { "$in": ["Read", "Execute", "Write"] }
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query permissions on db")?
|
||||
.into_iter()
|
||||
.map(|p| p.resource_target.extract_variant_id().1.to_string())
|
||||
// collect into hashset first to remove any duplicates
|
||||
.collect::<HashSet<_>>();
|
||||
Ok(permissions.into_iter().collect())
|
||||
}
|
||||
|
||||
pub fn id_or_name_filter(id_or_name: &str) -> Document {
|
||||
match ObjectId::from_str(id_or_name) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": id_or_name },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,737 +0,0 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use futures::future::join_all;
|
||||
use monitor_client::{
|
||||
api::write::CreateTag,
|
||||
entities::{
|
||||
alerter::{
|
||||
Alerter, AlerterConfig, AlerterConfigVariant, AlerterInfo,
|
||||
AlerterListItem, AlerterListItemInfo, AlerterQuerySpecifics,
|
||||
},
|
||||
build::{
|
||||
Build, BuildConfig, BuildInfo, BuildListItem,
|
||||
BuildListItemInfo, BuildQuerySpecifics,
|
||||
},
|
||||
builder::{
|
||||
Builder, BuilderConfig, BuilderConfigVariant, BuilderListItem,
|
||||
BuilderListItemInfo, BuilderQuerySpecifics,
|
||||
},
|
||||
deployment::{
|
||||
Deployment, DeploymentConfig, DeploymentImage,
|
||||
DeploymentListItem, DeploymentListItemInfo,
|
||||
DeploymentQuerySpecifics,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
procedure::{
|
||||
Procedure, ProcedureConfig, ProcedureListItem,
|
||||
ProcedureListItemInfo, ProcedureQuerySpecifics,
|
||||
},
|
||||
repo::{
|
||||
Repo, RepoConfig, RepoInfo, RepoListItem, RepoListItemInfo,
|
||||
RepoQuerySpecifics,
|
||||
},
|
||||
resource::{AddFilters, Resource, ResourceQuery},
|
||||
server::{
|
||||
Server, ServerConfig, ServerListItem, ServerListItemInfo,
|
||||
ServerQuerySpecifics,
|
||||
},
|
||||
server_template::{
|
||||
ServerTemplate, ServerTemplateConfig,
|
||||
ServerTemplateConfigVariant, ServerTemplateListItem,
|
||||
ServerTemplateListItemInfo, ServerTemplateQuerySpecifics,
|
||||
},
|
||||
update::{ResourceTarget, ResourceTargetVariant},
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
find::find_collect,
|
||||
mongodb::{
|
||||
bson::{doc, oid::ObjectId, Document},
|
||||
Collection,
|
||||
},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::{
|
||||
helpers::query::user_target_query,
|
||||
state::State,
|
||||
state::{db_client, deployment_status_cache, server_status_cache},
|
||||
};
|
||||
|
||||
use super::query::get_tag;
|
||||
|
||||
pub trait StateResource {
|
||||
type ListItem: Serialize + Send;
|
||||
type Config: Send
|
||||
+ Sync
|
||||
+ Unpin
|
||||
+ Serialize
|
||||
+ DeserializeOwned
|
||||
+ 'static;
|
||||
type Info: Send
|
||||
+ Sync
|
||||
+ Unpin
|
||||
+ Default
|
||||
+ Serialize
|
||||
+ DeserializeOwned
|
||||
+ 'static;
|
||||
type QuerySpecifics: AddFilters + Default + std::fmt::Debug;
|
||||
|
||||
fn name() -> &'static str;
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant;
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>>;
|
||||
|
||||
async fn to_list_item(
|
||||
resource: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem>;
|
||||
|
||||
async fn get_resource(
|
||||
id_or_name: &str,
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
let filter = match ObjectId::from_str(id_or_name) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": id_or_name },
|
||||
};
|
||||
Self::coll()
|
||||
.await
|
||||
.find_one(filter, None)
|
||||
.await
|
||||
.context("failed to query db for resource")?
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"did not find any {} matching {id_or_name}",
|
||||
Self::name()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_resource_check_permissions(
|
||||
id_or_name: &str,
|
||||
user: &User,
|
||||
permission_level: PermissionLevel,
|
||||
) -> anyhow::Result<Resource<Self::Config, Self::Info>> {
|
||||
let resource = Self::get_resource(id_or_name).await?;
|
||||
if user.admin {
|
||||
return Ok(resource);
|
||||
}
|
||||
let permissions =
|
||||
Self::get_user_permission_on_resource(&user.id, &resource.id)
|
||||
.await?;
|
||||
if permissions >= permission_level {
|
||||
Ok(resource)
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"user does not have required permissions on this {}",
|
||||
Self::name()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_user_permission_on_resource(
|
||||
user_id: &str,
|
||||
resource_id: &str,
|
||||
) -> anyhow::Result<PermissionLevel> {
|
||||
get_user_permission_on_resource(
|
||||
user_id,
|
||||
Self::resource_target_variant(),
|
||||
resource_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_resource_ids_for_non_admin(
|
||||
user_id: &str,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
get_resource_ids_for_non_admin(
|
||||
user_id,
|
||||
Self::resource_target_variant(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn list_resource_list_items_for_user(
|
||||
mut query: ResourceQuery<Self::QuerySpecifics>,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<Self::ListItem>> {
|
||||
validate_resource_query_tags(&mut query).await;
|
||||
let mut filters = Document::new();
|
||||
query.add_filters(&mut filters);
|
||||
Self::query_resource_list_items_for_user(filters, user).await
|
||||
}
|
||||
|
||||
async fn query_resource_list_items_for_user(
|
||||
filters: Document,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<Self::ListItem>> {
|
||||
let list = Self::query_resources_for_user(filters, user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| Self::to_list_item(resource));
|
||||
|
||||
let list = join_all(list)
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<anyhow::Result<Vec<_>>>()
|
||||
.context(format!(
|
||||
"failed to convert {} list item",
|
||||
Self::name()
|
||||
))?;
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
async fn list_resources_for_user(
|
||||
mut query: ResourceQuery<Self::QuerySpecifics>,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<Resource<Self::Config, Self::Info>>> {
|
||||
validate_resource_query_tags(&mut query).await;
|
||||
let mut filters = Document::new();
|
||||
query.add_filters(&mut filters);
|
||||
Self::query_resources_for_user(filters, user).await
|
||||
}
|
||||
|
||||
async fn query_resources_for_user(
|
||||
mut filters: Document,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<Resource<Self::Config, Self::Info>>> {
|
||||
if !user.admin {
|
||||
let ids = Self::get_resource_ids_for_non_admin(&user.id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.flat_map(|id| ObjectId::from_str(&id))
|
||||
.collect::<Vec<_>>();
|
||||
filters.insert("_id", doc! { "$in": ids });
|
||||
}
|
||||
find_collect(Self::coll().await, filters, None)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to pull {}s from mongo", Self::name())
|
||||
})
|
||||
}
|
||||
|
||||
async fn update_description(
|
||||
id_or_name: &str,
|
||||
description: &str,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
Self::get_resource_check_permissions(
|
||||
id_or_name,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
let filter = match ObjectId::from_str(id_or_name) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": id_or_name },
|
||||
};
|
||||
Self::coll()
|
||||
.await
|
||||
.update_one(
|
||||
filter,
|
||||
doc! { "$set": { "description": description } },
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_tags_on_resource(
|
||||
id_or_name: &str,
|
||||
tags: Vec<String>,
|
||||
user: User,
|
||||
) -> anyhow::Result<()> {
|
||||
let futures = tags.iter().map(|tag| async {
|
||||
match get_tag(tag).await {
|
||||
Ok(tag) => Ok(tag.id),
|
||||
Err(_) => State
|
||||
.resolve(
|
||||
CreateTag {
|
||||
name: tag.to_string(),
|
||||
},
|
||||
user.clone(),
|
||||
)
|
||||
.await
|
||||
.map(|tag| tag.id),
|
||||
}
|
||||
});
|
||||
let tags = join_all(futures)
|
||||
.await
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
Self::coll()
|
||||
.await
|
||||
.update_one(
|
||||
id_or_name_filter(id_or_name),
|
||||
doc! { "$set": { "tags": tags } },
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_tag_from_resources(
|
||||
tag_id: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
Self::coll()
|
||||
.await
|
||||
.update_many(
|
||||
doc! {},
|
||||
doc! { "$pull": { "tags": tag_id } },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to remove tag from resources")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn id_or_name_filter(id_or_name: &str) -> Document {
|
||||
match ObjectId::from_str(id_or_name) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": id_or_name },
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for Server {
|
||||
type ListItem = ServerListItem;
|
||||
type Config = ServerConfig;
|
||||
type Info = ();
|
||||
type QuerySpecifics = ServerQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"server"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Server
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<Server> {
|
||||
&db_client().await.servers
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
server: Server,
|
||||
) -> anyhow::Result<ServerListItem> {
|
||||
let status = server_status_cache().get(&server.id).await;
|
||||
Ok(ServerListItem {
|
||||
name: server.name,
|
||||
created_at: ObjectId::from_str(&server.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: server.id,
|
||||
tags: server.tags,
|
||||
resource_type: ResourceTargetVariant::Server,
|
||||
info: ServerListItemInfo {
|
||||
status: status.map(|s| s.status).unwrap_or_default(),
|
||||
region: server.config.region,
|
||||
send_unreachable_alerts: server
|
||||
.config
|
||||
.send_unreachable_alerts,
|
||||
send_cpu_alerts: server.config.send_cpu_alerts,
|
||||
send_mem_alerts: server.config.send_mem_alerts,
|
||||
send_disk_alerts: server.config.send_disk_alerts,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for Deployment {
|
||||
type ListItem = DeploymentListItem;
|
||||
type Config = DeploymentConfig;
|
||||
type Info = ();
|
||||
type QuerySpecifics = DeploymentQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"deployment"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Deployment
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<Deployment> {
|
||||
&db_client().await.deployments
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
deployment: Deployment,
|
||||
) -> anyhow::Result<DeploymentListItem> {
|
||||
let status = deployment_status_cache().get(&deployment.id).await;
|
||||
let (image, build_id) = match deployment.config.image {
|
||||
DeploymentImage::Build { build_id, version } => {
|
||||
let build = Build::get_resource(&build_id).await?;
|
||||
let version = if version.is_none() {
|
||||
build.config.version.to_string()
|
||||
} else {
|
||||
version.to_string()
|
||||
};
|
||||
(format!("{}:{version}", build.name), Some(build_id))
|
||||
}
|
||||
DeploymentImage::Image { image } => (image, None),
|
||||
};
|
||||
Ok(DeploymentListItem {
|
||||
name: deployment.name,
|
||||
created_at: ObjectId::from_str(&deployment.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: deployment.id,
|
||||
tags: deployment.tags,
|
||||
resource_type: ResourceTargetVariant::Deployment,
|
||||
info: DeploymentListItemInfo {
|
||||
state: status
|
||||
.as_ref()
|
||||
.map(|s| s.curr.state)
|
||||
.unwrap_or_default(),
|
||||
status: status.as_ref().and_then(|s| {
|
||||
s.curr.container.as_ref().and_then(|c| c.status.to_owned())
|
||||
}),
|
||||
image,
|
||||
server_id: deployment.config.server_id,
|
||||
build_id,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for Build {
|
||||
type ListItem = BuildListItem;
|
||||
type Config = BuildConfig;
|
||||
type Info = BuildInfo;
|
||||
type QuerySpecifics = BuildQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"build"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Build
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<Build> {
|
||||
&db_client().await.builds
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
build: Build,
|
||||
) -> anyhow::Result<BuildListItem> {
|
||||
Ok(BuildListItem {
|
||||
name: build.name,
|
||||
created_at: ObjectId::from_str(&build.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: build.id,
|
||||
tags: build.tags,
|
||||
resource_type: ResourceTargetVariant::Build,
|
||||
info: BuildListItemInfo {
|
||||
last_built_at: build.info.last_built_at,
|
||||
version: build.config.version,
|
||||
repo: build.config.repo,
|
||||
branch: build.config.branch,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for Repo {
|
||||
type ListItem = RepoListItem;
|
||||
type Config = RepoConfig;
|
||||
type Info = RepoInfo;
|
||||
type QuerySpecifics = RepoQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"repo"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Repo
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<Repo> {
|
||||
&db_client().await.repos
|
||||
}
|
||||
|
||||
async fn to_list_item(repo: Repo) -> anyhow::Result<RepoListItem> {
|
||||
Ok(RepoListItem {
|
||||
name: repo.name,
|
||||
created_at: ObjectId::from_str(&repo.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: repo.id,
|
||||
tags: repo.tags,
|
||||
resource_type: ResourceTargetVariant::Repo,
|
||||
info: RepoListItemInfo {
|
||||
last_pulled_at: repo.info.last_pulled_at,
|
||||
repo: repo.config.repo,
|
||||
branch: repo.config.branch,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for Builder {
|
||||
type ListItem = BuilderListItem;
|
||||
type Config = BuilderConfig;
|
||||
type Info = ();
|
||||
type QuerySpecifics = BuilderQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"builder"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Builder
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<Builder> {
|
||||
&db_client().await.builders
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
builder: Builder,
|
||||
) -> anyhow::Result<BuilderListItem> {
|
||||
let (builder_type, instance_type) = match builder.config {
|
||||
BuilderConfig::Server(config) => (
|
||||
BuilderConfigVariant::Server.to_string(),
|
||||
Some(config.server_id),
|
||||
),
|
||||
BuilderConfig::Aws(config) => (
|
||||
BuilderConfigVariant::Aws.to_string(),
|
||||
Some(config.instance_type),
|
||||
),
|
||||
};
|
||||
|
||||
Ok(BuilderListItem {
|
||||
name: builder.name,
|
||||
created_at: ObjectId::from_str(&builder.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: builder.id,
|
||||
tags: builder.tags,
|
||||
resource_type: ResourceTargetVariant::Builder,
|
||||
info: BuilderListItemInfo {
|
||||
builder_type,
|
||||
instance_type,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for Alerter {
|
||||
type ListItem = AlerterListItem;
|
||||
type Config = AlerterConfig;
|
||||
type Info = AlerterInfo;
|
||||
type QuerySpecifics = AlerterQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"alerter"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Alerter
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<Alerter> {
|
||||
&db_client().await.alerters
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
alerter: Alerter,
|
||||
) -> anyhow::Result<AlerterListItem> {
|
||||
let (alerter_type, enabled) = match alerter.config {
|
||||
AlerterConfig::Custom(config) => {
|
||||
(AlerterConfigVariant::Custom.to_string(), config.enabled)
|
||||
}
|
||||
AlerterConfig::Slack(config) => {
|
||||
(AlerterConfigVariant::Slack.to_string(), config.enabled)
|
||||
}
|
||||
};
|
||||
Ok(AlerterListItem {
|
||||
name: alerter.name,
|
||||
created_at: ObjectId::from_str(&alerter.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: alerter.id,
|
||||
tags: alerter.tags,
|
||||
resource_type: ResourceTargetVariant::Alerter,
|
||||
info: AlerterListItemInfo {
|
||||
alerter_type: alerter_type.to_string(),
|
||||
is_default: alerter.info.is_default,
|
||||
enabled,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for Procedure {
|
||||
type ListItem = ProcedureListItem;
|
||||
type Config = ProcedureConfig;
|
||||
type Info = ();
|
||||
type QuerySpecifics = ProcedureQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"procedure"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Procedure
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<Procedure> {
|
||||
&db_client().await.procedures
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
procedure: Procedure,
|
||||
) -> anyhow::Result<ProcedureListItem> {
|
||||
Ok(ProcedureListItem {
|
||||
name: procedure.name,
|
||||
created_at: ObjectId::from_str(&procedure.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: procedure.id,
|
||||
tags: procedure.tags,
|
||||
resource_type: ResourceTargetVariant::Procedure,
|
||||
info: ProcedureListItemInfo {
|
||||
procedure_type: procedure.config.procedure_type,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StateResource for ServerTemplate {
|
||||
type ListItem = ServerTemplateListItem;
|
||||
type Config = ServerTemplateConfig;
|
||||
type Info = ();
|
||||
type QuerySpecifics = ServerTemplateQuerySpecifics;
|
||||
|
||||
fn name() -> &'static str {
|
||||
"server_template"
|
||||
}
|
||||
|
||||
fn resource_target_variant() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Alerter
|
||||
}
|
||||
|
||||
async fn coll() -> &'static Collection<ServerTemplate> {
|
||||
&db_client().await.server_templates
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
server_template: ServerTemplate,
|
||||
) -> anyhow::Result<ServerTemplateListItem> {
|
||||
let (template_type, instance_type) = match server_template.config
|
||||
{
|
||||
ServerTemplateConfig::Aws(config) => (
|
||||
ServerTemplateConfigVariant::Aws.to_string(),
|
||||
Some(config.instance_type),
|
||||
),
|
||||
};
|
||||
Ok(ServerTemplateListItem {
|
||||
name: server_template.name,
|
||||
created_at: ObjectId::from_str(&server_template.id)?
|
||||
.timestamp()
|
||||
.timestamp_millis(),
|
||||
id: server_template.id,
|
||||
tags: server_template.tags,
|
||||
resource_type: ResourceTargetVariant::ServerTemplate,
|
||||
info: ServerTemplateListItemInfo {
|
||||
provider: template_type.to_string(),
|
||||
instance_type,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_user_permission_on_resource(
|
||||
user_id: &str,
|
||||
resource_variant: ResourceTargetVariant,
|
||||
resource_id: &str,
|
||||
) -> anyhow::Result<PermissionLevel> {
|
||||
let permission = find_collect(
|
||||
&db_client().await.permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(user_id).await?,
|
||||
"resource_target.type": resource_variant.as_ref(),
|
||||
"resource_target.id": resource_id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query db for permissions")?
|
||||
.into_iter()
|
||||
// get the max permission user has between personal / any user groups
|
||||
.fold(PermissionLevel::None, |level, permission| {
|
||||
if permission.level > level {
|
||||
permission.level
|
||||
} else {
|
||||
level
|
||||
}
|
||||
});
|
||||
Ok(permission)
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn delete_all_permissions_on_resource<T>(target: T)
|
||||
where
|
||||
T: Into<ResourceTarget> + std::fmt::Debug,
|
||||
{
|
||||
let target: ResourceTarget = target.into();
|
||||
let (variant, id) = target.extract_variant_id();
|
||||
if let Err(e) = db_client()
|
||||
.await
|
||||
.permissions
|
||||
.delete_many(
|
||||
doc! {
|
||||
"resource_target.type": variant.as_ref(),
|
||||
"resource_target.id": &id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("failed to delete_many permissions matching target {target:?} | {e:#}");
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn get_resource_ids_for_non_admin(
|
||||
user_id: &str,
|
||||
resource_type: ResourceTargetVariant,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
let permissions = find_collect(
|
||||
&db_client().await.permissions,
|
||||
doc! {
|
||||
"$or": user_target_query(user_id).await?,
|
||||
"resource_target.type": resource_type.as_ref(),
|
||||
"level": { "$in": ["Read", "Execute", "Write"] }
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to query permissions on db")?
|
||||
.into_iter()
|
||||
.map(|p| p.resource_target.extract_variant_id().1.to_string())
|
||||
// collect into hashset first to remove any duplicates
|
||||
.collect::<HashSet<_>>();
|
||||
Ok(permissions.into_iter().collect())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn validate_resource_query_tags<
|
||||
T: Default + std::fmt::Debug,
|
||||
>(
|
||||
query: &mut ResourceQuery<T>,
|
||||
) {
|
||||
let futures = query.tags.iter().map(|tag| get_tag(tag));
|
||||
let res = join_all(futures).await;
|
||||
query.tags = res.into_iter().flatten().map(|tag| tag.id).collect();
|
||||
}
|
||||
@@ -29,6 +29,36 @@ pub fn make_update(
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn add_update(
|
||||
mut update: Update,
|
||||
) -> anyhow::Result<String> {
|
||||
update.id = db_client()
|
||||
.await
|
||||
.updates
|
||||
.insert_one(&update, None)
|
||||
.await
|
||||
.context("failed to insert update into db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not object id")?
|
||||
.to_string();
|
||||
let id = update.id.clone();
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn update_update(update: Update) -> anyhow::Result<()> {
|
||||
update_one_by_id(&db_client().await.updates, &update.id, mungos::update::Update::Set(to_document(&update)?), None)
|
||||
.await
|
||||
.context("failed to update the update on db. the update build process was deleted")?;
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
async fn update_list_item(
|
||||
update: Update,
|
||||
@@ -63,33 +93,3 @@ async fn send_update(update: UpdateListItem) -> anyhow::Result<()> {
|
||||
update_channel().sender.lock().await.send(update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn add_update(
|
||||
mut update: Update,
|
||||
) -> anyhow::Result<String> {
|
||||
update.id = db_client()
|
||||
.await
|
||||
.updates
|
||||
.insert_one(&update, None)
|
||||
.await
|
||||
.context("failed to insert update into db")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not object id")?
|
||||
.to_string();
|
||||
let id = update.id.clone();
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn update_update(update: Update) -> anyhow::Result<()> {
|
||||
update_one_by_id(&db_client().await.updates, &update.id, mungos::update::Update::Set(to_document(&update)?), None)
|
||||
.await
|
||||
.context("failed to update the update on db. the update build process was deleted")?;
|
||||
let update = update_list_item(update).await?;
|
||||
let _ = send_update(update).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use axum::{extract::Path, http::HeaderMap, routing::post, Router};
|
||||
use hex::ToHex;
|
||||
@@ -9,11 +11,13 @@ use monitor_client::{
|
||||
use resolver_api::Resolve;
|
||||
use serde::Deserialize;
|
||||
use sha2::Sha256;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::Instrument;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::{random_duration, resource::StateResource},
|
||||
helpers::{cache::Cache, random_duration},
|
||||
resource,
|
||||
state::State,
|
||||
};
|
||||
|
||||
@@ -116,9 +120,15 @@ async fn handle_build_webhook(
|
||||
headers: HeaderMap,
|
||||
body: String,
|
||||
) -> anyhow::Result<()> {
|
||||
// Acquire and hold lock to make a task queue for
|
||||
// subsequent listener calls on same resource.
|
||||
// It would fail if we let it go through from action state busy.
|
||||
let lock = build_locks().get_or_insert_default(&build_id).await;
|
||||
let _lock = lock.lock().await;
|
||||
|
||||
verify_gh_signature(headers, &body).await?;
|
||||
let request_branch = extract_branch(&body)?;
|
||||
let build = Build::get_resource(&build_id).await?;
|
||||
let build = resource::get::<Build>(&build_id).await?;
|
||||
if request_branch != build.config.branch {
|
||||
return Err(anyhow!("request branch does not match expected"));
|
||||
}
|
||||
@@ -136,9 +146,15 @@ async fn handle_repo_clone_webhook(
|
||||
headers: HeaderMap,
|
||||
body: String,
|
||||
) -> anyhow::Result<()> {
|
||||
// Acquire and hold lock to make a task queue for
|
||||
// subsequent listener calls on same resource.
|
||||
// It would fail if we let it go through from action state busy.
|
||||
let lock = repo_locks().get_or_insert_default(&repo_id).await;
|
||||
let _lock = lock.lock().await;
|
||||
|
||||
verify_gh_signature(headers, &body).await?;
|
||||
let request_branch = extract_branch(&body)?;
|
||||
let repo = Repo::get_resource(&repo_id).await?;
|
||||
let repo = resource::get::<Repo>(&repo_id).await?;
|
||||
if request_branch != repo.config.branch {
|
||||
return Err(anyhow!("request branch does not match expected"));
|
||||
}
|
||||
@@ -156,9 +172,15 @@ async fn handle_repo_pull_webhook(
|
||||
headers: HeaderMap,
|
||||
body: String,
|
||||
) -> anyhow::Result<()> {
|
||||
// Acquire and hold lock to make a task queue for
|
||||
// subsequent listener calls on same resource.
|
||||
// It would fail if we let it go through from action state busy.
|
||||
let lock = repo_locks().get_or_insert_default(&repo_id).await;
|
||||
let _lock = lock.lock().await;
|
||||
|
||||
verify_gh_signature(headers, &body).await?;
|
||||
let request_branch = extract_branch(&body)?;
|
||||
let repo = Repo::get_resource(&repo_id).await?;
|
||||
let repo = resource::get::<Repo>(&repo_id).await?;
|
||||
if request_branch != repo.config.branch {
|
||||
return Err(anyhow!("request branch does not match expected"));
|
||||
}
|
||||
@@ -177,6 +199,13 @@ async fn handle_procedure_webhook(
|
||||
headers: HeaderMap,
|
||||
body: String,
|
||||
) -> anyhow::Result<()> {
|
||||
// Acquire and hold lock to make a task queue for
|
||||
// subsequent listener calls on same resource.
|
||||
// It would fail if we let it go through from action state busy.
|
||||
let lock =
|
||||
procedure_locks().get_or_insert_default(&procedure_id).await;
|
||||
let _lock = lock.lock().await;
|
||||
|
||||
verify_gh_signature(headers, &body).await?;
|
||||
let request_branch = extract_branch(&body)?;
|
||||
if request_branch != target_branch {
|
||||
@@ -236,3 +265,20 @@ fn extract_branch(body: &str) -> anyhow::Result<String> {
|
||||
.replace("refs/heads/", "");
|
||||
Ok(branch)
|
||||
}
|
||||
|
||||
type ListenerLockCache = Cache<String, Arc<Mutex<()>>>;
|
||||
|
||||
fn build_locks() -> &'static ListenerLockCache {
|
||||
static BUILD_LOCKS: OnceLock<ListenerLockCache> = OnceLock::new();
|
||||
BUILD_LOCKS.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
fn repo_locks() -> &'static ListenerLockCache {
|
||||
static REPO_LOCKS: OnceLock<ListenerLockCache> = OnceLock::new();
|
||||
REPO_LOCKS.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
fn procedure_locks() -> &'static ListenerLockCache {
|
||||
static BUILD_LOCKS: OnceLock<ListenerLockCache> = OnceLock::new();
|
||||
BUILD_LOCKS.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ mod db;
|
||||
mod helpers;
|
||||
mod listener;
|
||||
mod monitor;
|
||||
mod resource;
|
||||
mod state;
|
||||
mod ws;
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@ use monitor_client::entities::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
helpers::{alert::send_alerts, resource::StateResource},
|
||||
monitor::deployment_status_cache,
|
||||
state::db_client,
|
||||
helpers::alert::send_alerts, monitor::deployment_status_cache,
|
||||
resource, state::db_client,
|
||||
};
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
@@ -26,7 +25,7 @@ pub async fn alert_deployments(
|
||||
let prev = v.prev.as_ref().unwrap().to_owned();
|
||||
if v.curr.state != prev {
|
||||
// send alert
|
||||
let d = Deployment::get_resource(&v.curr.id).await;
|
||||
let d = resource::get::<Deployment>(&v.curr.id).await;
|
||||
if let Err(e) = d {
|
||||
error!("failed to get deployment from db | {e:#?}");
|
||||
continue;
|
||||
|
||||
@@ -10,7 +10,7 @@ use monitor_client::entities::{
|
||||
user::User,
|
||||
};
|
||||
|
||||
use crate::helpers::resource::StateResource;
|
||||
use crate::resource;
|
||||
|
||||
// called after cache update
|
||||
#[instrument(level = "debug")]
|
||||
@@ -35,7 +35,7 @@ async fn get_all_servers_map() -> anyhow::Result<(
|
||||
HashMap<String, ServerListItem>,
|
||||
HashMap<String, String>,
|
||||
)> {
|
||||
let servers = Server::list_resource_list_items_for_user(
|
||||
let servers = resource::list_for_user::<Server>(
|
||||
ResourceQuery::default(),
|
||||
&User {
|
||||
admin: true,
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
use monitor_client::entities::{
|
||||
alerter::{
|
||||
Alerter, AlerterConfig, AlerterConfigDiff, AlerterConfigVariant,
|
||||
AlerterInfo, AlerterListItem, AlerterListItemInfo,
|
||||
AlerterQuerySpecifics, PartialAlerterConfig,
|
||||
},
|
||||
resource::Resource,
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
MergePartial, Operation,
|
||||
};
|
||||
use mungos::mongodb::{
|
||||
bson::{to_document, Document},
|
||||
Collection,
|
||||
};
|
||||
|
||||
use crate::state::db_client;
|
||||
|
||||
impl super::MonitorResource for Alerter {
|
||||
type Config = AlerterConfig;
|
||||
type PartialConfig = PartialAlerterConfig;
|
||||
type ConfigDiff = AlerterConfigDiff;
|
||||
type Info = AlerterInfo;
|
||||
type ListItem = AlerterListItem;
|
||||
type QuerySpecifics = AlerterQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Alerter
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.alerters
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
alerter: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
let (alerter_type, enabled) = match alerter.config {
|
||||
AlerterConfig::Custom(config) => {
|
||||
(AlerterConfigVariant::Custom.to_string(), config.enabled)
|
||||
}
|
||||
AlerterConfig::Slack(config) => {
|
||||
(AlerterConfigVariant::Slack.to_string(), config.enabled)
|
||||
}
|
||||
};
|
||||
Ok(AlerterListItem {
|
||||
name: alerter.name,
|
||||
id: alerter.id,
|
||||
tags: alerter.tags,
|
||||
resource_type: ResourceTargetVariant::Alerter,
|
||||
info: AlerterListItemInfo {
|
||||
alerter_type: alerter_type.to_string(),
|
||||
is_default: alerter.info.is_default,
|
||||
enabled,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(_id: &String) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateAlerter
|
||||
}
|
||||
|
||||
fn user_can_create(user: &User) -> bool {
|
||||
user.admin
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
_config: &mut Self::PartialConfig,
|
||||
_user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
_created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn default_info() -> anyhow::Result<Self::Info> {
|
||||
let is_default = db_client()
|
||||
.await
|
||||
.alerters
|
||||
.find_one(None, None)
|
||||
.await?
|
||||
.is_none();
|
||||
Ok(Self::Info { is_default })
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateAlerter
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
_id: &str,
|
||||
_config: &mut Self::PartialConfig,
|
||||
_user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_document(
|
||||
original: Resource<Self::Config, Self::Info>,
|
||||
config: Self::PartialConfig,
|
||||
) -> Result<Document, mungos::mongodb::bson::ser::Error> {
|
||||
let config = original.config.merge_partial(config);
|
||||
to_document(&config)
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
_updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteAlerter
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
use anyhow::Context;
|
||||
use monitor_client::entities::{
|
||||
build::{
|
||||
Build, BuildConfig, BuildConfigDiff, BuildInfo, BuildListItem,
|
||||
BuildListItemInfo, BuildQuerySpecifics, PartialBuildConfig,
|
||||
},
|
||||
builder::Builder,
|
||||
permission::PermissionLevel,
|
||||
resource::Resource,
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
};
|
||||
use mungos::mongodb::Collection;
|
||||
|
||||
use crate::{
|
||||
helpers::empty_or_only_spaces,
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
|
||||
impl super::MonitorResource for Build {
|
||||
type Config = BuildConfig;
|
||||
type PartialConfig = PartialBuildConfig;
|
||||
type ConfigDiff = BuildConfigDiff;
|
||||
type Info = BuildInfo;
|
||||
type ListItem = BuildListItem;
|
||||
type QuerySpecifics = BuildQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Build
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.builds
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
build: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
Ok(BuildListItem {
|
||||
name: build.name,
|
||||
id: build.id,
|
||||
tags: build.tags,
|
||||
resource_type: ResourceTargetVariant::Build,
|
||||
info: BuildListItemInfo {
|
||||
last_built_at: build.info.last_built_at,
|
||||
version: build.config.version,
|
||||
repo: build.config.repo,
|
||||
branch: build.config.branch,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(id: &String) -> anyhow::Result<bool> {
|
||||
action_states()
|
||||
.build
|
||||
.get(id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateBuild
|
||||
}
|
||||
|
||||
fn user_can_create(user: &User) -> bool {
|
||||
user.admin || user.create_build_permissions
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
_created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateBuild
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
_id: &str,
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
_updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteBuild
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialBuildConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(builder_id) = &config.builder_id {
|
||||
let builder = super::get_check_permissions::<Builder>(builder_id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.context("cannot create build using this builder. user must have at least read permissions on the builder.")?;
|
||||
config.builder_id = Some(builder.id)
|
||||
}
|
||||
if let Some(build_args) = &mut config.build_args {
|
||||
build_args.retain(|v| {
|
||||
!empty_or_only_spaces(&v.variable)
|
||||
&& !empty_or_only_spaces(&v.value)
|
||||
})
|
||||
}
|
||||
if let Some(extra_args) = &mut config.extra_args {
|
||||
extra_args.retain(|v| !empty_or_only_spaces(v))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
use anyhow::Context;
|
||||
use monitor_client::entities::{
|
||||
builder::{
|
||||
Builder, BuilderConfig, BuilderConfigDiff, BuilderConfigVariant,
|
||||
BuilderListItem, BuilderListItemInfo, BuilderQuerySpecifics,
|
||||
PartialBuilderConfig, PartialServerBuilderConfig,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
MergePartial, Operation,
|
||||
};
|
||||
use mungos::mongodb::{
|
||||
bson::{doc, to_document, Document},
|
||||
Collection,
|
||||
};
|
||||
|
||||
use crate::state::db_client;
|
||||
|
||||
impl super::MonitorResource for Builder {
|
||||
type Config = BuilderConfig;
|
||||
type PartialConfig = PartialBuilderConfig;
|
||||
type ConfigDiff = BuilderConfigDiff;
|
||||
type Info = ();
|
||||
type ListItem = BuilderListItem;
|
||||
type QuerySpecifics = BuilderQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Builder
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.builders
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
builder: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
let (builder_type, instance_type) = match builder.config {
|
||||
BuilderConfig::Server(config) => (
|
||||
BuilderConfigVariant::Server.to_string(),
|
||||
Some(config.server_id),
|
||||
),
|
||||
BuilderConfig::Aws(config) => (
|
||||
BuilderConfigVariant::Aws.to_string(),
|
||||
Some(config.instance_type),
|
||||
),
|
||||
};
|
||||
|
||||
Ok(BuilderListItem {
|
||||
name: builder.name,
|
||||
id: builder.id,
|
||||
tags: builder.tags,
|
||||
resource_type: ResourceTargetVariant::Builder,
|
||||
info: BuilderListItemInfo {
|
||||
builder_type,
|
||||
instance_type,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(_id: &String) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateBuilder
|
||||
}
|
||||
|
||||
fn user_can_create(user: &User) -> bool {
|
||||
user.admin
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
_created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateBuilder
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
_id: &str,
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
fn update_document(
|
||||
original: Resource<Self::Config, Self::Info>,
|
||||
config: Self::PartialConfig,
|
||||
) -> Result<Document, mungos::mongodb::bson::ser::Error> {
|
||||
let config = original.config.merge_partial(config);
|
||||
to_document(&config)
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
_updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteBuilder
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
// remove the builder from any attached builds
|
||||
db_client()
|
||||
.await
|
||||
.builds
|
||||
.update_many(
|
||||
doc! { "config.builder.params.builder_id": &resource.id },
|
||||
mungos::update::Update::Set(
|
||||
doc! { "config.builder.params.builder_id": "" },
|
||||
),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to update_many builds on database")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialBuilderConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
match config {
|
||||
PartialBuilderConfig::Server(PartialServerBuilderConfig {
|
||||
server_id: Some(server_id),
|
||||
}) if !server_id.is_empty() => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
*server_id = server.id;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
use anyhow::Context;
|
||||
use monitor_client::entities::{
|
||||
build::Build,
|
||||
deployment::{
|
||||
Deployment, DeploymentConfig, DeploymentConfigDiff,
|
||||
DeploymentImage, DeploymentListItem, DeploymentListItemInfo,
|
||||
DeploymentQuerySpecifics, DockerContainerState,
|
||||
PartialDeploymentConfig,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
};
|
||||
use mungos::mongodb::Collection;
|
||||
use periphery_client::api::container::RemoveContainer;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
empty_or_only_spaces, periphery_client,
|
||||
query::get_deployment_state,
|
||||
},
|
||||
state::{action_states, db_client, deployment_status_cache},
|
||||
};
|
||||
|
||||
use super::get_check_permissions;
|
||||
|
||||
impl super::MonitorResource for Deployment {
|
||||
type Config = DeploymentConfig;
|
||||
type PartialConfig = PartialDeploymentConfig;
|
||||
type ConfigDiff = DeploymentConfigDiff;
|
||||
type Info = ();
|
||||
type ListItem = DeploymentListItem;
|
||||
type QuerySpecifics = DeploymentQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Deployment
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.deployments
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
deployment: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
let status = deployment_status_cache().get(&deployment.id).await;
|
||||
let (image, build_id) = match deployment.config.image {
|
||||
DeploymentImage::Build { build_id, version } => {
|
||||
let build = super::get::<Build>(&build_id).await?;
|
||||
let version = if version.is_none() {
|
||||
build.config.version.to_string()
|
||||
} else {
|
||||
version.to_string()
|
||||
};
|
||||
(format!("{}:{version}", build.name), Some(build_id))
|
||||
}
|
||||
DeploymentImage::Image { image } => (image, None),
|
||||
};
|
||||
Ok(DeploymentListItem {
|
||||
name: deployment.name,
|
||||
id: deployment.id,
|
||||
tags: deployment.tags,
|
||||
resource_type: ResourceTargetVariant::Deployment,
|
||||
info: DeploymentListItemInfo {
|
||||
state: status
|
||||
.as_ref()
|
||||
.map(|s| s.curr.state)
|
||||
.unwrap_or_default(),
|
||||
status: status.as_ref().and_then(|s| {
|
||||
s.curr.container.as_ref().and_then(|c| c.status.to_owned())
|
||||
}),
|
||||
image,
|
||||
server_id: deployment.config.server_id,
|
||||
build_id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(id: &String) -> anyhow::Result<bool> {
|
||||
action_states()
|
||||
.deployment
|
||||
.get(id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateDeployment
|
||||
}
|
||||
|
||||
fn user_can_create(_user: &User) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
_created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateDeployment
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
_id: &str,
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
_updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteDeployment
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
deployment: &Resource<Self::Config, Self::Info>,
|
||||
update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
let state = get_deployment_state(deployment)
|
||||
.await
|
||||
.context("failed to get container state")?;
|
||||
if !matches!(
|
||||
state,
|
||||
DockerContainerState::NotDeployed
|
||||
| DockerContainerState::Unknown
|
||||
) {
|
||||
// container needs to be destroyed
|
||||
let server =
|
||||
super::get::<Server>(&deployment.config.server_id).await;
|
||||
if let Err(e) = server {
|
||||
update.push_error_log(
|
||||
"remove container",
|
||||
format!(
|
||||
"failed to retrieve server at {} from db | {e:#?}",
|
||||
deployment.config.server_id
|
||||
),
|
||||
);
|
||||
} else if let Ok(server) = server {
|
||||
match periphery_client(&server) {
|
||||
Ok(periphery) => match periphery
|
||||
.request(RemoveContainer {
|
||||
name: deployment.name.clone(),
|
||||
signal: deployment.config.termination_signal.into(),
|
||||
time: deployment.config.termination_timeout.into(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(log) => update.logs.push(log),
|
||||
Err(e) => update.push_error_log(
|
||||
"remove container",
|
||||
format!(
|
||||
"failed to remove container on periphery | {e:#?}"
|
||||
),
|
||||
),
|
||||
},
|
||||
Err(e) => update.push_error_log(
|
||||
"remove container",
|
||||
format!(
|
||||
"failed to remove container on periphery | {e:#?}"
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialDeploymentConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(server_id) = &config.server_id {
|
||||
if !server_id.is_empty() {
|
||||
let server = get_check_permissions::<Server>(server_id, user, PermissionLevel::Write)
|
||||
.await
|
||||
.context("cannot create deployment on this server. user must have update permissions on the server to perform this action.")?;
|
||||
config.server_id = Some(server.id);
|
||||
}
|
||||
}
|
||||
if let Some(DeploymentImage::Build { build_id, version }) =
|
||||
&config.image
|
||||
{
|
||||
if !build_id.is_empty() {
|
||||
let build = get_check_permissions::<Build>(build_id, user, PermissionLevel::Read)
|
||||
.await
|
||||
.context("cannot create deployment with this build attached. user must have at least read permissions on the build to perform this action.")?;
|
||||
config.image = Some(DeploymentImage::Build {
|
||||
build_id: build.id,
|
||||
version: version.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(volumes) = &mut config.volumes {
|
||||
volumes.retain(|v| {
|
||||
!empty_or_only_spaces(&v.local)
|
||||
&& !empty_or_only_spaces(&v.container)
|
||||
})
|
||||
}
|
||||
if let Some(ports) = &mut config.ports {
|
||||
ports.retain(|v| {
|
||||
!empty_or_only_spaces(&v.local)
|
||||
&& !empty_or_only_spaces(&v.container)
|
||||
})
|
||||
}
|
||||
if let Some(environment) = &mut config.environment {
|
||||
environment.retain(|v| {
|
||||
!empty_or_only_spaces(&v.variable)
|
||||
&& !empty_or_only_spaces(&v.value)
|
||||
})
|
||||
}
|
||||
if let Some(extra_args) = &mut config.extra_args {
|
||||
extra_args.retain(|v| !empty_or_only_spaces(v))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,619 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use futures::future::join_all;
|
||||
use monitor_client::{
|
||||
api::write::CreateTag,
|
||||
entities::{
|
||||
monitor_timestamp,
|
||||
permission::PermissionLevel,
|
||||
resource::{AddFilters, Resource, ResourceQuery},
|
||||
to_monitor_name,
|
||||
update::{ResourceTarget, ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
},
|
||||
};
|
||||
use mungos::{
|
||||
by_id::{delete_one_by_id, update_one_by_id},
|
||||
find::find_collect,
|
||||
mongodb::{
|
||||
bson::{doc, oid::ObjectId, to_document, Document},
|
||||
Collection,
|
||||
},
|
||||
};
|
||||
use partial_derive2::{Diff, FieldDiff, MaybeNone, PartialDiff};
|
||||
use resolver_api::Resolve;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serror::serialize_error_pretty;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
create_permission,
|
||||
query::{
|
||||
get_resource_ids_for_non_admin, get_tag,
|
||||
get_user_permission_on_resource, id_or_name_filter,
|
||||
},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
state::{db_client, State},
|
||||
};
|
||||
|
||||
mod alerter;
|
||||
mod build;
|
||||
mod builder;
|
||||
mod deployment;
|
||||
mod procedure;
|
||||
mod repo;
|
||||
mod server;
|
||||
mod server_template;
|
||||
|
||||
/// Implement on each monitor resource for common methods
|
||||
pub trait MonitorResource {
|
||||
type ListItem: Serialize + Send;
|
||||
type Config: Send
|
||||
+ Sync
|
||||
+ Unpin
|
||||
+ Serialize
|
||||
+ DeserializeOwned
|
||||
+ PartialDiff<Self::PartialConfig, Self::ConfigDiff>
|
||||
+ 'static;
|
||||
type PartialConfig: Into<Self::Config> + Serialize;
|
||||
type ConfigDiff: Into<Self::PartialConfig>
|
||||
+ Serialize
|
||||
+ Diff
|
||||
+ MaybeNone;
|
||||
type Info: Send
|
||||
+ Sync
|
||||
+ Unpin
|
||||
+ Default
|
||||
+ Serialize
|
||||
+ DeserializeOwned
|
||||
+ 'static;
|
||||
type QuerySpecifics: AddFilters + Default + std::fmt::Debug;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant;
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>>;
|
||||
|
||||
async fn to_list_item(
|
||||
resource: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem>;
|
||||
|
||||
#[allow(clippy::ptr_arg)]
|
||||
async fn busy(id: &String) -> anyhow::Result<bool>;
|
||||
|
||||
// =======
|
||||
// CREATE
|
||||
// =======
|
||||
|
||||
fn create_operation() -> Operation;
|
||||
|
||||
fn user_can_create(user: &User) -> bool;
|
||||
|
||||
async fn validate_create_config(
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
async fn default_info() -> anyhow::Result<Self::Info> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
created: &Resource<Self::Config, Self::Info>,
|
||||
update: &mut Update,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
// =======
|
||||
// UPDATE
|
||||
// =======
|
||||
|
||||
fn update_operation() -> Operation;
|
||||
|
||||
async fn validate_update_config(
|
||||
id: &str,
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
/// Should be overridden for enum configs, eg Alerter, Builder, ...
|
||||
fn update_document(
|
||||
_original: Resource<Self::Config, Self::Info>,
|
||||
config: Self::PartialConfig,
|
||||
) -> Result<Document, mungos::mongodb::bson::ser::Error> {
|
||||
to_document(&config)
|
||||
}
|
||||
|
||||
/// Run any required task after resource updated in database but
|
||||
/// before the request resolves.
|
||||
async fn post_update(
|
||||
updated: &Resource<Self::Config, Self::Info>,
|
||||
update: &mut Update,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
// =======
|
||||
// DELETE
|
||||
// =======
|
||||
|
||||
fn delete_operation() -> Operation;
|
||||
|
||||
/// Clean up all links to this resource before deleting it.
|
||||
async fn pre_delete(
|
||||
resource: &Resource<Self::Config, Self::Info>,
|
||||
update: &mut Update,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
/// Run any required task after resource deleted from database but
|
||||
/// before the request resolves.
|
||||
async fn post_delete(
|
||||
resource: &Resource<Self::Config, Self::Info>,
|
||||
update: &mut Update,
|
||||
) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
// Methods
|
||||
|
||||
// ======
|
||||
// GET
|
||||
// ======
|
||||
|
||||
pub async fn get<T: MonitorResource>(
|
||||
id_or_name: &str,
|
||||
) -> anyhow::Result<Resource<T::Config, T::Info>> {
|
||||
T::coll()
|
||||
.await
|
||||
.find_one(id_or_name_filter(id_or_name), None)
|
||||
.await
|
||||
.context("failed to query db for resource")?
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"did not find any {} matching {id_or_name}",
|
||||
T::resource_type()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_check_permissions<T: MonitorResource>(
|
||||
id_or_name: &str,
|
||||
user: &User,
|
||||
permission_level: PermissionLevel,
|
||||
) -> anyhow::Result<Resource<T::Config, T::Info>> {
|
||||
let resource = get::<T>(id_or_name).await?;
|
||||
if user.admin {
|
||||
return Ok(resource);
|
||||
}
|
||||
let permissions = get_user_permission_on_resource(
|
||||
&user.id,
|
||||
T::resource_type(),
|
||||
&resource.id,
|
||||
)
|
||||
.await?;
|
||||
if permissions >= permission_level {
|
||||
Ok(resource)
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"user does not have required permissions on this {}",
|
||||
T::resource_type()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// ======
|
||||
// LIST
|
||||
// ======
|
||||
|
||||
pub async fn list_for_user<T: MonitorResource>(
|
||||
mut query: ResourceQuery<T::QuerySpecifics>,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<T::ListItem>> {
|
||||
validate_resource_query_tags(&mut query).await;
|
||||
let mut filters = Document::new();
|
||||
query.add_filters(&mut filters);
|
||||
list_for_user_using_document::<T>(filters, user).await
|
||||
}
|
||||
|
||||
pub async fn list_for_user_using_document<T: MonitorResource>(
|
||||
filters: Document,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<T::ListItem>> {
|
||||
let list = list_full_for_user_using_document::<T>(filters, user)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| T::to_list_item(resource));
|
||||
|
||||
let list = join_all(list)
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<anyhow::Result<Vec<_>>>()
|
||||
.context(format!(
|
||||
"failed to convert {} list item",
|
||||
T::resource_type()
|
||||
))?;
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
pub async fn list_full_for_user<T: MonitorResource>(
|
||||
mut query: ResourceQuery<T::QuerySpecifics>,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<Resource<T::Config, T::Info>>> {
|
||||
validate_resource_query_tags(&mut query).await;
|
||||
let mut filters = Document::new();
|
||||
query.add_filters(&mut filters);
|
||||
list_full_for_user_using_document::<T>(filters, user).await
|
||||
}
|
||||
|
||||
async fn list_full_for_user_using_document<T: MonitorResource>(
|
||||
mut filters: Document,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<Resource<T::Config, T::Info>>> {
|
||||
if !user.admin {
|
||||
let ids =
|
||||
get_resource_ids_for_non_admin(&user.id, T::resource_type())
|
||||
.await?
|
||||
.into_iter()
|
||||
.flat_map(|id| ObjectId::from_str(&id))
|
||||
.collect::<Vec<_>>();
|
||||
filters.insert("_id", doc! { "$in": ids });
|
||||
}
|
||||
find_collect(T::coll().await, filters, None)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to pull {}s from mongo", T::resource_type())
|
||||
})
|
||||
}
|
||||
|
||||
// =======
|
||||
// CREATE
|
||||
// =======
|
||||
|
||||
pub async fn create<T: MonitorResource>(
|
||||
name: &str,
|
||||
mut config: T::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Resource<T::Config, T::Info>> {
|
||||
if !T::user_can_create(user) {
|
||||
return Err(anyhow!(
|
||||
"User does not have permissions to create {}",
|
||||
T::resource_type()
|
||||
));
|
||||
}
|
||||
|
||||
let name = to_monitor_name(name);
|
||||
|
||||
if ObjectId::from_str(&name).is_ok() {
|
||||
return Err(anyhow!("valid ObjectIds cannot be used as names"));
|
||||
}
|
||||
|
||||
let start_ts = monitor_timestamp();
|
||||
|
||||
T::validate_create_config(&mut config, user).await?;
|
||||
|
||||
let resource = Resource::<T::Config, T::Info> {
|
||||
id: Default::default(),
|
||||
name,
|
||||
updated_at: start_ts,
|
||||
description: Default::default(),
|
||||
tags: Default::default(),
|
||||
config: config.into(),
|
||||
info: T::default_info().await?,
|
||||
};
|
||||
|
||||
let resource_id = T::coll()
|
||||
.await
|
||||
.insert_one(&resource, None)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to add {} to db", T::resource_type())
|
||||
})?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
|
||||
let resource = get::<T>(&resource_id).await?;
|
||||
let target = resource_target::<T>(resource_id);
|
||||
|
||||
create_permission(user, target.clone(), PermissionLevel::Write)
|
||||
.await;
|
||||
|
||||
let mut update = make_update(target, T::create_operation(), user);
|
||||
update.start_ts = start_ts;
|
||||
update.push_simple_log(
|
||||
&format!("create {}", T::resource_type()),
|
||||
format!(
|
||||
"created {}\nid: {}\nname: {}",
|
||||
T::resource_type(),
|
||||
resource.id,
|
||||
resource.name
|
||||
),
|
||||
);
|
||||
update.push_simple_log(
|
||||
"config",
|
||||
serde_json::to_string_pretty(&resource.config)
|
||||
.context("failed to serialize resource config to JSON")?,
|
||||
);
|
||||
|
||||
T::post_create(&resource, &mut update).await?;
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(resource)
|
||||
}
|
||||
|
||||
// =======
|
||||
// UPDATE
|
||||
// =======
|
||||
|
||||
pub async fn update<T: MonitorResource>(
|
||||
id_or_name: &str,
|
||||
mut config: T::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Resource<T::Config, T::Info>> {
|
||||
let resource = get_check_permissions::<T>(
|
||||
id_or_name,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if T::busy(&resource.id).await? {
|
||||
return Err(anyhow!("{} busy", T::resource_type()));
|
||||
}
|
||||
|
||||
T::validate_update_config(&resource.id, &mut config, user).await?;
|
||||
|
||||
// Gets a diff object.
|
||||
let diff = resource.config.partial_diff(config);
|
||||
|
||||
if diff.is_none() {
|
||||
return Err(anyhow!("update has no changes"));
|
||||
}
|
||||
|
||||
let mut diff_log = String::from("diff");
|
||||
|
||||
for FieldDiff { field, from, to } in diff.iter_field_diffs() {
|
||||
diff_log.push_str(&format!(
|
||||
"\n\nfield: '{field}'\nfrom: {from}\nto: {to}"
|
||||
));
|
||||
}
|
||||
|
||||
// This minimizes the update against the existing config
|
||||
let config: T::PartialConfig = diff.into();
|
||||
|
||||
let id = resource.id.clone();
|
||||
|
||||
let config_doc = T::update_document(resource, config)
|
||||
.context("failed to serialize config to bson document")?;
|
||||
|
||||
update_one_by_id(
|
||||
T::coll().await,
|
||||
&id,
|
||||
mungos::update::Update::FlattenSet(doc! { "config": config_doc }),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to update resource on database")?;
|
||||
|
||||
let mut update = make_update(
|
||||
resource_target::<T>(id),
|
||||
T::update_operation(),
|
||||
user,
|
||||
);
|
||||
|
||||
update.push_simple_log("update config", diff_log);
|
||||
|
||||
let updated = get::<T>(id_or_name).await?;
|
||||
|
||||
T::post_update(&updated, &mut update).await?;
|
||||
|
||||
update.finalize();
|
||||
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
fn resource_target<T: MonitorResource>(id: String) -> ResourceTarget {
|
||||
match T::resource_type() {
|
||||
ResourceTargetVariant::System => ResourceTarget::System(id),
|
||||
ResourceTargetVariant::Build => ResourceTarget::Build(id),
|
||||
ResourceTargetVariant::Builder => ResourceTarget::Builder(id),
|
||||
ResourceTargetVariant::Deployment => {
|
||||
ResourceTarget::Deployment(id)
|
||||
}
|
||||
ResourceTargetVariant::Server => ResourceTarget::Server(id),
|
||||
ResourceTargetVariant::Repo => ResourceTarget::Repo(id),
|
||||
ResourceTargetVariant::Alerter => ResourceTarget::Alerter(id),
|
||||
ResourceTargetVariant::Procedure => ResourceTarget::Procedure(id),
|
||||
ResourceTargetVariant::ServerTemplate => {
|
||||
ResourceTarget::ServerTemplate(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_description<T: MonitorResource>(
|
||||
id_or_name: &str,
|
||||
description: &str,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
get_check_permissions::<T>(
|
||||
id_or_name,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
T::coll()
|
||||
.await
|
||||
.update_one(
|
||||
id_or_name_filter(id_or_name),
|
||||
doc! { "$set": { "description": description } },
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_tags<T: MonitorResource>(
|
||||
id_or_name: &str,
|
||||
tags: Vec<String>,
|
||||
user: User,
|
||||
) -> anyhow::Result<()> {
|
||||
let futures = tags.iter().map(|tag| async {
|
||||
match get_tag(tag).await {
|
||||
Ok(tag) => Ok(tag.id),
|
||||
Err(_) => State
|
||||
.resolve(
|
||||
CreateTag {
|
||||
name: tag.to_string(),
|
||||
},
|
||||
user.clone(),
|
||||
)
|
||||
.await
|
||||
.map(|tag| tag.id),
|
||||
}
|
||||
});
|
||||
let tags = join_all(futures)
|
||||
.await
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
T::coll()
|
||||
.await
|
||||
.update_one(
|
||||
id_or_name_filter(id_or_name),
|
||||
doc! { "$set": { "tags": tags } },
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_tag_from_all<T: MonitorResource>(
|
||||
tag_id: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
T::coll()
|
||||
.await
|
||||
.update_many(doc! {}, doc! { "$pull": { "tags": tag_id } }, None)
|
||||
.await
|
||||
.context("failed to remove tag from resources")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// =======
|
||||
// DELETE
|
||||
// =======
|
||||
|
||||
pub async fn delete<T: MonitorResource>(
|
||||
id_or_name: &str,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Resource<T::Config, T::Info>> {
|
||||
let resource = get_check_permissions::<T>(
|
||||
id_or_name,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if T::busy(&resource.id).await? {
|
||||
return Err(anyhow!("{} busy", T::resource_type()));
|
||||
}
|
||||
|
||||
let target = resource_target::<T>(resource.id.clone());
|
||||
|
||||
let mut update =
|
||||
make_update(target.clone(), T::delete_operation(), user);
|
||||
|
||||
T::pre_delete(&resource, &mut update).await?;
|
||||
|
||||
delete_all_permissions_on_resource(target.clone()).await;
|
||||
remove_from_recently_viewed(target.clone()).await;
|
||||
|
||||
delete_one_by_id(T::coll().await, &resource.id, None)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to delete {} from database", T::resource_type())
|
||||
})?;
|
||||
|
||||
update.push_simple_log(
|
||||
&format!("delete {}", T::resource_type()),
|
||||
format!("deleted {} {}", T::resource_type(), resource.name),
|
||||
);
|
||||
|
||||
if let Err(e) = T::post_delete(&resource, &mut update).await {
|
||||
update.push_error_log("post delete", serialize_error_pretty(&e));
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
add_update(update).await?;
|
||||
|
||||
Ok(resource)
|
||||
}
|
||||
|
||||
// =======
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
pub async fn validate_resource_query_tags<
|
||||
T: Default + std::fmt::Debug,
|
||||
>(
|
||||
query: &mut ResourceQuery<T>,
|
||||
) {
|
||||
let futures = query.tags.iter().map(|tag| get_tag(tag));
|
||||
let res = join_all(futures).await;
|
||||
query.tags = res.into_iter().flatten().map(|tag| tag.id).collect();
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn delete_all_permissions_on_resource<T>(target: T)
|
||||
where
|
||||
T: Into<ResourceTarget> + std::fmt::Debug,
|
||||
{
|
||||
let target: ResourceTarget = target.into();
|
||||
let (variant, id) = target.extract_variant_id();
|
||||
if let Err(e) = db_client()
|
||||
.await
|
||||
.permissions
|
||||
.delete_many(
|
||||
doc! {
|
||||
"resource_target.type": variant.as_ref(),
|
||||
"resource_target.id": &id
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("failed to delete_many permissions matching target {target:?} | {e:#}");
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn remove_from_recently_viewed<T>(resource: T)
|
||||
where
|
||||
T: Into<ResourceTarget> + std::fmt::Debug,
|
||||
{
|
||||
let resource: ResourceTarget = resource.into();
|
||||
let (ty, id) = resource.extract_variant_id();
|
||||
if let Err(e) = db_client()
|
||||
.await
|
||||
.users
|
||||
.update_many(
|
||||
doc! {},
|
||||
doc! {
|
||||
"$pull": {
|
||||
"recently_viewed": {
|
||||
"type": ty.to_string(),
|
||||
"id": id,
|
||||
}
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to remove resource from users recently viewed")
|
||||
{
|
||||
warn!("{e:#}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
use anyhow::anyhow;
|
||||
use monitor_client::{
|
||||
api::execute::Execution,
|
||||
entities::{
|
||||
build::Build,
|
||||
deployment::Deployment,
|
||||
permission::PermissionLevel,
|
||||
procedure::{
|
||||
PartialProcedureConfig, Procedure, ProcedureConfig,
|
||||
ProcedureConfigDiff, ProcedureListItem, ProcedureListItemInfo,
|
||||
ProcedureQuerySpecifics,
|
||||
},
|
||||
repo::Repo,
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
},
|
||||
};
|
||||
use mungos::mongodb::Collection;
|
||||
|
||||
use crate::state::{action_states, db_client};
|
||||
|
||||
impl super::MonitorResource for Procedure {
|
||||
type Config = ProcedureConfig;
|
||||
type PartialConfig = PartialProcedureConfig;
|
||||
type ConfigDiff = ProcedureConfigDiff;
|
||||
type Info = ();
|
||||
type ListItem = ProcedureListItem;
|
||||
type QuerySpecifics = ProcedureQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Procedure
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.procedures
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
procedure: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
Ok(ProcedureListItem {
|
||||
name: procedure.name,
|
||||
id: procedure.id,
|
||||
tags: procedure.tags,
|
||||
resource_type: ResourceTargetVariant::Procedure,
|
||||
info: ProcedureListItemInfo {
|
||||
procedure_type: procedure.config.procedure_type,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(id: &String) -> anyhow::Result<bool> {
|
||||
action_states()
|
||||
.procedure
|
||||
.get(id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateProcedure
|
||||
}
|
||||
|
||||
fn user_can_create(_user: &User) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user, None).await
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
_created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateProcedure
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
id: &str,
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user, Some(id)).await
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
_updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteProcedure
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialProcedureConfig,
|
||||
user: &User,
|
||||
id: Option<&str>,
|
||||
) -> anyhow::Result<()> {
|
||||
let Some(executions) = &mut config.executions else {
|
||||
return Ok(());
|
||||
};
|
||||
for exec in executions {
|
||||
match &mut exec.execution {
|
||||
Execution::None(_) => {}
|
||||
Execution::RunProcedure(params) => {
|
||||
let procedure = super::get_check_permissions::<Procedure>(
|
||||
¶ms.procedure,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
match id {
|
||||
Some(id) if procedure.id == id => {
|
||||
return Err(anyhow!(
|
||||
"Cannot have self-referential procedure"
|
||||
))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
params.procedure = procedure.id;
|
||||
}
|
||||
Execution::RunBuild(params) => {
|
||||
let build = super::get_check_permissions::<Build>(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
}
|
||||
Execution::Deploy(params) => {
|
||||
let deployment = super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StartContainer(params) => {
|
||||
let deployment = super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StopContainer(params) => {
|
||||
let deployment = super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StopAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RemoveContainer(params) => {
|
||||
let deployment = super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::CloneRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::PullRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::PruneDockerNetworks(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneDockerImages(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneDockerContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute,
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
use anyhow::Context;
|
||||
use monitor_client::entities::{
|
||||
permission::PermissionLevel,
|
||||
repo::{
|
||||
PartialRepoConfig, Repo, RepoConfig, RepoConfigDiff, RepoInfo,
|
||||
RepoListItem, RepoListItemInfo, RepoQuerySpecifics,
|
||||
},
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
};
|
||||
use mungos::mongodb::Collection;
|
||||
use periphery_client::api::git::DeleteRepo;
|
||||
use serror::serialize_error_pretty;
|
||||
|
||||
use crate::{
|
||||
helpers::periphery_client,
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
|
||||
use super::get_check_permissions;
|
||||
|
||||
impl super::MonitorResource for Repo {
|
||||
type Config = RepoConfig;
|
||||
type PartialConfig = PartialRepoConfig;
|
||||
type ConfigDiff = RepoConfigDiff;
|
||||
type Info = RepoInfo;
|
||||
type ListItem = RepoListItem;
|
||||
type QuerySpecifics = RepoQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Repo
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.repos
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
repo: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
Ok(RepoListItem {
|
||||
name: repo.name,
|
||||
id: repo.id,
|
||||
tags: repo.tags,
|
||||
resource_type: ResourceTargetVariant::Repo,
|
||||
info: RepoListItemInfo {
|
||||
last_pulled_at: repo.info.last_pulled_at,
|
||||
repo: repo.config.repo,
|
||||
branch: repo.config.branch,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(id: &String) -> anyhow::Result<bool> {
|
||||
action_states()
|
||||
.repo
|
||||
.get(id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateRepo
|
||||
}
|
||||
|
||||
fn user_can_create(_user: &User) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
_created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateRepo
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
_id: &str,
|
||||
config: &mut Self::PartialConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
validate_config(config, user).await
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
_updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteRepo
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
repo: &Resource<Self::Config, Self::Info>,
|
||||
update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
if repo.config.server_id.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let server = super::get::<Server>(&repo.config.server_id).await?;
|
||||
let periphery = periphery_client(&server)?;
|
||||
|
||||
match periphery
|
||||
.request(DeleteRepo {
|
||||
name: repo.name.clone(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(log) => update.logs.push(log),
|
||||
Err(e) => update.push_error_log(
|
||||
"delete repo on periphery",
|
||||
serialize_error_pretty(&e),
|
||||
),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(user))]
|
||||
async fn validate_config(
|
||||
config: &mut PartialRepoConfig,
|
||||
user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
match &config.server_id {
|
||||
Some(server_id) if !server_id.is_empty() => {
|
||||
let server = get_check_permissions::<Server>(
|
||||
server_id,
|
||||
user,
|
||||
PermissionLevel::Write,
|
||||
)
|
||||
.await
|
||||
.context("cannot create repo on this server. user must have update permissions on the server.")?;
|
||||
config.server_id = Some(server.id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
use anyhow::Context;
|
||||
use monitor_client::entities::{
|
||||
monitor_timestamp,
|
||||
resource::Resource,
|
||||
server::{
|
||||
PartialServerConfig, Server, ServerConfig, ServerConfigDiff,
|
||||
ServerListItem, ServerListItemInfo, ServerQuerySpecifics,
|
||||
},
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
Operation,
|
||||
};
|
||||
use mungos::mongodb::{bson::doc, Collection};
|
||||
|
||||
use crate::{
|
||||
monitor::update_cache_for_server,
|
||||
state::{action_states, db_client, server_status_cache},
|
||||
};
|
||||
|
||||
impl super::MonitorResource for Server {
|
||||
type Config = ServerConfig;
|
||||
type PartialConfig = PartialServerConfig;
|
||||
type ConfigDiff = ServerConfigDiff;
|
||||
type Info = ();
|
||||
type ListItem = ServerListItem;
|
||||
type QuerySpecifics = ServerQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::Server
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.servers
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
server: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
let status = server_status_cache().get(&server.id).await;
|
||||
Ok(ServerListItem {
|
||||
name: server.name,
|
||||
id: server.id,
|
||||
tags: server.tags,
|
||||
resource_type: ResourceTargetVariant::Server,
|
||||
info: ServerListItemInfo {
|
||||
status: status.map(|s| s.status).unwrap_or_default(),
|
||||
region: server.config.region,
|
||||
send_unreachable_alerts: server
|
||||
.config
|
||||
.send_unreachable_alerts,
|
||||
send_cpu_alerts: server.config.send_cpu_alerts,
|
||||
send_mem_alerts: server.config.send_mem_alerts,
|
||||
send_disk_alerts: server.config.send_disk_alerts,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(id: &String) -> anyhow::Result<bool> {
|
||||
action_states()
|
||||
.server
|
||||
.get(id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.busy()
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateServer
|
||||
}
|
||||
|
||||
fn user_can_create(user: &User) -> bool {
|
||||
user.admin || user.create_server_permissions
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
_config: &mut Self::PartialConfig,
|
||||
_user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
update_cache_for_server(created).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateServer
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
_id: &str,
|
||||
_config: &mut Self::PartialConfig,
|
||||
_user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
update_cache_for_server(updated).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteServer
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
let db = db_client().await;
|
||||
|
||||
let id = &resource.id;
|
||||
|
||||
db.builders
|
||||
.update_many(
|
||||
doc! { "config.params.server_id": &id },
|
||||
doc! { "$set": { "config.params.server_id": "" } },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from builders")?;
|
||||
|
||||
db.deployments
|
||||
.update_many(
|
||||
doc! { "config.server_id": &id },
|
||||
doc! { "$set": { "config.server_id": "" } },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from deployments")?;
|
||||
|
||||
db.repos
|
||||
.update_many(
|
||||
doc! { "config.server_id": &id },
|
||||
doc! { "$set": { "config.server_id": "" } },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from repos")?;
|
||||
|
||||
db.alerts
|
||||
.update_many(
|
||||
doc! { "target.type": "Server", "target.id": &id },
|
||||
doc! { "$set": {
|
||||
"resolved": true,
|
||||
"resolved_ts": monitor_timestamp()
|
||||
} },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to detach server from repos")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
server_status_cache().remove(&resource.id).await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
use monitor_client::entities::{
|
||||
resource::Resource,
|
||||
server_template::{
|
||||
PartialServerTemplateConfig, ServerTemplate,
|
||||
ServerTemplateConfig, ServerTemplateConfigDiff,
|
||||
ServerTemplateConfigVariant, ServerTemplateListItem,
|
||||
ServerTemplateListItemInfo, ServerTemplateQuerySpecifics,
|
||||
},
|
||||
update::{ResourceTargetVariant, Update},
|
||||
user::User,
|
||||
MergePartial, Operation,
|
||||
};
|
||||
use mungos::mongodb::{
|
||||
bson::{to_document, Document},
|
||||
Collection,
|
||||
};
|
||||
|
||||
use crate::state::db_client;
|
||||
|
||||
impl super::MonitorResource for ServerTemplate {
|
||||
type Config = ServerTemplateConfig;
|
||||
type PartialConfig = PartialServerTemplateConfig;
|
||||
type ConfigDiff = ServerTemplateConfigDiff;
|
||||
type Info = ();
|
||||
type ListItem = ServerTemplateListItem;
|
||||
type QuerySpecifics = ServerTemplateQuerySpecifics;
|
||||
|
||||
fn resource_type() -> ResourceTargetVariant {
|
||||
ResourceTargetVariant::ServerTemplate
|
||||
}
|
||||
|
||||
async fn coll(
|
||||
) -> &'static Collection<Resource<Self::Config, Self::Info>> {
|
||||
&db_client().await.server_templates
|
||||
}
|
||||
|
||||
async fn to_list_item(
|
||||
server_template: Resource<Self::Config, Self::Info>,
|
||||
) -> anyhow::Result<Self::ListItem> {
|
||||
let (template_type, instance_type) = match server_template.config
|
||||
{
|
||||
ServerTemplateConfig::Aws(config) => (
|
||||
ServerTemplateConfigVariant::Aws.to_string(),
|
||||
Some(config.instance_type),
|
||||
),
|
||||
};
|
||||
Ok(ServerTemplateListItem {
|
||||
name: server_template.name,
|
||||
id: server_template.id,
|
||||
tags: server_template.tags,
|
||||
resource_type: ResourceTargetVariant::ServerTemplate,
|
||||
info: ServerTemplateListItemInfo {
|
||||
provider: template_type.to_string(),
|
||||
instance_type,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async fn busy(_id: &String) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// CREATE
|
||||
|
||||
fn create_operation() -> Operation {
|
||||
Operation::CreateServerTemplate
|
||||
}
|
||||
|
||||
fn user_can_create(user: &User) -> bool {
|
||||
user.admin
|
||||
}
|
||||
|
||||
async fn validate_create_config(
|
||||
_config: &mut Self::PartialConfig,
|
||||
_user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_create(
|
||||
_created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
|
||||
fn update_operation() -> Operation {
|
||||
Operation::UpdateServerTemplate
|
||||
}
|
||||
|
||||
async fn validate_update_config(
|
||||
_id: &str,
|
||||
_config: &mut Self::PartialConfig,
|
||||
_user: &User,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_document(
|
||||
original: Resource<Self::Config, Self::Info>,
|
||||
config: Self::PartialConfig,
|
||||
) -> Result<Document, mungos::mongodb::bson::ser::Error> {
|
||||
let config = original.config.merge_partial(config);
|
||||
to_document(&config)
|
||||
}
|
||||
|
||||
async fn post_update(
|
||||
_updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
||||
fn delete_operation() -> Operation {
|
||||
Operation::DeleteServerTemplate
|
||||
}
|
||||
|
||||
async fn pre_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn post_delete(
|
||||
_resource: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -25,8 +25,7 @@ use crate::{
|
||||
auth::{auth_api_key_check_enabled, auth_jwt_check_enabled},
|
||||
db::DbClient,
|
||||
helpers::{
|
||||
channel::update_channel,
|
||||
resource::get_user_permission_on_resource,
|
||||
channel::update_channel, query::get_user_permission_on_resource,
|
||||
},
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
@@ -74,9 +74,12 @@ pub struct Version {
|
||||
pub patch: i32,
|
||||
}
|
||||
|
||||
impl ToString for Version {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}.{}.{}", self.major, self.minor, self.patch)
|
||||
impl std::fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!(
|
||||
"{}.{}.{}",
|
||||
self.major, self.minor, self.patch
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -369,7 +369,7 @@ impl TryFrom<Deployment>
|
||||
.collect(),
|
||||
network: value.docker_run_args.network,
|
||||
restart: value.docker_run_args.restart.into(),
|
||||
process_args: value
|
||||
command: value
|
||||
.docker_run_args
|
||||
.post_image
|
||||
.unwrap_or_default(),
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
## Examples
|
||||
|
||||
```sh
|
||||
monrun resources up ./resources/prod_hub.toml
|
||||
```
|
||||
|
||||
```sh
|
||||
monrun exec
|
||||
```
|
||||
@@ -1,46 +0,0 @@
|
||||
[[build]]
|
||||
name = "test_logger"
|
||||
tags = ["test"]
|
||||
config.builder = "mogh-builder"
|
||||
config.repo = "mbecker20/test_logger"
|
||||
config.branch = "master"
|
||||
config.github_account = "mbecker20"
|
||||
config.docker_account = "mbecker2020"
|
||||
|
||||
[[build]]
|
||||
name = "alert_logger"
|
||||
tags = ["test"]
|
||||
config.builder = "mogh-builder"
|
||||
config.repo = "mbecker20/monitor"
|
||||
config.branch = "next"
|
||||
config.build_path = "."
|
||||
config.dockerfile_path = "bin/alert_logger/Dockerfile"
|
||||
config.docker_account = "mbecker2020"
|
||||
|
||||
[[build]]
|
||||
name = "update_logger"
|
||||
tags = ["test"]
|
||||
config.builder = "mogh-builder"
|
||||
config.repo = "mbecker20/monitor"
|
||||
config.branch = "next"
|
||||
config.build_path = "."
|
||||
config.dockerfile_path = "bin/update_logger/Dockerfile"
|
||||
config.docker_account = "mbecker2020"
|
||||
|
||||
# BUILDER
|
||||
[[builder]]
|
||||
name = "mogh-builder"
|
||||
description = "the builder for the mogh images"
|
||||
config.type = "Aws"
|
||||
config.params.region = "us-east-2"
|
||||
config.params.instance_type = "c5.2xlarge"
|
||||
config.params.volume_gb = 20
|
||||
config.params.ami_id = "ami-01c8bdba63708cde6"
|
||||
config.params.subnet_id = "subnet-02ae5ad480eacc4bc"
|
||||
config.params.key_pair_name = "mogh-key"
|
||||
config.params.assign_public_ip = true
|
||||
config.params.use_public_ip = false
|
||||
config.params.port = 8000
|
||||
config.params.security_group_ids = ["sg-049d98c819f9ace58", "sg-006c0ca638af8eb44"]
|
||||
config.params.github_accounts = ["mbecker20"]
|
||||
config.params.docker_accounts = ["mbecker2020"]
|
||||
@@ -1,51 +0,0 @@
|
||||
[[deployment]]
|
||||
name = "hello-world"
|
||||
tags = ["test"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Image"
|
||||
config.image.params.image = "hello-world"
|
||||
|
||||
[[deployment]]
|
||||
name = "test_logger"
|
||||
tags = ["test"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Build"
|
||||
config.image.params.build = "test_logger"
|
||||
|
||||
[[deployment]]
|
||||
name = "alert_logger"
|
||||
tags = ["test"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Build"
|
||||
config.image.params.build = "alert_logger"
|
||||
|
||||
[[deployment]]
|
||||
name = "update_logger"
|
||||
tags = ["test", "logging"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Build"
|
||||
config.image.params.build = "update_logger"
|
||||
[[deployment.config.environment]]
|
||||
variable = "MONITOR_ADDRESS"
|
||||
value = "http://localhost:9001"
|
||||
|
||||
[[deployment]]
|
||||
name = "vector"
|
||||
tags = ["logging"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Image"
|
||||
config.image.params.image = "timberio/vector"
|
||||
|
||||
[[deployment]]
|
||||
name = "loki"
|
||||
tags = ["logging"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Image"
|
||||
config.image.params.image = "grafana/loki"
|
||||
|
||||
[[deployment]]
|
||||
name = "grafana"
|
||||
tags = ["frontend"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Image"
|
||||
config.image.params.image = "grafana/grafana"
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
[[server]]
|
||||
name = "mogh-server"
|
||||
description = "main mogh server"
|
||||
tags = ["mogh", "prod"]
|
||||
config.address = "http://localhost:8001"
|
||||
config.region = "us-east-2"
|
||||
|
||||
[[server]]
|
||||
name = "testo"
|
||||
description = "test server 1"
|
||||
tags = ["mogh", "test"]
|
||||
config.address = "http://locahost:8120"
|
||||
config.enabled = false
|
||||
@@ -1,28 +0,0 @@
|
||||
[[user_group]]
|
||||
name = "uso_groupo"
|
||||
users = ["becker.maxh", "defi moses"]
|
||||
|
||||
[[user_group.permission]]
|
||||
target.type = "Server"
|
||||
target.id = "mogh-server"
|
||||
level = "Read"
|
||||
|
||||
[[user_group.permission]]
|
||||
target.type = "Deployment"
|
||||
target.id = "monitor-core-v1-dev"
|
||||
level = "Execute"
|
||||
|
||||
[[user_group.permission]]
|
||||
target.type = "Deployment"
|
||||
target.id = "monitor-frontend-v1-dev"
|
||||
level = "Execute"
|
||||
|
||||
[[user_group.permission]]
|
||||
target.type = "Build"
|
||||
target.id = "monitor-core-next"
|
||||
level = "Execute"
|
||||
|
||||
[[user_group.permission]]
|
||||
target.type = "Build"
|
||||
target.id = "monitor-frontend-next"
|
||||
level = "Execute"
|
||||
@@ -1,212 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use monitor_client::{
|
||||
api::write::{UpdateDescription, UpdateTagsOnResource},
|
||||
entities::{
|
||||
resource::{Resource, ResourceListItem},
|
||||
toml::ResourceToml,
|
||||
update::ResourceTarget,
|
||||
},
|
||||
};
|
||||
use partial_derive2::MaybeNone;
|
||||
|
||||
use crate::{cli_args, monitor_client};
|
||||
|
||||
pub mod alerter;
|
||||
pub mod build;
|
||||
pub mod builder;
|
||||
pub mod deployment;
|
||||
pub mod procedure;
|
||||
pub mod repo;
|
||||
pub mod server;
|
||||
|
||||
type ToUpdate<T> = Vec<(String, ResourceToml<T>)>;
|
||||
type ToCreate<T> = Vec<ResourceToml<T>>;
|
||||
type UpdatesResult<T> = (ToCreate<T>, ToUpdate<T>);
|
||||
|
||||
pub trait ResourceSync {
|
||||
type PartialConfig: std::fmt::Debug
|
||||
+ Clone
|
||||
+ Send
|
||||
+ MaybeNone
|
||||
+ 'static;
|
||||
type FullConfig: Clone + Send + 'static;
|
||||
type FullInfo: Default;
|
||||
type ListItemInfo: 'static;
|
||||
|
||||
fn display() -> &'static str;
|
||||
|
||||
fn resource_target(id: String) -> ResourceTarget;
|
||||
|
||||
fn name_to_resource(
|
||||
) -> &'static HashMap<String, ResourceListItem<Self::ListItemInfo>>;
|
||||
|
||||
/// Creates the resource and returns created id.
|
||||
async fn create(
|
||||
resource: ResourceToml<Self::PartialConfig>,
|
||||
) -> anyhow::Result<String>;
|
||||
|
||||
/// Updates the resource at id with the partial config.
|
||||
async fn update(
|
||||
id: String,
|
||||
resource: ResourceToml<Self::PartialConfig>,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
async fn get(
|
||||
id: String,
|
||||
) -> anyhow::Result<Resource<Self::FullConfig, Self::FullInfo>>;
|
||||
|
||||
/// Diffs the declared toml (partial) against the full existing config.
|
||||
/// Removes all fields from toml (partial) that haven't changed.
|
||||
async fn minimize_update(
|
||||
original: Self::FullConfig,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::PartialConfig>;
|
||||
|
||||
async fn get_updates(
|
||||
resources: Vec<ResourceToml<Self::PartialConfig>>,
|
||||
) -> anyhow::Result<UpdatesResult<Self::PartialConfig>> {
|
||||
let map = Self::name_to_resource();
|
||||
|
||||
let mut to_create = ToCreate::<Self::PartialConfig>::new();
|
||||
let mut to_update = ToUpdate::<Self::PartialConfig>::new();
|
||||
|
||||
for mut resource in resources {
|
||||
match map.get(&resource.name).map(|s| s.id.clone()) {
|
||||
Some(id) => {
|
||||
// Get the full original config for the resource.
|
||||
let original = Self::get(id.clone()).await?.config;
|
||||
|
||||
// Minimizes updates through diffing.
|
||||
resource.config =
|
||||
Self::minimize_update(original, resource.config).await?;
|
||||
|
||||
// Only try to update if there are any fields to update.
|
||||
if !resource.config.is_none() {
|
||||
to_update.push((id, resource));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
to_create.push(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let verbose = cli_args().verbose;
|
||||
|
||||
if !to_create.is_empty() {
|
||||
if verbose {
|
||||
println!("\n{} TO CREATE:\n{to_create:#?}", Self::display(),);
|
||||
} else {
|
||||
println!(
|
||||
"\n{} TO CREATE: {:#?}",
|
||||
Self::display(),
|
||||
to_create
|
||||
.iter()
|
||||
.map(|item| item.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !to_update.is_empty() {
|
||||
if verbose {
|
||||
println!(
|
||||
"\n{} TO UPDATE:\n{:#?}",
|
||||
Self::display(),
|
||||
to_update.iter().map(|(_, r)| r).collect::<Vec<_>>()
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"\n{} TO UPDATE: {}",
|
||||
Self::display(),
|
||||
to_update
|
||||
.iter()
|
||||
.map(|(_, item)| item.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((to_create, to_update))
|
||||
}
|
||||
|
||||
async fn run_updates(
|
||||
to_create: ToCreate<Self::PartialConfig>,
|
||||
to_update: ToUpdate<Self::PartialConfig>,
|
||||
) {
|
||||
let log_after = !to_update.is_empty() || !to_create.is_empty();
|
||||
|
||||
for resource in to_create {
|
||||
let name = resource.name.clone();
|
||||
let tags = resource.tags.clone();
|
||||
let description = resource.description.clone();
|
||||
let id = match Self::create(resource).await {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"failed to create {} {name} | {e:#}",
|
||||
Self::display(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
Self::update_tags(id.clone(), &name, tags).await;
|
||||
Self::update_description(id, description).await;
|
||||
info!("{} {name} created", Self::display());
|
||||
}
|
||||
|
||||
for (id, resource) in to_update {
|
||||
// Update resource
|
||||
let name = resource.name.clone();
|
||||
let tags = resource.tags.clone();
|
||||
let description = resource.description.clone();
|
||||
if let Err(e) = Self::update(id.clone(), resource).await {
|
||||
warn!("failed to update {} {name} | {e:#}", Self::display());
|
||||
}
|
||||
Self::update_tags(id.clone(), &name, tags).await;
|
||||
Self::update_description(id, description).await;
|
||||
info!("{} {name} updated", Self::display());
|
||||
}
|
||||
|
||||
if log_after {
|
||||
info!(
|
||||
"============ {}s synced ✅ ============",
|
||||
Self::display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_tags(
|
||||
resource_id: String,
|
||||
resource_name: &str,
|
||||
tags: Vec<String>,
|
||||
) {
|
||||
// Update tags
|
||||
if let Err(e) = monitor_client()
|
||||
.write(UpdateTagsOnResource {
|
||||
target: Self::resource_target(resource_id),
|
||||
tags,
|
||||
})
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"failed to update tags on {} {resource_name} | {e:#}",
|
||||
Self::display(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_description(id: String, description: String) {
|
||||
if let Err(e) = monitor_client()
|
||||
.write(UpdateDescription {
|
||||
target: Self::resource_target(id.clone()),
|
||||
description,
|
||||
})
|
||||
.await
|
||||
{
|
||||
warn!("failed to update resource {id} description | {e:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,7 @@ fn get_version_image_name(
|
||||
image_name: &str,
|
||||
version: &Version,
|
||||
) -> String {
|
||||
format!("{image_name}:{}", version.to_string())
|
||||
format!("{image_name}:{version}")
|
||||
}
|
||||
|
||||
fn get_latest_image_name(image_name: &str) -> String {
|
||||
|
||||
@@ -197,7 +197,7 @@ pub async fn deploy(
|
||||
return Log::error("docker login", serialize_error_pretty(&e))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if let Err(e) = docker_login(
|
||||
&optional_string(&deployment.config.docker_account),
|
||||
&docker_token,
|
||||
@@ -266,7 +266,7 @@ pub fn docker_run_command(
|
||||
volumes,
|
||||
ports,
|
||||
network,
|
||||
process_args,
|
||||
command,
|
||||
restart,
|
||||
environment,
|
||||
labels,
|
||||
@@ -285,9 +285,9 @@ pub fn docker_run_command(
|
||||
let restart = parse_restart(restart);
|
||||
let environment = parse_environment(environment);
|
||||
let labels = parse_labels(labels);
|
||||
let process_args = parse_process_args(process_args);
|
||||
let command = parse_command(command);
|
||||
let extra_args = parse_extra_args(extra_args);
|
||||
format!("docker run -d --name {name}{ports}{volumes}{network}{restart}{environment}{labels}{extra_args} {image}{process_args}")
|
||||
format!("docker run -d --name {name}{ports}{volumes}{network}{restart}{environment}{labels}{extra_args} {image}{command}")
|
||||
}
|
||||
|
||||
fn parse_conversions(
|
||||
@@ -321,10 +321,10 @@ fn parse_restart(restart: &RestartMode) -> String {
|
||||
format!(" --restart {restart}")
|
||||
}
|
||||
|
||||
fn parse_process_args(process_args: &String) -> String {
|
||||
if process_args.is_empty() {
|
||||
fn parse_command(command: &str) -> String {
|
||||
if command.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" {process_args}")
|
||||
format!(" {command}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[package]
|
||||
name = "monitor_client"
|
||||
description = "Client for the monitor build and deployment system"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@@ -35,6 +35,19 @@
|
||||
//! - [execute]: Run actions on monitor resources, eg [execute::RunBuild].
|
||||
//! - [write]: Requests which alter data, like create / update / delete resources.
|
||||
//!
|
||||
//! ## Errors
|
||||
//!
|
||||
//! Request errors will be returned with a JSON body containing information about the error.
|
||||
//! They will have the following common format:
|
||||
//! ```
|
||||
//! {
|
||||
//! "error": "top level error message",
|
||||
//! "trace": [
|
||||
//! "first traceback message",
|
||||
//! "second traceback message"
|
||||
//! ]
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
pub mod auth;
|
||||
pub mod execute;
|
||||
|
||||
@@ -23,7 +23,6 @@ impl Busy for DeploymentActionState {
|
||||
|| self.starting
|
||||
|| self.stopping
|
||||
|| self.renaming
|
||||
|| self.deleting
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +34,7 @@ impl Busy for BuildActionState {
|
||||
|
||||
impl Busy for RepoActionState {
|
||||
fn busy(&self) -> bool {
|
||||
self.cloning || self.pulling || self.updating || self.deleting
|
||||
self.cloning || self.pulling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@ use derive_builder::Builder;
|
||||
use derive_default_builder::DefaultBuilder;
|
||||
use derive_variants::EnumVariants;
|
||||
use mungos::mongodb::bson::{doc, Document};
|
||||
use partial_derive2::{MaybeNone, Partial, PartialDiff};
|
||||
use partial_derive2::{Diff, MaybeNone, Partial, PartialDiff};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{AsRefStr, Display, EnumString};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use super::resource::{
|
||||
AddFilters, Resource, ResourceListItem, ResourceQuery,
|
||||
use super::{
|
||||
resource::{AddFilters, Resource, ResourceListItem, ResourceQuery},
|
||||
MergePartial,
|
||||
};
|
||||
|
||||
#[typeshare]
|
||||
@@ -56,28 +57,6 @@ pub enum AlerterConfig {
|
||||
Slack(SlackAlerterConfig),
|
||||
}
|
||||
|
||||
impl PartialDiff<PartialAlerterConfig> for AlerterConfig {
|
||||
fn partial_diff(
|
||||
&self,
|
||||
partial: PartialAlerterConfig,
|
||||
) -> PartialAlerterConfig {
|
||||
match self {
|
||||
AlerterConfig::Custom(original) => match partial {
|
||||
PartialAlerterConfig::Custom(partial) => {
|
||||
PartialAlerterConfig::Custom(original.partial_diff(partial))
|
||||
}
|
||||
slack => slack,
|
||||
},
|
||||
AlerterConfig::Slack(original) => match partial {
|
||||
PartialAlerterConfig::Slack(partial) => {
|
||||
PartialAlerterConfig::Slack(original.partial_diff(partial))
|
||||
}
|
||||
custom => custom,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, EnumVariants)]
|
||||
#[variant_derive(
|
||||
@@ -96,11 +75,81 @@ pub enum PartialAlerterConfig {
|
||||
Slack(_PartialSlackAlerterConfig),
|
||||
}
|
||||
|
||||
impl MaybeNone for PartialAlerterConfig {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum AlerterConfigDiff {
|
||||
Custom(CustomAlerterConfigDiff),
|
||||
Slack(SlackAlerterConfigDiff),
|
||||
}
|
||||
|
||||
impl From<AlerterConfigDiff> for PartialAlerterConfig {
|
||||
fn from(value: AlerterConfigDiff) -> Self {
|
||||
match value {
|
||||
AlerterConfigDiff::Custom(diff) => {
|
||||
PartialAlerterConfig::Custom(diff.into())
|
||||
}
|
||||
AlerterConfigDiff::Slack(diff) => {
|
||||
PartialAlerterConfig::Slack(diff.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Diff for AlerterConfigDiff {
|
||||
fn iter_field_diffs(
|
||||
&self,
|
||||
) -> impl Iterator<Item = partial_derive2::FieldDiff> {
|
||||
match self {
|
||||
AlerterConfigDiff::Custom(diff) => {
|
||||
diff.iter_field_diffs().collect::<Vec<_>>().into_iter()
|
||||
}
|
||||
AlerterConfigDiff::Slack(diff) => {
|
||||
diff.iter_field_diffs().collect::<Vec<_>>().into_iter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialDiff<PartialAlerterConfig, AlerterConfigDiff>
|
||||
for AlerterConfig
|
||||
{
|
||||
fn partial_diff(
|
||||
&self,
|
||||
partial: PartialAlerterConfig,
|
||||
) -> AlerterConfigDiff {
|
||||
match self {
|
||||
AlerterConfig::Custom(original) => match partial {
|
||||
PartialAlerterConfig::Custom(partial) => {
|
||||
AlerterConfigDiff::Custom(original.partial_diff(partial))
|
||||
}
|
||||
PartialAlerterConfig::Slack(partial) => {
|
||||
let full: SlackAlerterConfig = partial.into();
|
||||
AlerterConfigDiff::Slack(SlackAlerterConfigDiff {
|
||||
url: Some((original.url.clone(), full.url)),
|
||||
enabled: Some((original.enabled, full.enabled)),
|
||||
})
|
||||
}
|
||||
},
|
||||
AlerterConfig::Slack(original) => match partial {
|
||||
PartialAlerterConfig::Slack(partial) => {
|
||||
AlerterConfigDiff::Slack(original.partial_diff(partial))
|
||||
}
|
||||
PartialAlerterConfig::Custom(partial) => {
|
||||
let full: CustomAlerterConfig = partial.into();
|
||||
AlerterConfigDiff::Custom(CustomAlerterConfigDiff {
|
||||
url: Some((original.url.clone(), full.url)),
|
||||
enabled: Some((original.enabled, full.enabled)),
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaybeNone for AlerterConfigDiff {
|
||||
fn is_none(&self) -> bool {
|
||||
match self {
|
||||
PartialAlerterConfig::Custom(config) => config.is_none(),
|
||||
PartialAlerterConfig::Slack(config) => config.is_none(),
|
||||
AlerterConfigDiff::Custom(config) => config.is_none(),
|
||||
AlerterConfigDiff::Slack(config) => config.is_none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,8 +180,9 @@ impl From<AlerterConfig> for PartialAlerterConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl AlerterConfig {
|
||||
pub fn merge_partial(
|
||||
impl MergePartial for AlerterConfig {
|
||||
type Partial = PartialAlerterConfig;
|
||||
fn merge_partial(
|
||||
self,
|
||||
partial: PartialAlerterConfig,
|
||||
) -> AlerterConfig {
|
||||
@@ -171,13 +221,21 @@ pub type _PartialCustomAlerterConfig = PartialCustomAlerterConfig;
|
||||
#[partial(skip_serializing_none, from, diff)]
|
||||
pub struct CustomAlerterConfig {
|
||||
/// The http/s endpoint to send the POST to
|
||||
#[partial_default(String::from("http://localhost:7000"))]
|
||||
#[serde(default = "default_custom_url")]
|
||||
#[builder(default = "default_custom_url()")]
|
||||
#[partial_default(default_custom_url())]
|
||||
pub url: String,
|
||||
|
||||
/// Whether the alerter is enabled
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
fn default_custom_url() -> String {
|
||||
String::from("http://localhost:7000")
|
||||
}
|
||||
|
||||
#[typeshare(serialized_as = "Partial<SlackAlerterConfig>")]
|
||||
pub type _PartialSlackAlerterConfig = PartialSlackAlerterConfig;
|
||||
|
||||
@@ -188,15 +246,32 @@ pub type _PartialSlackAlerterConfig = PartialSlackAlerterConfig;
|
||||
#[partial(skip_serializing_none, from, diff)]
|
||||
pub struct SlackAlerterConfig {
|
||||
/// The slack app url
|
||||
#[partial_default(String::from(
|
||||
"https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
))]
|
||||
#[serde(default = "default_slack_url")]
|
||||
#[builder(default = "default_slack_url()")]
|
||||
#[partial_default(default_slack_url())]
|
||||
pub url: String,
|
||||
|
||||
/// Whether the alerter is enabled
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for SlackAlerterConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
url: default_slack_url(),
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_slack_url() -> String {
|
||||
String::from(
|
||||
"https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
)
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type AlerterQuery = ResourceQuery<AlerterQuerySpecifics>;
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use derive_builder::Builder;
|
||||
use derive_variants::EnumVariants;
|
||||
use partial_derive2::{MaybeNone, Partial, PartialDiff};
|
||||
use partial_derive2::{Diff, MaybeNone, Partial, PartialDiff};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{Display, EnumString};
|
||||
use typeshare::typeshare;
|
||||
|
||||
use super::resource::{
|
||||
AddFilters, Resource, ResourceListItem, ResourceQuery,
|
||||
use super::{
|
||||
resource::{AddFilters, Resource, ResourceListItem, ResourceQuery},
|
||||
MergePartial,
|
||||
};
|
||||
|
||||
#[typeshare]
|
||||
@@ -45,28 +46,6 @@ pub enum BuilderConfig {
|
||||
Aws(AwsBuilderConfig),
|
||||
}
|
||||
|
||||
impl PartialDiff<PartialBuilderConfig> for BuilderConfig {
|
||||
fn partial_diff(
|
||||
&self,
|
||||
partial: PartialBuilderConfig,
|
||||
) -> PartialBuilderConfig {
|
||||
match self {
|
||||
BuilderConfig::Server(original) => match partial {
|
||||
PartialBuilderConfig::Server(partial) => {
|
||||
PartialBuilderConfig::Server(original.partial_diff(partial))
|
||||
}
|
||||
aws => aws,
|
||||
},
|
||||
BuilderConfig::Aws(original) => match partial {
|
||||
PartialBuilderConfig::Aws(partial) => {
|
||||
PartialBuilderConfig::Aws(original.partial_diff(partial))
|
||||
}
|
||||
server => server,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Partial representation of [BuilderConfig]
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, EnumVariants)]
|
||||
@@ -85,11 +64,76 @@ pub enum PartialBuilderConfig {
|
||||
Aws(_PartialAwsBuilderConfig),
|
||||
}
|
||||
|
||||
impl MaybeNone for PartialBuilderConfig {
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum BuilderConfigDiff {
|
||||
Server(ServerBuilderConfigDiff),
|
||||
Aws(AwsBuilderConfigDiff),
|
||||
}
|
||||
|
||||
impl From<BuilderConfigDiff> for PartialBuilderConfig {
|
||||
fn from(value: BuilderConfigDiff) -> Self {
|
||||
match value {
|
||||
BuilderConfigDiff::Server(diff) => {
|
||||
PartialBuilderConfig::Server(diff.into())
|
||||
}
|
||||
BuilderConfigDiff::Aws(diff) => {
|
||||
PartialBuilderConfig::Aws(diff.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Diff for BuilderConfigDiff {
|
||||
fn iter_field_diffs(
|
||||
&self,
|
||||
) -> impl Iterator<Item = partial_derive2::FieldDiff> {
|
||||
match self {
|
||||
BuilderConfigDiff::Server(diff) => {
|
||||
diff.iter_field_diffs().collect::<Vec<_>>().into_iter()
|
||||
}
|
||||
BuilderConfigDiff::Aws(diff) => {
|
||||
diff.iter_field_diffs().collect::<Vec<_>>().into_iter()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialDiff<PartialBuilderConfig, BuilderConfigDiff>
|
||||
for BuilderConfig
|
||||
{
|
||||
fn partial_diff(
|
||||
&self,
|
||||
partial: PartialBuilderConfig,
|
||||
) -> BuilderConfigDiff {
|
||||
match self {
|
||||
BuilderConfig::Server(original) => match partial {
|
||||
PartialBuilderConfig::Server(partial) => {
|
||||
BuilderConfigDiff::Server(original.partial_diff(partial))
|
||||
}
|
||||
PartialBuilderConfig::Aws(partial) => {
|
||||
let default = AwsBuilderConfig::default();
|
||||
BuilderConfigDiff::Aws(default.partial_diff(partial))
|
||||
}
|
||||
},
|
||||
BuilderConfig::Aws(original) => match partial {
|
||||
PartialBuilderConfig::Aws(partial) => {
|
||||
BuilderConfigDiff::Aws(original.partial_diff(partial))
|
||||
}
|
||||
PartialBuilderConfig::Server(partial) => {
|
||||
let default = ServerBuilderConfig::default();
|
||||
BuilderConfigDiff::Server(default.partial_diff(partial))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaybeNone for BuilderConfigDiff {
|
||||
fn is_none(&self) -> bool {
|
||||
match self {
|
||||
PartialBuilderConfig::Server(config) => config.is_none(),
|
||||
PartialBuilderConfig::Aws(config) => config.is_none(),
|
||||
BuilderConfigDiff::Server(config) => config.is_none(),
|
||||
BuilderConfigDiff::Aws(config) => config.is_none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,8 +164,9 @@ impl From<BuilderConfig> for PartialBuilderConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl BuilderConfig {
|
||||
pub fn merge_partial(
|
||||
impl MergePartial for BuilderConfig {
|
||||
type Partial = PartialBuilderConfig;
|
||||
fn merge_partial(
|
||||
self,
|
||||
partial: PartialBuilderConfig,
|
||||
) -> BuilderConfig {
|
||||
@@ -178,7 +223,9 @@ pub type _PartialServerBuilderConfig = PartialServerBuilderConfig;
|
||||
|
||||
/// Configuration for a monitor server builder.
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Default, Builder, Partial,
|
||||
)]
|
||||
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
#[partial(skip_serializing_none, from, diff)]
|
||||
pub struct ServerBuilderConfig {
|
||||
@@ -193,7 +240,7 @@ pub type _PartialAwsBuilderConfig = PartialAwsBuilderConfig;
|
||||
|
||||
/// Configuration for an AWS builder.
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Builder, Partial)]
|
||||
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
#[partial(skip_serializing_none, from, diff)]
|
||||
pub struct AwsBuilderConfig {
|
||||
@@ -248,6 +295,25 @@ pub struct AwsBuilderConfig {
|
||||
pub docker_accounts: Vec<String>,
|
||||
}
|
||||
|
||||
impl Default for AwsBuilderConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
region: aws_default_region(),
|
||||
instance_type: aws_default_instance_type(),
|
||||
volume_gb: aws_default_volume_gb(),
|
||||
port: default_port(),
|
||||
ami_id: Default::default(),
|
||||
subnet_id: Default::default(),
|
||||
security_group_ids: Default::default(),
|
||||
key_pair_name: Default::default(),
|
||||
assign_public_ip: false,
|
||||
use_public_ip: false,
|
||||
github_accounts: Default::default(),
|
||||
docker_accounts: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AwsBuilderConfig {
|
||||
pub fn builder() -> AwsBuilderConfigBuilder {
|
||||
AwsBuilderConfigBuilder::default()
|
||||
|
||||
@@ -233,7 +233,7 @@ fn default_periphery_port() -> u16 {
|
||||
}
|
||||
|
||||
fn default_repo_dir() -> PathBuf {
|
||||
"/repos".parse().unwrap()
|
||||
"/etc/monitor/repos".parse().unwrap()
|
||||
}
|
||||
|
||||
fn default_stats_refresh_interval() -> Timelength {
|
||||
|
||||
@@ -128,10 +128,10 @@ pub struct DeploymentConfig {
|
||||
/// This is interpolated at the end of the `docker run` command,
|
||||
/// which means they are either passed to the containers inner process,
|
||||
/// or replaces the container command, depending on use of ENTRYPOINT or CMD in dockerfile.
|
||||
/// Empty is no process args.
|
||||
/// Empty is no command.
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub process_args: String,
|
||||
pub command: String,
|
||||
|
||||
/// Extra args which are interpolated into the `docker run` command,
|
||||
/// and affect the container configuration.
|
||||
@@ -374,7 +374,6 @@ pub struct DeploymentActionState {
|
||||
pub starting: bool,
|
||||
pub removing: bool,
|
||||
pub renaming: bool,
|
||||
pub deleting: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -43,6 +43,11 @@ pub type _Serror = Serror;
|
||||
)]
|
||||
pub struct NoData {}
|
||||
|
||||
pub trait MergePartial: Sized {
|
||||
type Partial;
|
||||
fn merge_partial(self, partial: Self::Partial) -> Self;
|
||||
}
|
||||
|
||||
pub fn all_logs_success(logs: &[update::Log]) -> bool {
|
||||
for log in logs {
|
||||
if !log.success {
|
||||
@@ -147,9 +152,12 @@ pub struct Version {
|
||||
pub patch: i32,
|
||||
}
|
||||
|
||||
impl ToString for Version {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}.{}.{}", self.major, self.minor, self.patch)
|
||||
impl std::fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!(
|
||||
"{}.{}.{}",
|
||||
self.major, self.minor, self.patch
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,8 +91,6 @@ fn default_branch() -> String {
|
||||
pub struct RepoActionState {
|
||||
pub cloning: bool,
|
||||
pub pulling: bool,
|
||||
pub updating: bool,
|
||||
pub deleting: bool,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -28,13 +28,16 @@ pub struct Resource<Config, Info: Default = ()> {
|
||||
#[builder(setter(skip))]
|
||||
pub id: MongoId,
|
||||
|
||||
///
|
||||
/// The resource name.
|
||||
/// This is guaranteed unique among others of the same resource type.
|
||||
pub name: String,
|
||||
|
||||
/// A description for the resource
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub description: String,
|
||||
|
||||
/// When description last updated
|
||||
#[serde(default)]
|
||||
#[builder(setter(skip))]
|
||||
pub updated_at: I64,
|
||||
@@ -44,23 +47,28 @@ pub struct Resource<Config, Info: Default = ()> {
|
||||
#[builder(default)]
|
||||
pub tags: Vec<String>,
|
||||
|
||||
/// Resource-specific information (not user configurable).
|
||||
#[serde(default)]
|
||||
#[builder(setter(skip))]
|
||||
pub info: Info,
|
||||
|
||||
/// Resource-specific configuration.
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ResourceListItem<Info> {
|
||||
/// The resource id
|
||||
pub id: String,
|
||||
/// The resource type, ie `Server` or `Deployment`
|
||||
#[serde(rename = "type")]
|
||||
pub resource_type: ResourceTargetVariant,
|
||||
/// The resource name
|
||||
pub name: String,
|
||||
pub created_at: I64,
|
||||
/// Tag Ids
|
||||
pub tags: Vec<String>,
|
||||
/// Resource specific info
|
||||
pub info: Info,
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ impl ServerConfig {
|
||||
}
|
||||
|
||||
fn default_enabled() -> bool {
|
||||
true
|
||||
false
|
||||
}
|
||||
|
||||
fn default_stats_monitoring() -> bool {
|
||||
|
||||
@@ -2,7 +2,7 @@ use derive_builder::Builder;
|
||||
use derive_default_builder::DefaultBuilder;
|
||||
use derive_variants::EnumVariants;
|
||||
use mungos::mongodb::bson::{doc, Document};
|
||||
use partial_derive2::{MaybeNone, Partial, PartialDiff};
|
||||
use partial_derive2::{Diff, MaybeNone, Partial, PartialDiff};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::{AsRefStr, Display, EnumString};
|
||||
use typeshare::typeshare;
|
||||
@@ -10,6 +10,7 @@ use typeshare::typeshare;
|
||||
use super::{
|
||||
builder::AwsBuilderConfig,
|
||||
resource::{AddFilters, Resource, ResourceListItem, ResourceQuery},
|
||||
MergePartial,
|
||||
};
|
||||
|
||||
#[typeshare]
|
||||
@@ -46,25 +47,6 @@ pub enum ServerTemplateConfig {
|
||||
Aws(AwsServerTemplateConfig),
|
||||
}
|
||||
|
||||
impl PartialDiff<PartialServerTemplateConfig>
|
||||
for ServerTemplateConfig
|
||||
{
|
||||
fn partial_diff(
|
||||
&self,
|
||||
partial: PartialServerTemplateConfig,
|
||||
) -> PartialServerTemplateConfig {
|
||||
match self {
|
||||
ServerTemplateConfig::Aws(original) => match partial {
|
||||
PartialServerTemplateConfig::Aws(partial) => {
|
||||
PartialServerTemplateConfig::Aws(
|
||||
original.partial_diff(partial),
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, EnumVariants)]
|
||||
#[variant_derive(
|
||||
@@ -82,10 +64,55 @@ pub enum PartialServerTemplateConfig {
|
||||
Aws(_PartialAwsServerTemplateConfig),
|
||||
}
|
||||
|
||||
impl MaybeNone for PartialServerTemplateConfig {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerTemplateConfigDiff {
|
||||
Aws(AwsServerTemplateConfigDiff),
|
||||
}
|
||||
|
||||
impl From<ServerTemplateConfigDiff> for PartialServerTemplateConfig {
|
||||
fn from(value: ServerTemplateConfigDiff) -> Self {
|
||||
match value {
|
||||
ServerTemplateConfigDiff::Aws(diff) => {
|
||||
PartialServerTemplateConfig::Aws(diff.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Diff for ServerTemplateConfigDiff {
|
||||
fn iter_field_diffs(
|
||||
&self,
|
||||
) -> impl Iterator<Item = partial_derive2::FieldDiff> {
|
||||
match self {
|
||||
ServerTemplateConfigDiff::Aws(diff) => diff.iter_field_diffs(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
PartialDiff<PartialServerTemplateConfig, ServerTemplateConfigDiff>
|
||||
for ServerTemplateConfig
|
||||
{
|
||||
fn partial_diff(
|
||||
&self,
|
||||
partial: PartialServerTemplateConfig,
|
||||
) -> ServerTemplateConfigDiff {
|
||||
match self {
|
||||
ServerTemplateConfig::Aws(original) => match partial {
|
||||
PartialServerTemplateConfig::Aws(partial) => {
|
||||
ServerTemplateConfigDiff::Aws(
|
||||
original.partial_diff(partial),
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaybeNone for ServerTemplateConfigDiff {
|
||||
fn is_none(&self) -> bool {
|
||||
match self {
|
||||
PartialServerTemplateConfig::Aws(config) => config.is_none(),
|
||||
ServerTemplateConfigDiff::Aws(config) => config.is_none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,8 +139,9 @@ impl From<ServerTemplateConfig> for PartialServerTemplateConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerTemplateConfig {
|
||||
pub fn merge_partial(
|
||||
impl MergePartial for ServerTemplateConfig {
|
||||
type Partial = PartialServerTemplateConfig;
|
||||
fn merge_partial(
|
||||
self,
|
||||
partial: PartialServerTemplateConfig,
|
||||
) -> ServerTemplateConfig {
|
||||
@@ -141,6 +169,7 @@ impl ServerTemplateConfig {
|
||||
.use_public_ip
|
||||
.unwrap_or(config.use_public_ip),
|
||||
port: partial.port.unwrap_or(config.port),
|
||||
user_data: partial.user_data.unwrap_or(config.user_data),
|
||||
};
|
||||
ServerTemplateConfig::Aws(config)
|
||||
}
|
||||
@@ -160,24 +189,32 @@ pub type _PartialAwsServerTemplateConfig =
|
||||
#[partial(skip_serializing_none, from, diff)]
|
||||
pub struct AwsServerTemplateConfig {
|
||||
/// The aws region to launch the server in, eg. us-east-1
|
||||
#[serde(default = "default_region")]
|
||||
#[builder(default = "default_region()")]
|
||||
#[partial_default(default_region())]
|
||||
pub region: String,
|
||||
/// The instance type to launch, eg. c5.2xlarge
|
||||
#[serde(default = "default_instance_type")]
|
||||
#[builder(default = "default_instance_type()")]
|
||||
#[partial_default(default_instance_type())]
|
||||
pub instance_type: String,
|
||||
/// Specify the EBS volumes to attach.
|
||||
pub volumes: Vec<AwsVolume>,
|
||||
/// Specify the ami id to use. Must be set up to start the periphery binary on startup.
|
||||
pub ami_id: String,
|
||||
/// The subnet to assign to the instance.
|
||||
pub subnet_id: String,
|
||||
/// The security groups to give to the instance.
|
||||
pub security_group_ids: Vec<String>,
|
||||
/// The key pair name to give to the instance in case SSH access required.
|
||||
pub key_pair_name: String,
|
||||
/// Assign a public ip to the instance. Depending on how your network is
|
||||
/// setup, this may be required for the instance to reach the public internet.
|
||||
#[serde(default = "default_assign_public_ip")]
|
||||
#[builder(default = "default_assign_public_ip()")]
|
||||
#[partial_default(default_assign_public_ip())]
|
||||
pub assign_public_ip: bool,
|
||||
/// Use the instances public ip as the address for the server.
|
||||
/// Could be used when build instances are created in another non-interconnected network to the core api.
|
||||
#[serde(default = "default_use_public_ip")]
|
||||
#[builder(default = "default_use_public_ip()")]
|
||||
#[partial_default(default_use_public_ip())]
|
||||
pub use_public_ip: bool,
|
||||
/// The port periphery will be running on in AMI.
|
||||
/// Default: `8120`
|
||||
@@ -185,6 +222,45 @@ pub struct AwsServerTemplateConfig {
|
||||
#[builder(default = "default_port()")]
|
||||
#[partial_default(default_port())]
|
||||
pub port: i32,
|
||||
/// The user data to deploy the instance with.
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub user_data: String,
|
||||
/// The security groups to give to the instance.
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub security_group_ids: Vec<String>,
|
||||
/// Specify the EBS volumes to attach.
|
||||
#[serde(default = "default_volumes")]
|
||||
#[builder(default = "default_volumes()")]
|
||||
#[partial_default(default_volumes())]
|
||||
pub volumes: Vec<AwsVolume>,
|
||||
}
|
||||
|
||||
fn default_region() -> String {
|
||||
String::from("us-east-1")
|
||||
}
|
||||
|
||||
fn default_instance_type() -> String {
|
||||
String::from("t3.small")
|
||||
}
|
||||
|
||||
fn default_assign_public_ip() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn default_use_public_ip() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn default_volumes() -> Vec<AwsVolume> {
|
||||
vec![AwsVolume {
|
||||
device_name: "/dev/sda1".to_string(),
|
||||
size_gb: 20,
|
||||
volume_type: AwsVolumeType::Gp2,
|
||||
iops: 0,
|
||||
throughput: 0,
|
||||
}]
|
||||
}
|
||||
|
||||
fn default_port() -> i32 {
|
||||
@@ -200,12 +276,35 @@ pub struct AwsVolume {
|
||||
pub device_name: String,
|
||||
/// The size of the volume in GB
|
||||
pub size_gb: i32,
|
||||
/// The type of volume, eg gp2, gp3, io1
|
||||
pub volume_type: Option<String>,
|
||||
/// The iops of the volume, or AWS default.
|
||||
pub iops: Option<i32>,
|
||||
/// The throughput of the volume, or AWS default.
|
||||
pub throughput: Option<i32>,
|
||||
/// The type of volume. Options: gp2, gp3, io1, io2.
|
||||
pub volume_type: AwsVolumeType,
|
||||
/// The iops of the volume, or 0 for AWS default.
|
||||
pub iops: i32,
|
||||
/// The throughput of the volume, or 0 for AWS default.
|
||||
pub throughput: i32,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Default,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Display,
|
||||
AsRefStr,
|
||||
)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum AwsVolumeType {
|
||||
#[default]
|
||||
Gp2,
|
||||
Gp3,
|
||||
Io1,
|
||||
Io2,
|
||||
}
|
||||
|
||||
impl AwsServerTemplateConfig {
|
||||
@@ -214,11 +313,11 @@ impl AwsServerTemplateConfig {
|
||||
region: value.region.clone(),
|
||||
instance_type: value.instance_type.clone(),
|
||||
volumes: vec![AwsVolume {
|
||||
size_gb: value.volume_gb,
|
||||
device_name: "/dev/sda1".to_string(),
|
||||
volume_type: None,
|
||||
iops: None,
|
||||
throughput: None,
|
||||
size_gb: value.volume_gb,
|
||||
volume_type: AwsVolumeType::Gp2,
|
||||
iops: 0,
|
||||
throughput: 0,
|
||||
}],
|
||||
ami_id: value.ami_id.clone(),
|
||||
subnet_id: value.subnet_id.clone(),
|
||||
@@ -227,6 +326,7 @@ impl AwsServerTemplateConfig {
|
||||
assign_public_ip: value.assign_public_ip,
|
||||
use_public_ip: value.use_public_ip,
|
||||
port: value.port,
|
||||
user_data: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,9 +79,10 @@ pub struct ResourcesToml {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ResourceToml<PartialConfig> {
|
||||
///
|
||||
/// The resource name. Required
|
||||
pub name: String,
|
||||
|
||||
/// The resource description.
|
||||
#[serde(default)]
|
||||
pub description: String,
|
||||
|
||||
@@ -89,16 +90,20 @@ pub struct ResourceToml<PartialConfig> {
|
||||
#[serde(default)]
|
||||
pub tags: Vec<String>,
|
||||
|
||||
/// Resource specific configuration
|
||||
pub config: PartialConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UserGroupToml {
|
||||
/// User group name
|
||||
pub name: String,
|
||||
|
||||
/// Users in the group
|
||||
#[serde(default)]
|
||||
pub users: Vec<String>,
|
||||
|
||||
/// Permissions given to the group
|
||||
#[serde(default, rename = "permission")]
|
||||
pub permissions: Vec<PermissionToml>,
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user