mirror of
https://github.com/moghtech/komodo.git
synced 2026-11-02 00:02:23 -06:00
Compare commits
156
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54d167fe1e | ||
|
|
6b6743876e | ||
|
|
38a4c4a470 | ||
|
|
84368411ca | ||
|
|
e41f350776 | ||
|
|
3fef8644cc | ||
|
|
baef9cc1b1 | ||
|
|
7c2403b7d1 | ||
|
|
95ffa5b4f5 | ||
|
|
72a095f6be | ||
|
|
67c869931f | ||
|
|
42ea59aa23 | ||
|
|
3abbab87e6 | ||
|
|
d16ebbe526 | ||
|
|
b44a6f7e57 | ||
|
|
1f078a0432 | ||
|
|
7f9b7aa6cf | ||
|
|
96aefa75a9 | ||
|
|
9cf2df74ef | ||
|
|
47f5e5bd37 | ||
|
|
51683612f5 | ||
|
|
108c9215d1 | ||
|
|
1fd3836a4a | ||
|
|
b765e9576d | ||
|
|
cea6fd2038 | ||
|
|
60c3eda08a | ||
|
|
b09419cd4b | ||
|
|
13ff216ea7 | ||
|
|
d56d34c448 | ||
|
|
d0da77a6b8 | ||
|
|
7725cfa5bd | ||
|
|
cc5d947412 | ||
|
|
3badb678db | ||
|
|
f8af6da1a2 | ||
|
|
dca0ae772b | ||
|
|
558fb7bade | ||
|
|
cb7943ed5b | ||
|
|
92967d3278 | ||
|
|
b796dc8398 | ||
|
|
7bdbaca4b4 | ||
|
|
a90971eea6 | ||
|
|
5643424ed8 | ||
|
|
b1f5cccc0a | ||
|
|
e82a268861 | ||
|
|
77b3c6d325 | ||
|
|
9f90a913fc | ||
|
|
76e84ddd6e | ||
|
|
2dd759e375 | ||
|
|
b2b9df0e2a | ||
|
|
6a42d47b6f | ||
|
|
ca440c14b6 | ||
|
|
f2478de853 | ||
|
|
ebb4ea3c87 | ||
|
|
77ffb14f7d | ||
|
|
7430798f31 | ||
|
|
881542a21c | ||
|
|
0857583778 | ||
|
|
7d9eab92e7 | ||
|
|
d9abbbadaf | ||
|
|
877a013fc0 | ||
|
|
a6811783ec | ||
|
|
7c406fd9db | ||
|
|
c84083062c | ||
|
|
4efc27b4db | ||
|
|
9f413fec21 | ||
|
|
e8e1d1dfbc | ||
|
|
bceb1e6ebf | ||
|
|
0222ad3b3c | ||
|
|
3dad87e18f | ||
|
|
1a5779ca14 | ||
|
|
a24fefca52 | ||
|
|
25e957ef0c | ||
|
|
0629744356 | ||
|
|
5f6469cb01 | ||
|
|
7b502cc6ca | ||
|
|
2c328f27e4 | ||
|
|
34da2063f9 | ||
|
|
cac20e4ff2 | ||
|
|
6c17049cb7 | ||
|
|
9f4b246e43 | ||
|
|
5dd865a856 | ||
|
|
7293a7004a | ||
|
|
b82ec53c33 | ||
|
|
7ce1fdaf78 | ||
|
|
ce0425befd | ||
|
|
4cbd9d2ac7 | ||
|
|
8b92d1e435 | ||
|
|
593967b6c7 | ||
|
|
e2a38a75f8 | ||
|
|
9847b9499a | ||
|
|
2e1d001bfe | ||
|
|
65be710adc | ||
|
|
f122a3e18b | ||
|
|
250bc017ca | ||
|
|
4f93ef4d52 | ||
|
|
9c871497e4 | ||
|
|
fa5ac54243 | ||
|
|
3720c92908 | ||
|
|
aa99982687 | ||
|
|
232437a9d8 | ||
|
|
ea0381ad41 | ||
|
|
10476e561d | ||
|
|
f3e77ccffe | ||
|
|
c94d83bc3a | ||
|
|
9ede3977c5 | ||
|
|
160709788a | ||
|
|
3c89a72841 | ||
|
|
9e71f008df | ||
|
|
c4cbfada00 | ||
|
|
24890e8aed | ||
|
|
5a272d1e98 | ||
|
|
1444e803de | ||
|
|
dd59f56b6d | ||
|
|
4729dc2e3c | ||
|
|
178203c07c | ||
|
|
919ae99e2c | ||
|
|
62ea22af7f | ||
|
|
d17ba3f9c4 | ||
|
|
03f3b2c80d | ||
|
|
e2c52fea6b | ||
|
|
ae9f2d9f74 | ||
|
|
842c67bb50 | ||
|
|
056050e534 | ||
|
|
88ac4cf9dd | ||
|
|
c58ec67f0a | ||
|
|
2ba2cb1e0b | ||
|
|
ebe9bd75ad | ||
|
|
4faf031b8a | ||
|
|
738c347155 | ||
|
|
4c1d7a61dc | ||
|
|
7af8e5b287 | ||
|
|
8bf4ea482a | ||
|
|
527b300caa | ||
|
|
d0067f0d2b | ||
|
|
cca040c718 | ||
|
|
63be48e2fb | ||
|
|
d723515d28 | ||
|
|
0c34695d1a | ||
|
|
3c4551ae19 | ||
|
|
da5ac544f6 | ||
|
|
c3dc710598 | ||
|
|
6c3b7cc30b | ||
|
|
8b19fdba40 | ||
|
|
5e3ef5910a | ||
|
|
08e2c51301 | ||
|
|
7943655fb8 | ||
|
|
e22eda939c | ||
|
|
a28892e726 | ||
|
|
295f9927d4 | ||
|
|
a9aad11a51 | ||
|
|
aafb5a68ed | ||
|
|
1e4c7b8498 | ||
|
|
aca4c09bf8 | ||
|
|
5af8dd4553 | ||
|
|
75627901ce | ||
|
|
adcb0c0755 |
Generated
+54
-54
@@ -106,9 +106,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.99"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
@@ -178,9 +178,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "aws-config"
|
||||
version = "1.8.5"
|
||||
version = "1.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c478f5b10ce55c9a33f87ca3404ca92768b144fc1bfdede7c0121214a8283a25"
|
||||
checksum = "483020b893cdef3d89637e428d588650c71cfae7ea2e6ecbaee4de4ff99fb2dd"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -267,9 +267,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ec2"
|
||||
version = "1.159.0"
|
||||
version = "1.156.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed753fc534bbb68760d5b2e3e46f81e5ccb9a96d8359b9ce793e123cb3073e8e"
|
||||
checksum = "0e04d00427a65f27b7f704f43352560d1eba7d31204a9566e2c633fb5c1d96ed"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -290,9 +290,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sso"
|
||||
version = "1.81.0"
|
||||
version = "1.79.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79ede098271e3471036c46957cba2ba30888f53bda2515bf04b560614a30a36e"
|
||||
checksum = "0a847168f15b46329fa32c7aca4e4f1a2e072f9b422f0adb19756f2e1457f111"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -312,9 +312,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-ssooidc"
|
||||
version = "1.82.0"
|
||||
version = "1.80.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43326f724ba2cc957e6f3deac0ca1621a3e5d4146f5970c24c8a108dac33070f"
|
||||
checksum = "b654dd24d65568738593e8239aef279a86a15374ec926ae8714e2d7245f34149"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -334,9 +334,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-sdk-sts"
|
||||
version = "1.83.0"
|
||||
version = "1.81.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5468593c47efc31fdbe6c902d1a5fde8d9c82f78a3f8ccfe907b1e9434748cb"
|
||||
checksum = "c92ea8a7602321c83615c82b408820ad54280fb026e92de0eeea937342fafa24"
|
||||
dependencies = [
|
||||
"aws-credential-types",
|
||||
"aws-runtime",
|
||||
@@ -828,7 +828,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_urlencoded",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
@@ -904,7 +904,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cache"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"tokio",
|
||||
@@ -1071,7 +1071,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "command"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"komodo_client",
|
||||
"run_command",
|
||||
@@ -1080,7 +1080,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "config"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"indexmap 2.10.0",
|
||||
@@ -1088,7 +1088,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml_ng",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"toml",
|
||||
"wildcard",
|
||||
]
|
||||
@@ -1320,7 +1320,7 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||
|
||||
[[package]]
|
||||
name = "database"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
@@ -1619,7 +1619,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "environment"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"formatting",
|
||||
@@ -1629,9 +1629,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "environment_file"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1719,7 +1719,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "formatting"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"serror",
|
||||
]
|
||||
@@ -1881,7 +1881,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "git"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cache",
|
||||
@@ -2487,7 +2487,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "interpolate"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -2618,7 +2618,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_cli"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -2643,7 +2643,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_client"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async_timing_util",
|
||||
@@ -2666,7 +2666,7 @@ dependencies = [
|
||||
"serde_qs",
|
||||
"serror",
|
||||
"strum 0.27.2",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.27.0",
|
||||
"tokio-util",
|
||||
@@ -2677,7 +2677,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_core"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -2747,7 +2747,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komodo_periphery"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@@ -2868,7 +2868,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "logger"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -3017,7 +3017,7 @@ dependencies = [
|
||||
"mongo_indexed_derive",
|
||||
"mongodb",
|
||||
"serde",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3369,7 +3369,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"js-sys",
|
||||
"pin-project-lite",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@@ -3399,7 +3399,7 @@ dependencies = [
|
||||
"opentelemetry_sdk",
|
||||
"prost",
|
||||
"reqwest",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"tonic",
|
||||
"tracing",
|
||||
]
|
||||
@@ -3435,7 +3435,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"rand 0.9.2",
|
||||
"serde_json",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
]
|
||||
@@ -3610,7 +3610,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "periphery_client"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"komodo_client",
|
||||
@@ -3807,7 +3807,7 @@ dependencies = [
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls 0.23.31",
|
||||
"socket2 0.5.10",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -3828,7 +3828,7 @@ dependencies = [
|
||||
"rustls 0.23.31",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -4141,7 +4141,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "response"
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -4599,7 +4599,7 @@ checksum = "f3faaf9e727533a19351a43cc5a8de957372163c7d35cc48c90b75cdda13c352"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4782,7 +4782,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -4931,7 +4931,7 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91bb902c47385644774ba89f7e7982d3a32cf1da4e8870bf633cdece450eced1"
|
||||
dependencies = [
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4978,9 +4978,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.37.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753"
|
||||
checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"memchr",
|
||||
@@ -5034,11 +5034,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.14"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.14",
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5054,9 +5054,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.14"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -5277,14 +5277,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_pretty"
|
||||
version = "1.2.0"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dd195be7600b4b26e951a25b382b67a976d2d44bb967a1a5b573a3e63d18c29"
|
||||
checksum = "dd412f79539d72f97ac0ec9905b0178be1be4f255ddc2ae6f1ff3f7b234bb870"
|
||||
dependencies = [
|
||||
"ordered_hash_map",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5488,7 +5488,7 @@ dependencies = [
|
||||
"log",
|
||||
"rand 0.9.2",
|
||||
"sha1",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
@@ -5507,7 +5507,7 @@ dependencies = [
|
||||
"rustls 0.23.31",
|
||||
"rustls-pki-types",
|
||||
"sha1",
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
@@ -5862,7 +5862,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9b0540e91e49de3817c314da0dd3bc518093ceacc6ea5327cb0e1eb073e5189"
|
||||
dependencies = [
|
||||
"thiserror 2.0.14",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+7
-7
@@ -8,7 +8,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.19.0"
|
||||
version = "1.19.0-dev-9"
|
||||
edition = "2024"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -42,7 +42,7 @@ partial_derive2 = "0.4.3"
|
||||
derive_variants = "1.0.0"
|
||||
mongo_indexed = "2.0.2"
|
||||
resolver_api = "3.0.0"
|
||||
toml_pretty = "1.2.0"
|
||||
toml_pretty = "1.1.2"
|
||||
mungos = "3.2.1"
|
||||
svi = "1.2.0"
|
||||
|
||||
@@ -74,8 +74,8 @@ serde_qs = "0.15.0"
|
||||
toml = "0.9.5"
|
||||
|
||||
# ERROR
|
||||
anyhow = "1.0.99"
|
||||
thiserror = "2.0.14"
|
||||
anyhow = "1.0.98"
|
||||
thiserror = "2.0.12"
|
||||
|
||||
# LOGGING
|
||||
opentelemetry-otlp = { version = "0.30.0", features = ["tls-roots", "reqwest-rustls"] }
|
||||
@@ -108,11 +108,11 @@ hex = "0.4.3"
|
||||
# SYSTEM
|
||||
portable-pty = "0.9.0"
|
||||
bollard = "0.19.2"
|
||||
sysinfo = "0.37.0"
|
||||
sysinfo = "0.36.1"
|
||||
|
||||
# CLOUD
|
||||
aws-config = "1.8.5"
|
||||
aws-sdk-ec2 = "1.159.0"
|
||||
aws-config = "1.8.4"
|
||||
aws-sdk-ec2 = "1.156.0"
|
||||
aws-credential-types = "1.2.5"
|
||||
|
||||
## CRON
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Builds the Komodo Core, Periphery, and Util binaries
|
||||
## for a specific architecture.
|
||||
|
||||
FROM rust:1.89.0-bullseye AS builder
|
||||
FROM rust:1.88.0-bullseye AS builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
## Builds the Komodo Core, Periphery, and Util binaries
|
||||
## for a specific architecture.
|
||||
|
||||
## Uses chef for dependency caching to help speed up back-to-back builds.
|
||||
|
||||
FROM lukemathwalker/cargo-chef:latest-rust-1.89.0-bullseye AS chef
|
||||
WORKDIR /builder
|
||||
|
||||
# Plan just the RECIPE to see if things have changed
|
||||
FROM chef AS planner
|
||||
COPY . .
|
||||
RUN cargo chef prepare --recipe-path recipe.json
|
||||
|
||||
FROM chef AS builder
|
||||
COPY --from=planner /builder/recipe.json recipe.json
|
||||
# Build JUST dependencies - cached layer
|
||||
RUN cargo chef cook --release --recipe-path recipe.json
|
||||
# NOW copy again (this time into builder) and build app
|
||||
COPY . .
|
||||
RUN \
|
||||
cargo build --release --bin core && \
|
||||
cargo build --release --bin periphery && \
|
||||
cargo build --release --bin km
|
||||
|
||||
# Copy just the binaries to scratch image
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /builder/target/release/core /core
|
||||
COPY --from=builder /builder/target/release/periphery /periphery
|
||||
COPY --from=builder /builder/target/release/km /km
|
||||
|
||||
LABEL org.opencontainers.image.source=https://github.com/moghtech/komodo
|
||||
LABEL org.opencontainers.image.description="Komodo Binaries"
|
||||
LABEL org.opencontainers.image.licenses=GPL-3.0
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM rust:1.89.0-bullseye AS builder
|
||||
FROM rust:1.88.0-bullseye AS builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
|
||||
@@ -7,13 +7,13 @@ Can be used to move between MongoDB / FerretDB, or upgrade from FerretDB v1 to v
|
||||
services:
|
||||
|
||||
copy_database:
|
||||
image: ghcr.io/moghtech/komodo-cli
|
||||
command: km database copy -y
|
||||
image: ghcr.io/moghtech/komodo-util
|
||||
environment:
|
||||
KOMODO_DATABASE_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@source:27017
|
||||
KOMODO_DATABASE_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
KOMODO_CLI_DATABASE_TARGET_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@target:27017
|
||||
KOMODO_CLI_DATABASE_TARGET_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
MODE: CopyDatabase
|
||||
SOURCE_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@source:27017
|
||||
SOURCE_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
TARGET_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@target:27017
|
||||
TARGET_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
|
||||
```
|
||||
|
||||
@@ -90,13 +90,13 @@ services:
|
||||
...(new database)
|
||||
|
||||
copy_database:
|
||||
image: ghcr.io/moghtech/komodo-cli
|
||||
command: km database copy -y
|
||||
image: ghcr.io/moghtech/komodo-util
|
||||
environment:
|
||||
KOMODO_DATABASE_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@ferretdb:27017/${KOMODO_DATABASE_DB_NAME:-komodo}?authMechanism=PLAIN
|
||||
KOMODO_DATABASE_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
KOMODO_CLI_DATABASE_TARGET_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@ferretdb2:27017
|
||||
KOMODO_CLI_DATABASE_TARGET_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
MODE: CopyDatabase
|
||||
SOURCE_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@ferretdb:27017/${KOMODO_DATABASE_DB_NAME:-komodo}?authMechanism=PLAIN
|
||||
SOURCE_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
TARGET_URI: mongodb://${KOMODO_DB_USERNAME}:${KOMODO_DB_PASSWORD}@ferretdb2:27017
|
||||
TARGET_DB_NAME: ${KOMODO_DATABASE_DB_NAME:-komodo}
|
||||
|
||||
...(unchanged)
|
||||
```
|
||||
|
||||
@@ -14,17 +14,15 @@ use komodo_client::{
|
||||
config::cli::args::container::{
|
||||
Container, ContainerCommand, InspectContainer,
|
||||
},
|
||||
docker::{
|
||||
self,
|
||||
container::{ContainerListItem, ContainerStateStatusEnum},
|
||||
docker::container::{
|
||||
ContainerListItem, ContainerStateStatusEnum,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::{
|
||||
PrintTable, clamp_sha, matches_wildcards, parse_wildcards,
|
||||
print_items,
|
||||
PrintTable, matches_wildcards, parse_wildcards, print_items,
|
||||
},
|
||||
config::cli_config,
|
||||
};
|
||||
@@ -42,7 +40,6 @@ async fn list_containers(
|
||||
Container {
|
||||
all,
|
||||
down,
|
||||
links,
|
||||
reverse,
|
||||
containers: names,
|
||||
images,
|
||||
@@ -129,7 +126,7 @@ async fn list_containers(
|
||||
if *reverse {
|
||||
containers.reverse();
|
||||
}
|
||||
print_items(containers, *format, *links)?;
|
||||
print_items(containers, *format)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -199,60 +196,38 @@ pub async fn inspect_container(
|
||||
);
|
||||
}
|
||||
1 => {
|
||||
println!("{}", serialize_container(inspect, &containers[0])?);
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&containers[0])
|
||||
.context("Failed to serialize items to JSON")?
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
let containers = containers
|
||||
.iter()
|
||||
.map(|c| serialize_container(inspect, c))
|
||||
.collect::<anyhow::Result<Vec<_>>>()?
|
||||
.join("\n");
|
||||
println!("{containers}");
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&containers)
|
||||
.context("Failed to serialize items to JSON")?
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_container(
|
||||
inspect: &InspectContainer,
|
||||
container: &docker::container::Container,
|
||||
) -> anyhow::Result<String> {
|
||||
let res = if inspect.state {
|
||||
serde_json::to_string_pretty(&container.state)
|
||||
} else if inspect.mounts {
|
||||
serde_json::to_string_pretty(&container.mounts)
|
||||
} else if inspect.host_config {
|
||||
serde_json::to_string_pretty(&container.host_config)
|
||||
} else if inspect.config {
|
||||
serde_json::to_string_pretty(&container.config)
|
||||
} else if inspect.network_settings {
|
||||
serde_json::to_string_pretty(&container.network_settings)
|
||||
} else {
|
||||
serde_json::to_string_pretty(container)
|
||||
}
|
||||
.context("Failed to serialize items to JSON")?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
// (Option<Server Name>, Container)
|
||||
impl PrintTable for (Option<&'_ str>, ContainerListItem) {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&[
|
||||
"Container",
|
||||
"State",
|
||||
"Server",
|
||||
"Ports",
|
||||
"Networks",
|
||||
"Image",
|
||||
"Link",
|
||||
]
|
||||
} else {
|
||||
&["Container", "State", "Server", "Ports", "Networks", "Image"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&[
|
||||
"Container",
|
||||
"State",
|
||||
"Server",
|
||||
"Ports",
|
||||
"Networks",
|
||||
"Image",
|
||||
"Link",
|
||||
]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<Cell> {
|
||||
fn row(self) -> Vec<Cell> {
|
||||
let color = match self.1.state {
|
||||
ContainerStateStatusEnum::Running => Color::Green,
|
||||
ContainerStateStatusEnum::Paused => Color::DarkYellow,
|
||||
@@ -282,21 +257,6 @@ impl PrintTable for (Option<&'_ str>, ContainerListItem) {
|
||||
} else {
|
||||
Cell::new(format!(":{}", ports.join(", :")))
|
||||
};
|
||||
|
||||
let image = self.1.image.as_deref().unwrap_or("Unknown");
|
||||
let mut res = vec![
|
||||
Cell::new(self.1.name.clone()).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.1.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.0.unwrap_or("Unknown")),
|
||||
ports,
|
||||
Cell::new(networks.join(", ")),
|
||||
Cell::new(clamp_sha(&image)),
|
||||
];
|
||||
if !links {
|
||||
return res;
|
||||
}
|
||||
let link = if let Some(server_id) = self.1.server_id {
|
||||
format!(
|
||||
"{}/servers/{server_id}/container/{}",
|
||||
@@ -306,7 +266,16 @@ impl PrintTable for (Option<&'_ str>, ContainerListItem) {
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
res.push(Cell::new(link));
|
||||
res
|
||||
vec![
|
||||
Cell::new(self.1.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.1.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.0.unwrap_or("Unknown")),
|
||||
ports,
|
||||
Cell::new(networks.join(", ")),
|
||||
Cell::new(self.1.image.as_deref().unwrap_or("Unknown")),
|
||||
Cell::new(link),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,12 +218,6 @@ pub async fn handle(
|
||||
Execution::ClearRepoCache(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BackupCoreDatabase(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::GlobalAutoUpdate(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::Sleep(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
@@ -472,14 +466,6 @@ pub async fn handle(
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BackupCoreDatabase(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::GlobalAutoUpdate(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Sleep(request) => {
|
||||
let duration =
|
||||
Duration::from_millis(request.duration_ms as u64);
|
||||
|
||||
+120
-323
@@ -51,38 +51,38 @@ pub async fn handle(list: &args::list::List) -> anyhow::Result<()> {
|
||||
match &list.command {
|
||||
None => list_all(list).await,
|
||||
Some(ListCommand::Servers(filters)) => {
|
||||
list_resources::<ServerListItem>(filters, false).await
|
||||
list_resources::<ServerListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Stacks(filters)) => {
|
||||
list_resources::<StackListItem>(filters, false).await
|
||||
list_resources::<StackListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Deployments(filters)) => {
|
||||
list_resources::<DeploymentListItem>(filters, false).await
|
||||
list_resources::<DeploymentListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Builds(filters)) => {
|
||||
list_resources::<BuildListItem>(filters, false).await
|
||||
list_resources::<BuildListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Repos(filters)) => {
|
||||
list_resources::<RepoListItem>(filters, false).await
|
||||
list_resources::<RepoListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Procedures(filters)) => {
|
||||
list_resources::<ProcedureListItem>(filters, false).await
|
||||
list_resources::<ProcedureListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Actions(filters)) => {
|
||||
list_resources::<ActionListItem>(filters, false).await
|
||||
list_resources::<ActionListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Syncs(filters)) => {
|
||||
list_resources::<ResourceSyncListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Builders(filters)) => {
|
||||
list_resources::<BuilderListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Alerters(filters)) => {
|
||||
list_resources::<AlerterListItem>(filters, false).await
|
||||
list_resources::<ResourceSyncListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Schedules(filters)) => {
|
||||
list_schedules(filters).await
|
||||
}
|
||||
Some(ListCommand::Builders(filters)) => {
|
||||
list_resources::<BuilderListItem>(filters).await
|
||||
}
|
||||
Some(ListCommand::Alerters(filters)) => {
|
||||
list_resources::<AlerterListItem>(filters).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,61 +105,61 @@ async fn list_all(list: &args::list::List) -> anyhow::Result<()> {
|
||||
.into_iter()
|
||||
.map(|t| (t.id, t.name))
|
||||
.collect::<HashMap<_, _>>())),
|
||||
ServerListItem::list(client, &filters, true),
|
||||
StackListItem::list(client, &filters, true),
|
||||
DeploymentListItem::list(client, &filters, true),
|
||||
BuildListItem::list(client, &filters, true),
|
||||
RepoListItem::list(client, &filters, true),
|
||||
ProcedureListItem::list(client, &filters, true),
|
||||
ActionListItem::list(client, &filters, true),
|
||||
ResourceSyncListItem::list(client, &filters, true),
|
||||
ServerListItem::list(client, &filters),
|
||||
StackListItem::list(client, &filters),
|
||||
DeploymentListItem::list(client, &filters),
|
||||
BuildListItem::list(client, &filters),
|
||||
RepoListItem::list(client, &filters),
|
||||
ProcedureListItem::list(client, &filters),
|
||||
ActionListItem::list(client, &filters),
|
||||
ResourceSyncListItem::list(client, &filters),
|
||||
)?;
|
||||
|
||||
if !servers.is_empty() {
|
||||
fix_tags(&mut servers, &tags);
|
||||
print_items(servers, filters.format, list.links)?;
|
||||
print_items(servers, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !stacks.is_empty() {
|
||||
fix_tags(&mut stacks, &tags);
|
||||
print_items(stacks, filters.format, list.links)?;
|
||||
print_items(stacks, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !deployments.is_empty() {
|
||||
fix_tags(&mut deployments, &tags);
|
||||
print_items(deployments, filters.format, list.links)?;
|
||||
print_items(deployments, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !builds.is_empty() {
|
||||
fix_tags(&mut builds, &tags);
|
||||
print_items(builds, filters.format, list.links)?;
|
||||
print_items(builds, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !repos.is_empty() {
|
||||
fix_tags(&mut repos, &tags);
|
||||
print_items(repos, filters.format, list.links)?;
|
||||
print_items(repos, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !procedures.is_empty() {
|
||||
fix_tags(&mut procedures, &tags);
|
||||
print_items(procedures, filters.format, list.links)?;
|
||||
print_items(procedures, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !actions.is_empty() {
|
||||
fix_tags(&mut actions, &tags);
|
||||
print_items(actions, filters.format, list.links)?;
|
||||
print_items(actions, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !syncs.is_empty() {
|
||||
fix_tags(&mut syncs, &tags);
|
||||
print_items(syncs, filters.format, list.links)?;
|
||||
print_items(syncs, filters.format)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
@@ -168,7 +168,6 @@ async fn list_all(list: &args::list::List) -> anyhow::Result<()> {
|
||||
|
||||
async fn list_resources<T>(
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
T: ListResources,
|
||||
@@ -176,7 +175,7 @@ where
|
||||
{
|
||||
let client = crate::command::komodo_client().await?;
|
||||
let (mut resources, tags) = tokio::try_join!(
|
||||
T::list(client, filters, minimal),
|
||||
T::list(client, filters),
|
||||
client.read(ListTags::default()).map(|res| res.map(|res| res
|
||||
.into_iter()
|
||||
.map(|t| (t.id, t.name))
|
||||
@@ -184,7 +183,7 @@ where
|
||||
)?;
|
||||
fix_tags(&mut resources, &tags);
|
||||
if !resources.is_empty() {
|
||||
print_items(resources, filters.format, filters.links)?;
|
||||
print_items(resources, filters.format)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -227,7 +226,7 @@ async fn list_schedules(
|
||||
a.name.cmp(&b.name).then(a.enabled.cmp(&b.enabled))
|
||||
});
|
||||
if !schedules.is_empty() {
|
||||
print_items(schedules, filters.format, filters.links)?;
|
||||
print_items(schedules, filters.format)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -255,8 +254,6 @@ where
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
// For use with root `km ls`
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<ResourceListItem<Self::Info>>>;
|
||||
}
|
||||
|
||||
@@ -267,14 +264,12 @@ impl ListResources for ServerListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
_minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let servers = client
|
||||
.read(ListServers {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
.await?;
|
||||
@@ -287,8 +282,6 @@ impl ListResources for ServerListItem {
|
||||
true
|
||||
} else if filters.down {
|
||||
!matches!(server.info.state, ServerState::Ok)
|
||||
} else if filters.in_progress {
|
||||
false
|
||||
} else {
|
||||
matches!(server.info.state, ServerState::Ok)
|
||||
};
|
||||
@@ -310,7 +303,6 @@ impl ListResources for StackListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
_minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let (servers, mut stacks) = tokio::try_join!(
|
||||
client
|
||||
@@ -325,7 +317,6 @@ impl ListResources for StackListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
)?;
|
||||
@@ -346,17 +337,9 @@ impl ListResources for StackListItem {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
!matches!(
|
||||
stack.info.state,
|
||||
StackState::Running | StackState::Deploying
|
||||
)
|
||||
} else if filters.in_progress {
|
||||
matches!(stack.info.state, StackState::Deploying)
|
||||
!matches!(stack.info.state, StackState::Running)
|
||||
} else {
|
||||
matches!(
|
||||
stack.info.state,
|
||||
StackState::Running | StackState::Deploying
|
||||
)
|
||||
matches!(stack.info.state, StackState::Running)
|
||||
};
|
||||
state_check
|
||||
&& matches_wildcards(&names, &[stack.name.as_str()])
|
||||
@@ -382,7 +365,6 @@ impl ListResources for DeploymentListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
_minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let (servers, mut deployments) = tokio::try_join!(
|
||||
client
|
||||
@@ -397,7 +379,6 @@ impl ListResources for DeploymentListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
)?;
|
||||
@@ -419,17 +400,9 @@ impl ListResources for DeploymentListItem {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
!matches!(
|
||||
deployment.info.state,
|
||||
DeploymentState::Running | DeploymentState::Deploying
|
||||
)
|
||||
} else if filters.in_progress {
|
||||
matches!(deployment.info.state, DeploymentState::Deploying)
|
||||
!matches!(deployment.info.state, DeploymentState::Running)
|
||||
} else {
|
||||
matches!(
|
||||
deployment.info.state,
|
||||
DeploymentState::Running | DeploymentState::Deploying
|
||||
)
|
||||
matches!(deployment.info.state, DeploymentState::Running)
|
||||
};
|
||||
state_check
|
||||
&& matches_wildcards(&names, &[deployment.name.as_str()])
|
||||
@@ -455,7 +428,6 @@ impl ListResources for BuildListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let (builders, mut builds) = tokio::try_join!(
|
||||
client
|
||||
@@ -470,7 +442,6 @@ impl ListResources for BuildListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
)?;
|
||||
@@ -488,20 +459,7 @@ impl ListResources for BuildListItem {
|
||||
let mut builds = builds
|
||||
.into_iter()
|
||||
.filter(|build| {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
matches!(
|
||||
build.info.state,
|
||||
BuildState::Failed | BuildState::Unknown
|
||||
)
|
||||
} else if minimal || filters.in_progress {
|
||||
matches!(build.info.state, BuildState::Building)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
state_check
|
||||
&& matches_wildcards(&names, &[build.name.as_str()])
|
||||
matches_wildcards(&names, &[build.name.as_str()])
|
||||
&& matches_wildcards(
|
||||
&builders,
|
||||
&[build.info.builder_id.as_str()],
|
||||
@@ -523,7 +481,6 @@ impl ListResources for RepoListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
let mut repos = client
|
||||
@@ -531,30 +488,11 @@ impl ListResources for RepoListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|repo| {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
matches!(
|
||||
repo.info.state,
|
||||
RepoState::Failed | RepoState::Unknown
|
||||
)
|
||||
} else if minimal || filters.in_progress {
|
||||
matches!(
|
||||
repo.info.state,
|
||||
RepoState::Building | RepoState::Cloning
|
||||
)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
state_check
|
||||
&& matches_wildcards(&names, &[repo.name.as_str()])
|
||||
})
|
||||
.filter(|repo| matches_wildcards(&names, &[repo.name.as_str()]))
|
||||
.collect::<Vec<_>>();
|
||||
repos.sort_by(|a, b| {
|
||||
a.name
|
||||
@@ -571,7 +509,6 @@ impl ListResources for ProcedureListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
let mut procedures = client
|
||||
@@ -579,26 +516,12 @@ impl ListResources for ProcedureListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|procedure| {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
matches!(
|
||||
procedure.info.state,
|
||||
ProcedureState::Failed | ProcedureState::Unknown
|
||||
)
|
||||
} else if minimal || filters.in_progress {
|
||||
matches!(procedure.info.state, ProcedureState::Running)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
state_check
|
||||
&& matches_wildcards(&names, &[procedure.name.as_str()])
|
||||
matches_wildcards(&names, &[procedure.name.as_str()])
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
procedures.sort_by(|a, b| {
|
||||
@@ -619,7 +542,6 @@ impl ListResources for ActionListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
let mut actions = client
|
||||
@@ -627,26 +549,12 @@ impl ListResources for ActionListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|action| {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
matches!(
|
||||
action.info.state,
|
||||
ActionState::Failed | ActionState::Unknown
|
||||
)
|
||||
} else if minimal || filters.in_progress {
|
||||
matches!(action.info.state, ActionState::Running)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
state_check
|
||||
&& matches_wildcards(&names, &[action.name.as_str()])
|
||||
matches_wildcards(&names, &[action.name.as_str()])
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
actions.sort_by(|a, b| {
|
||||
@@ -667,7 +575,6 @@ impl ListResources for ResourceSyncListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
let mut syncs = client
|
||||
@@ -675,30 +582,11 @@ impl ListResources for ResourceSyncListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|sync| {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
matches!(
|
||||
sync.info.state,
|
||||
ResourceSyncState::Failed | ResourceSyncState::Unknown
|
||||
)
|
||||
} else if minimal || filters.in_progress {
|
||||
matches!(
|
||||
sync.info.state,
|
||||
ResourceSyncState::Syncing | ResourceSyncState::Pending
|
||||
)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
state_check
|
||||
&& matches_wildcards(&names, &[sync.name.as_str()])
|
||||
})
|
||||
.filter(|sync| matches_wildcards(&names, &[sync.name.as_str()]))
|
||||
.collect::<Vec<_>>();
|
||||
syncs.sort_by(|a, b| {
|
||||
a.name.cmp(&b.name).then(a.info.state.cmp(&b.info.state))
|
||||
@@ -712,7 +600,6 @@ impl ListResources for BuilderListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
let mut builders = client
|
||||
@@ -720,14 +607,12 @@ impl ListResources for BuilderListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|builder| {
|
||||
(!minimal || filters.all)
|
||||
&& matches_wildcards(&names, &[builder.name.as_str()])
|
||||
matches_wildcards(&names, &[builder.name.as_str()])
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
builders.sort_by(|a, b| {
|
||||
@@ -744,7 +629,6 @@ impl ListResources for AlerterListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
let mut syncs = client
|
||||
@@ -752,15 +636,11 @@ impl ListResources for AlerterListItem {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|sync| {
|
||||
(!minimal || filters.all)
|
||||
&& matches_wildcards(&names, &[sync.name.as_str()])
|
||||
})
|
||||
.filter(|sync| matches_wildcards(&names, &[sync.name.as_str()]))
|
||||
.collect::<Vec<_>>();
|
||||
syncs.sort_by(|a, b| {
|
||||
a.info
|
||||
@@ -776,51 +656,39 @@ impl ListResources for AlerterListItem {
|
||||
// TABLE
|
||||
|
||||
impl PrintTable for ResourceListItem<ServerListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Server", "State", "Address", "Tags", "Link"]
|
||||
} else {
|
||||
&["Server", "State", "Address", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Server", "State", "Address", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<Cell> {
|
||||
fn row(self) -> Vec<Cell> {
|
||||
let color = match self.info.state {
|
||||
ServerState::Ok => Color::Green,
|
||||
ServerState::NotOk => Color::Red,
|
||||
ServerState::Disabled => Color::Blue,
|
||||
};
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.address),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Server,
|
||||
&self.id,
|
||||
)))
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<StackListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Stack", "State", "Server", "Tags", "Link"]
|
||||
} else {
|
||||
&["Stack", "State", "Server", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Stack", "State", "Server", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let color = match self.info.state {
|
||||
StackState::Down => Color::Blue,
|
||||
StackState::Running => Color::Green,
|
||||
StackState::Deploying => Color::DarkYellow,
|
||||
StackState::Paused => Color::DarkYellow,
|
||||
StackState::Unknown => Color::Magenta,
|
||||
_ => Color::Red,
|
||||
@@ -832,7 +700,7 @@ impl PrintTable for ResourceListItem<StackListItemInfo> {
|
||||
// } else {
|
||||
// "UI Defined"
|
||||
// };
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
@@ -840,97 +708,75 @@ impl PrintTable for ResourceListItem<StackListItemInfo> {
|
||||
Cell::new(self.info.server_id),
|
||||
// Cell::new(source),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Stack,
|
||||
&self.id,
|
||||
)))
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<DeploymentListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Deployment", "State", "Server", "Tags", "Link"]
|
||||
} else {
|
||||
&["Deployment", "State", "Server", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Deployment", "State", "Server", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let color = match self.info.state {
|
||||
DeploymentState::NotDeployed => Color::Blue,
|
||||
DeploymentState::Running => Color::Green,
|
||||
DeploymentState::Deploying => Color::DarkYellow,
|
||||
DeploymentState::Paused => Color::DarkYellow,
|
||||
DeploymentState::Unknown => Color::Magenta,
|
||||
_ => Color::Red,
|
||||
};
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.server_id),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Deployment,
|
||||
&self.id,
|
||||
)))
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<BuildListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Build", "State", "Builder", "Tags", "Link"]
|
||||
} else {
|
||||
&["Build", "State", "Builder", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Build", "State", "Builder", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let color = match self.info.state {
|
||||
BuildState::Ok => Color::Green,
|
||||
BuildState::Building => Color::DarkYellow,
|
||||
BuildState::Unknown => Color::Magenta,
|
||||
BuildState::Failed => Color::Red,
|
||||
};
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.builder_id),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Build,
|
||||
&self.id,
|
||||
)));
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<RepoListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Repo", "State", "Link", "Tags", "Link"]
|
||||
} else {
|
||||
&["Repo", "State", "Link", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Repo", "State", "Link", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let color = match self.info.state {
|
||||
RepoState::Ok => Color::Green,
|
||||
RepoState::Building
|
||||
@@ -939,34 +785,27 @@ impl PrintTable for ResourceListItem<RepoListItemInfo> {
|
||||
RepoState::Unknown => Color::Magenta,
|
||||
RepoState::Failed => Color::Red,
|
||||
};
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.repo_link),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Repo,
|
||||
&self.id,
|
||||
)))
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<ProcedureListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Procedure", "State", "Next Run", "Tags", "Link"]
|
||||
} else {
|
||||
&["Procedure", "State", "Next Run", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Procedure", "State", "Next Run", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let color = match self.info.state {
|
||||
ProcedureState::Ok => Color::Green,
|
||||
ProcedureState::Running => Color::DarkYellow,
|
||||
@@ -982,34 +821,27 @@ impl PrintTable for ResourceListItem<ProcedureListItemInfo> {
|
||||
} else {
|
||||
Cell::new(String::from("None"))
|
||||
};
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
next_run,
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Procedure,
|
||||
&self.id,
|
||||
)))
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<ActionListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Action", "State", "Next Run", "Tags", "Link"]
|
||||
} else {
|
||||
&["Action", "State", "Next Run", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Action", "State", "Next Run", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let color = match self.info.state {
|
||||
ActionState::Ok => Color::Green,
|
||||
ActionState::Running => Color::DarkYellow,
|
||||
@@ -1025,34 +857,27 @@ impl PrintTable for ResourceListItem<ActionListItemInfo> {
|
||||
} else {
|
||||
Cell::new(String::from("None"))
|
||||
};
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
next_run,
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Action,
|
||||
&self.id,
|
||||
)));
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<ResourceSyncListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Sync", "State", "Tags", "Link"]
|
||||
} else {
|
||||
&["Sync", "State", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Sync", "State", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let color = match self.info.state {
|
||||
ResourceSyncState::Ok => Color::Green,
|
||||
ResourceSyncState::Pending | ResourceSyncState::Syncing => {
|
||||
@@ -1061,59 +886,45 @@ impl PrintTable for ResourceListItem<ResourceSyncListItemInfo> {
|
||||
ResourceSyncState::Unknown => Color::Magenta,
|
||||
ResourceSyncState::Failed => Color::Red,
|
||||
};
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::ResourceSync,
|
||||
&self.id,
|
||||
)))
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<BuilderListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Builder", "Type", "Tags", "Link"]
|
||||
} else {
|
||||
&["Builder", "Type", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Builder", "Type", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
let mut res = vec![
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.builder_type),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Builder,
|
||||
&self.id,
|
||||
)));
|
||||
}
|
||||
res
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<AlerterListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Alerter", "Type", "Enabled", "Tags", "Link"]
|
||||
} else {
|
||||
&["Alerter", "Type", "Enabled", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Alerter", "Type", "Enabled", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
let mut row = vec![
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.endpoint_type),
|
||||
if self.info.enabled {
|
||||
@@ -1122,27 +933,20 @@ impl PrintTable for ResourceListItem<AlerterListItemInfo> {
|
||||
Cell::new(self.info.enabled.to_string()).fg(Color::Red)
|
||||
},
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
row.push(Cell::new(resource_link(
|
||||
Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Alerter,
|
||||
&self.id,
|
||||
)));
|
||||
}
|
||||
row
|
||||
)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for Schedule {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Name", "Type", "Next Run", "Tags", "Link"]
|
||||
} else {
|
||||
&["Name", "Type", "Next Run", "Tags"]
|
||||
}
|
||||
fn header() -> &'static [&'static str] {
|
||||
&["Name", "Type", "Next Run", "Tags", "Link"]
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<comfy_table::Cell> {
|
||||
fn row(self) -> Vec<comfy_table::Cell> {
|
||||
let next_run = if let Some(ts) = self.next_scheduled_run {
|
||||
Cell::new(
|
||||
format_timetamp(ts)
|
||||
@@ -1153,19 +957,12 @@ impl PrintTable for Schedule {
|
||||
Cell::new(String::from("None"))
|
||||
};
|
||||
let (resource_type, id) = self.target.extract_variant_id();
|
||||
let mut res = vec![
|
||||
vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.target.extract_variant_id().0),
|
||||
next_run,
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
resource_type,
|
||||
id,
|
||||
)));
|
||||
}
|
||||
res
|
||||
Cell::new(resource_link(&cli_config().host, resource_type, id)),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+11
-31
@@ -5,8 +5,7 @@ use chrono::TimeZone;
|
||||
use colored::Colorize;
|
||||
use comfy_table::{Attribute, Cell, Table};
|
||||
use komodo_client::{
|
||||
KomodoClient,
|
||||
entities::config::cli::{CliTableBorders, args::CliFormat},
|
||||
KomodoClient, entities::config::cli::args::CliFormat,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use tokio::sync::OnceCell;
|
||||
@@ -92,30 +91,19 @@ fn sanitize_uri(uri: &str) -> String {
|
||||
fn print_items<T: PrintTable + Serialize>(
|
||||
items: Vec<T>,
|
||||
format: CliFormat,
|
||||
links: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
match format {
|
||||
CliFormat::Table => {
|
||||
let mut table = Table::new();
|
||||
let preset = {
|
||||
use comfy_table::presets::*;
|
||||
match cli_config().table_borders {
|
||||
None | Some(CliTableBorders::Horizontal) => {
|
||||
UTF8_HORIZONTAL_ONLY
|
||||
}
|
||||
Some(CliTableBorders::Vertical) => UTF8_FULL_CONDENSED,
|
||||
Some(CliTableBorders::Inside) => UTF8_NO_BORDERS,
|
||||
Some(CliTableBorders::Outside) => UTF8_BORDERS_ONLY,
|
||||
Some(CliTableBorders::All) => UTF8_FULL,
|
||||
}
|
||||
};
|
||||
table.load_preset(preset).set_header(
|
||||
T::header(links)
|
||||
.into_iter()
|
||||
.map(|h| Cell::new(h).add_attribute(Attribute::Bold)),
|
||||
);
|
||||
table
|
||||
.load_preset(comfy_table::presets::UTF8_FULL)
|
||||
.set_header(
|
||||
T::header()
|
||||
.into_iter()
|
||||
.map(|h| Cell::new(h).add_attribute(Attribute::Bold)),
|
||||
);
|
||||
for item in items {
|
||||
table.add_row(item.row(links));
|
||||
table.add_row(item.row());
|
||||
}
|
||||
println!("{table}");
|
||||
}
|
||||
@@ -131,8 +119,8 @@ fn print_items<T: PrintTable + Serialize>(
|
||||
}
|
||||
|
||||
trait PrintTable {
|
||||
fn header(links: bool) -> &'static [&'static str];
|
||||
fn row(self, links: bool) -> Vec<Cell>;
|
||||
fn header() -> &'static [&'static str];
|
||||
fn row(self) -> Vec<Cell>;
|
||||
}
|
||||
|
||||
fn parse_wildcards(items: &[String]) -> Vec<Wildcard<'_>> {
|
||||
@@ -168,14 +156,6 @@ fn format_timetamp(ts: i64) -> anyhow::Result<String> {
|
||||
Ok(ts)
|
||||
}
|
||||
|
||||
fn clamp_sha(maybe_sha: &str) -> String {
|
||||
if maybe_sha.starts_with("sha256:") {
|
||||
maybe_sha[0..20].to_string() + "..."
|
||||
} else {
|
||||
maybe_sha.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// fn text_link(link: &str, text: &str) -> String {
|
||||
// format!("\x1b]8;;{link}\x07{text}\x1b]8;;\x07")
|
||||
// }
|
||||
|
||||
@@ -191,9 +191,6 @@ pub fn cli_config() -> &'static CliConfig {
|
||||
config_profile,
|
||||
config_aliases: config.config_aliases,
|
||||
default_profile: config.default_profile,
|
||||
table_borders: env
|
||||
.komodo_cli_table_borders
|
||||
.or(config.table_borders),
|
||||
host: host
|
||||
.or(env.komodo_cli_host)
|
||||
.or(env.komodo_host)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
# Build Core
|
||||
FROM rust:1.89.0-bullseye AS core-builder
|
||||
FROM rust:1.88.0-bullseye AS core-builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
|
||||
@@ -25,7 +25,6 @@ use crate::{
|
||||
|
||||
use super::Variant;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AuthArgs {
|
||||
pub headers: HeaderMap,
|
||||
}
|
||||
@@ -42,7 +41,7 @@ pub struct AuthArgs {
|
||||
#[allow(clippy::enum_variant_names, clippy::large_enum_variant)]
|
||||
pub enum AuthRequest {
|
||||
GetLoginOptions(GetLoginOptions),
|
||||
SignUpLocalUser(SignUpLocalUser),
|
||||
CreateLocalUser(CreateLocalUser),
|
||||
LoginLocalUser(LoginLocalUser),
|
||||
ExchangeForJwt(ExchangeForJwt),
|
||||
GetUser(GetUser),
|
||||
@@ -139,10 +138,8 @@ impl Resolve<AuthArgs> for ExchangeForJwt {
|
||||
self,
|
||||
_: &AuthArgs,
|
||||
) -> serror::Result<ExchangeForJwtResponse> {
|
||||
jwt_client()
|
||||
.redeem_exchange_token(&self.token)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
let jwt = jwt_client().redeem_exchange_token(&self.token).await?;
|
||||
Ok(ExchangeForJwtResponse { jwt })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,319 +0,0 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use command::run_komodo_command;
|
||||
use database::mungos::{find::find_collect, mongodb::bson::doc};
|
||||
use formatting::{bold, format_serror};
|
||||
use komodo_client::{
|
||||
api::execute::{
|
||||
BackupCoreDatabase, ClearRepoCache, GlobalAutoUpdate,
|
||||
},
|
||||
entities::{
|
||||
deployment::DeploymentState, server::ServerState,
|
||||
stack::StackState,
|
||||
},
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
api::execute::{
|
||||
ExecuteArgs, pull_deployment_inner, pull_stack_inner,
|
||||
},
|
||||
config::core_config,
|
||||
helpers::update::update_update,
|
||||
state::{
|
||||
db_client, deployment_status_cache, server_status_cache,
|
||||
stack_status_cache,
|
||||
},
|
||||
};
|
||||
|
||||
/// Makes sure the method can only be called once at a time
|
||||
fn clear_repo_cache_lock() -> &'static Mutex<()> {
|
||||
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
LOCK.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for ClearRepoCache {
|
||||
#[instrument(
|
||||
name = "ClearRepoCache",
|
||||
skip(user, update),
|
||||
fields(user_id = user.id, update_id = update.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("This method is admin only.")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
}
|
||||
|
||||
let _lock = clear_repo_cache_lock()
|
||||
.try_lock()
|
||||
.context("Clear already in progress...")?;
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
let mut contents =
|
||||
tokio::fs::read_dir(&core_config().repo_directory)
|
||||
.await
|
||||
.context("Failed to read repo cache directory")?;
|
||||
|
||||
loop {
|
||||
let path = match contents
|
||||
.next_entry()
|
||||
.await
|
||||
.context("Failed to read contents at path")
|
||||
{
|
||||
Ok(Some(contents)) => contents.path(),
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
update.push_error_log(
|
||||
"Read Directory",
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if path.is_dir() {
|
||||
match tokio::fs::remove_dir_all(&path)
|
||||
.await
|
||||
.context("Failed to clear contents at path")
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
update.push_error_log(
|
||||
"Clear Directory",
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/// Makes sure the method can only be called once at a time
|
||||
fn backup_database_lock() -> &'static Mutex<()> {
|
||||
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
LOCK.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for BackupCoreDatabase {
|
||||
#[instrument(
|
||||
name = "BackupCoreDatabase",
|
||||
skip(user, update),
|
||||
fields(user_id = user.id, update_id = update.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("This method is admin only.")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
}
|
||||
|
||||
let _lock = backup_database_lock()
|
||||
.try_lock()
|
||||
.context("Backup already in progress...")?;
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let res = run_komodo_command(
|
||||
"Backup Core Database",
|
||||
None,
|
||||
"km database backup --yes",
|
||||
)
|
||||
.await;
|
||||
|
||||
update.logs.push(res);
|
||||
update.finalize();
|
||||
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/// Makes sure the method can only be called once at a time
|
||||
fn global_update_lock() -> &'static Mutex<()> {
|
||||
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
LOCK.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
#[instrument(
|
||||
name = "GlobalAutoUpdate",
|
||||
skip(user, update),
|
||||
fields(user_id = user.id, update_id = update.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("This method is admin only.")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
}
|
||||
|
||||
let _lock = global_update_lock()
|
||||
.try_lock()
|
||||
.context("Global update already in progress...")?;
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
// This is all done in sequence because there is no rush,
|
||||
// the pulls / deploys happen spaced out to ease the load on system.
|
||||
let servers = find_collect(&db_client().servers, None, None)
|
||||
.await
|
||||
.context("Failed to query for servers from database")?;
|
||||
|
||||
let query = doc! {
|
||||
"$or": [
|
||||
{ "config.poll_for_updates": true },
|
||||
{ "config.auto_update": true }
|
||||
]
|
||||
};
|
||||
|
||||
let (stacks, repos) = tokio::try_join!(
|
||||
find_collect(&db_client().stacks, query.clone(), None),
|
||||
find_collect(&db_client().repos, None, None)
|
||||
)
|
||||
.context("Failed to query for resources from database")?;
|
||||
|
||||
let server_status_cache = server_status_cache();
|
||||
let stack_status_cache = stack_status_cache();
|
||||
|
||||
// Will be edited later at update.logs[0]
|
||||
update.push_simple_log("Auto Pull", String::new());
|
||||
|
||||
for stack in stacks {
|
||||
let Some(status) = stack_status_cache.get(&stack.id).await
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
// Only pull running stacks.
|
||||
if !matches!(status.curr.state, StackState::Running) {
|
||||
continue;
|
||||
}
|
||||
if let Some(server) =
|
||||
servers.iter().find(|s| s.id == stack.config.server_id)
|
||||
// This check is probably redundant along with running check
|
||||
// but shouldn't hurt
|
||||
&& server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
let name = stack.name.clone();
|
||||
let repo = if stack.config.linked_repo.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let Some(repo) =
|
||||
repos.iter().find(|r| r.id == stack.config.linked_repo)
|
||||
else {
|
||||
update.push_error_log(
|
||||
&format!("Pull Stack {name}"),
|
||||
format!(
|
||||
"Did not find any Repo matching {}",
|
||||
stack.config.linked_repo
|
||||
),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
Some(repo.clone())
|
||||
};
|
||||
if let Err(e) =
|
||||
pull_stack_inner(stack, Vec::new(), server, repo, None)
|
||||
.await
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Pull Stack {name}"),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
}
|
||||
update.logs[0]
|
||||
.stdout
|
||||
.push_str(&format!("Pulled Stack {} ✅", bold(name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let deployment_status_cache = deployment_status_cache();
|
||||
let deployments =
|
||||
find_collect(&db_client().deployments, query, None)
|
||||
.await
|
||||
.context("Failed to query for deployments from database")?;
|
||||
for deployment in deployments {
|
||||
let Some(status) =
|
||||
deployment_status_cache.get(&deployment.id).await
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
// Only pull running deployments.
|
||||
if !matches!(status.curr.state, DeploymentState::Running) {
|
||||
continue;
|
||||
}
|
||||
if let Some(server) =
|
||||
servers.iter().find(|s| s.id == deployment.config.server_id)
|
||||
// This check is probably redundant along with running check
|
||||
// but shouldn't hurt
|
||||
&& server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
let name = deployment.name.clone();
|
||||
if let Err(e) =
|
||||
pull_deployment_inner(deployment, server).await
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Pull Deployment {name}"),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
}
|
||||
update.logs[0].stdout.push_str(&format!(
|
||||
"Pulled Deployment {} ✅",
|
||||
bold(name)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{pin::Pin, time::Instant};
|
||||
use std::{pin::Pin, sync::OnceLock, time::Instant};
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{Context, anyhow};
|
||||
use axum::{
|
||||
Extension, Router, extract::Path, middleware, routing::post,
|
||||
};
|
||||
@@ -18,16 +18,19 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use response::JsonString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use serror::Json;
|
||||
use serror::{AddStatusCodeError, Json};
|
||||
use tokio::sync::Mutex;
|
||||
use typeshare::typeshare;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
auth::auth_request,
|
||||
config::core_config,
|
||||
helpers::update::{init_execution_update, update_update},
|
||||
resource::{KomodoResource, list_full_for_user_using_pattern},
|
||||
state::db_client,
|
||||
@@ -37,7 +40,6 @@ mod action;
|
||||
mod alerter;
|
||||
mod build;
|
||||
mod deployment;
|
||||
mod maintenance;
|
||||
mod procedure;
|
||||
mod repo;
|
||||
mod server;
|
||||
@@ -145,8 +147,6 @@ pub enum ExecuteRequest {
|
||||
|
||||
// ==== MAINTENANCE ====
|
||||
ClearRepoCache(ClearRepoCache),
|
||||
BackupCoreDatabase(BackupCoreDatabase),
|
||||
GlobalAutoUpdate(GlobalAutoUpdate),
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
@@ -334,3 +334,71 @@ async fn batch_execute<E: BatchExecute>(
|
||||
});
|
||||
Ok(join_all(futures).await)
|
||||
}
|
||||
|
||||
fn clear_repo_cache_lock() -> &'static Mutex<()> {
|
||||
static CLEAR_REPO_CACHE_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
CLEAR_REPO_CACHE_LOCK.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for ClearRepoCache {
|
||||
#[instrument(name = "ClearRepoCache", skip(user, update), fields(user_id = user.id, update_id = update.id))]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, update }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("This method is admin only.")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
}
|
||||
|
||||
let _lock = clear_repo_cache_lock()
|
||||
.try_lock()
|
||||
.context("Clear already in progress...")?;
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
let mut contents =
|
||||
tokio::fs::read_dir(&core_config().repo_directory)
|
||||
.await
|
||||
.context("Failed to read repo cache directory")?;
|
||||
|
||||
loop {
|
||||
let path = match contents
|
||||
.next_entry()
|
||||
.await
|
||||
.context("Failed to read contents at path")
|
||||
{
|
||||
Ok(Some(contents)) => contents.path(),
|
||||
Ok(None) => break,
|
||||
Err(e) => {
|
||||
update.push_error_log(
|
||||
"Read Directory",
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if path.is_dir() {
|
||||
match tokio::fs::remove_dir_all(&path)
|
||||
.await
|
||||
.context("Failed to clear contents at path")
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
update.push_error_log(
|
||||
"Clear Directory",
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ pub struct WriteArgs {
|
||||
#[serde(tag = "type", content = "params")]
|
||||
pub enum WriteRequest {
|
||||
// ==== USER ====
|
||||
CreateLocalUser(CreateLocalUser),
|
||||
UpdateUserUsername(UpdateUserUsername),
|
||||
UpdateUserPassword(UpdateUserPassword),
|
||||
DeleteUser(DeleteUser),
|
||||
|
||||
@@ -27,6 +27,7 @@ use periphery_client::api::compose::{
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
api::execute::pull_stack_inner,
|
||||
config::core_config,
|
||||
helpers::{
|
||||
periphery_client,
|
||||
@@ -555,6 +556,24 @@ impl Resolve<WriteArgs> for RefreshStackCache {
|
||||
.await
|
||||
.context("failed to update stack info on db")?;
|
||||
|
||||
if (stack.config.poll_for_updates || stack.config.auto_update)
|
||||
&& !stack.config.server_id.is_empty()
|
||||
{
|
||||
let (server, state) =
|
||||
get_server_with_state(&stack.config.server_id).await?;
|
||||
if state == ServerState::Ok {
|
||||
let name = stack.name.clone();
|
||||
if let Err(e) =
|
||||
pull_stack_inner(stack, Vec::new(), &server, repo, None)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Failed to pull latest images for Stack {name} | {e:#}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,17 @@ use database::mungos::{
|
||||
use komodo_client::{
|
||||
api::write::{CreateTag, DeleteTag, RenameTag, UpdateTagColor},
|
||||
entities::{
|
||||
action::Action, alerter::Alerter, build::Build, builder::Builder,
|
||||
deployment::Deployment, procedure::Procedure, repo::Repo,
|
||||
server::Server, stack::Stack, sync::ResourceSync, tag::Tag,
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::Builder,
|
||||
deployment::Deployment,
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
sync::ResourceSync,
|
||||
tag::{Tag, TagColor},
|
||||
},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
@@ -36,8 +44,9 @@ impl Resolve<WriteArgs> for CreateTag {
|
||||
let mut tag = Tag {
|
||||
id: Default::default(),
|
||||
name: self.name,
|
||||
color: self.color.unwrap_or_default(),
|
||||
color: TagColor::Slate,
|
||||
owner: user.id.clone(),
|
||||
unused: false,
|
||||
};
|
||||
|
||||
tag.id = db_client()
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use async_timing_util::unix_timestamp_ms;
|
||||
use database::{
|
||||
hash_password,
|
||||
mungos::mongodb::bson::{doc, oid::ObjectId},
|
||||
};
|
||||
use database::mungos::mongodb::bson::{doc, oid::ObjectId};
|
||||
use komodo_client::{
|
||||
api::write::*,
|
||||
entities::{
|
||||
NoData,
|
||||
user::{User, UserConfig},
|
||||
api::write::{
|
||||
DeleteUser, DeleteUserResponse, UpdateUserPassword,
|
||||
UpdateUserPasswordResponse, UpdateUserUsername,
|
||||
UpdateUserUsernameResponse,
|
||||
},
|
||||
entities::NoData,
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
|
||||
use crate::{config::core_config, state::db_client};
|
||||
|
||||
@@ -23,85 +18,7 @@ use super::WriteArgs;
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for CreateLocalUser {
|
||||
#[instrument(name = "CreateLocalUser", skip(admin, self), fields(admin_id = admin.id, username = self.username))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> serror::Result<CreateLocalUserResponse> {
|
||||
if !admin.admin {
|
||||
return Err(
|
||||
anyhow!("This method is admin-only.")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
}
|
||||
|
||||
if self.username.is_empty() {
|
||||
return Err(anyhow!("Username cannot be empty.").into());
|
||||
}
|
||||
|
||||
if ObjectId::from_str(&self.username).is_ok() {
|
||||
return Err(
|
||||
anyhow!("Username cannot be valid ObjectId").into(),
|
||||
);
|
||||
}
|
||||
|
||||
if self.password.is_empty() {
|
||||
return Err(anyhow!("Password cannot be empty.").into());
|
||||
}
|
||||
|
||||
let db = db_client();
|
||||
|
||||
if db
|
||||
.users
|
||||
.find_one(doc! { "username": &self.username })
|
||||
.await
|
||||
.context("Failed to query for existing users")?
|
||||
.is_some()
|
||||
{
|
||||
return Err(anyhow!("Username already taken.").into());
|
||||
}
|
||||
|
||||
let ts = unix_timestamp_ms() as i64;
|
||||
let hashed_password = hash_password(self.password)?;
|
||||
|
||||
let mut user = User {
|
||||
id: Default::default(),
|
||||
username: self.username,
|
||||
enabled: true,
|
||||
admin: false,
|
||||
super_admin: false,
|
||||
create_server_permissions: false,
|
||||
create_build_permissions: false,
|
||||
updated_at: ts,
|
||||
last_update_view: 0,
|
||||
recents: Default::default(),
|
||||
all: Default::default(),
|
||||
config: UserConfig::Local {
|
||||
password: hashed_password,
|
||||
},
|
||||
};
|
||||
|
||||
user.id = db_client()
|
||||
.users
|
||||
.insert_one(&user)
|
||||
.await
|
||||
.context("failed to create user")?
|
||||
.inserted_id
|
||||
.as_object_id()
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
|
||||
user.sanitize();
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for UpdateUserUsername {
|
||||
#[instrument(name = "UpdateUserUsername", skip(user), fields(user_id = user.id))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
@@ -119,13 +36,6 @@ impl Resolve<WriteArgs> for UpdateUserUsername {
|
||||
if self.username.is_empty() {
|
||||
return Err(anyhow!("Username cannot be empty.").into());
|
||||
}
|
||||
|
||||
if ObjectId::from_str(&self.username).is_ok() {
|
||||
return Err(
|
||||
anyhow!("Username cannot be valid ObjectId").into(),
|
||||
);
|
||||
}
|
||||
|
||||
let db = db_client();
|
||||
if db
|
||||
.users
|
||||
@@ -152,7 +62,6 @@ impl Resolve<WriteArgs> for UpdateUserUsername {
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for UpdateUserPassword {
|
||||
#[instrument(name = "UpdateUserPassword", skip(user, self), fields(user_id = user.id))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
@@ -175,16 +84,12 @@ impl Resolve<WriteArgs> for UpdateUserPassword {
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for DeleteUser {
|
||||
#[instrument(name = "DeleteUser", skip(admin), fields(user = self.user))]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> serror::Result<DeleteUserResponse> {
|
||||
if !admin.admin {
|
||||
return Err(
|
||||
anyhow!("This method is admin-only.")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
return Err(anyhow!("Calling user is not admin.").into());
|
||||
}
|
||||
if admin.username == self.user || admin.id == self.user {
|
||||
return Err(anyhow!("User cannot delete themselves.").into());
|
||||
|
||||
@@ -8,15 +8,13 @@ use database::mungos::mongodb::bson::doc;
|
||||
use jsonwebtoken::{
|
||||
DecodingKey, EncodingKey, Header, Validation, decode, encode,
|
||||
};
|
||||
use komodo_client::{
|
||||
api::auth::JwtResponse, entities::config::core::CoreConfig,
|
||||
};
|
||||
use komodo_client::entities::config::core::CoreConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::helpers::random_string;
|
||||
|
||||
type ExchangeTokenMap = Mutex<HashMap<String, (JwtResponse, u128)>>;
|
||||
type ExchangeTokenMap = Mutex<HashMap<String, (String, u128)>>;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct JwtClaims {
|
||||
@@ -53,20 +51,16 @@ impl JwtClient {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode(
|
||||
&self,
|
||||
user_id: String,
|
||||
) -> anyhow::Result<JwtResponse> {
|
||||
pub fn encode(&self, user_id: String) -> anyhow::Result<String> {
|
||||
let iat = unix_timestamp_ms();
|
||||
let exp = iat + self.ttl_ms;
|
||||
let claims = JwtClaims {
|
||||
id: user_id.clone(),
|
||||
id: user_id,
|
||||
iat,
|
||||
exp,
|
||||
};
|
||||
let jwt = encode(&self.header, &claims, &self.encoding_key)
|
||||
.context("failed at signing claim")?;
|
||||
Ok(JwtResponse { user_id, jwt })
|
||||
encode(&self.header, &claims, &self.encoding_key)
|
||||
.context("failed at signing claim")
|
||||
}
|
||||
|
||||
pub fn decode(&self, jwt: &str) -> anyhow::Result<JwtClaims> {
|
||||
@@ -76,10 +70,7 @@ impl JwtClient {
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn create_exchange_token(
|
||||
&self,
|
||||
jwt: JwtResponse,
|
||||
) -> String {
|
||||
pub async fn create_exchange_token(&self, jwt: String) -> String {
|
||||
let exchange_token = random_string(40);
|
||||
self.exchange_tokens.lock().await.insert(
|
||||
exchange_token.clone(),
|
||||
@@ -95,7 +86,7 @@ impl JwtClient {
|
||||
pub async fn redeem_exchange_token(
|
||||
&self,
|
||||
exchange_token: &str,
|
||||
) -> anyhow::Result<JwtResponse> {
|
||||
) -> anyhow::Result<String> {
|
||||
let (jwt, valid_until) = self
|
||||
.exchange_tokens
|
||||
.lock()
|
||||
|
||||
+17
-26
@@ -8,8 +8,8 @@ use database::{
|
||||
};
|
||||
use komodo_client::{
|
||||
api::auth::{
|
||||
LoginLocalUser, LoginLocalUserResponse, SignUpLocalUser,
|
||||
SignUpLocalUserResponse,
|
||||
CreateLocalUser, CreateLocalUserResponse, LoginLocalUser,
|
||||
LoginLocalUserResponse,
|
||||
},
|
||||
entities::user::{User, UserConfig},
|
||||
};
|
||||
@@ -21,12 +21,12 @@ use crate::{
|
||||
state::{db_client, jwt_client},
|
||||
};
|
||||
|
||||
impl Resolve<AuthArgs> for SignUpLocalUser {
|
||||
#[instrument(name = "SignUpLocalUser", skip(self))]
|
||||
impl Resolve<AuthArgs> for CreateLocalUser {
|
||||
#[instrument(name = "CreateLocalUser", skip(self))]
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &AuthArgs,
|
||||
) -> serror::Result<SignUpLocalUserResponse> {
|
||||
) -> serror::Result<CreateLocalUserResponse> {
|
||||
let core_config = core_config();
|
||||
|
||||
if !core_config.local_auth {
|
||||
@@ -47,27 +47,16 @@ impl Resolve<AuthArgs> for SignUpLocalUser {
|
||||
return Err(anyhow!("Password cannot be empty string").into());
|
||||
}
|
||||
|
||||
let db = db_client();
|
||||
let hashed_password = hash_password(self.password)?;
|
||||
|
||||
let no_users_exist =
|
||||
db.users.find_one(Document::new()).await?.is_none();
|
||||
db_client().users.find_one(Document::new()).await?.is_none();
|
||||
|
||||
if !no_users_exist && core_config.disable_user_registration {
|
||||
return Err(anyhow!("User registration is disabled").into());
|
||||
}
|
||||
|
||||
if db
|
||||
.users
|
||||
.find_one(doc! { "username": &self.username })
|
||||
.await
|
||||
.context("Failed to query for existing users")?
|
||||
.is_some()
|
||||
{
|
||||
return Err(anyhow!("Username already taken.").into());
|
||||
}
|
||||
|
||||
let ts = unix_timestamp_ms() as i64;
|
||||
let hashed_password = hash_password(self.password)?;
|
||||
|
||||
let user = User {
|
||||
id: Default::default(),
|
||||
@@ -96,10 +85,11 @@ impl Resolve<AuthArgs> for SignUpLocalUser {
|
||||
.context("inserted_id is not ObjectId")?
|
||||
.to_string();
|
||||
|
||||
jwt_client()
|
||||
.encode(user_id.clone())
|
||||
.context("failed to generate jwt for user")
|
||||
.map_err(Into::into)
|
||||
let jwt = jwt_client()
|
||||
.encode(user_id)
|
||||
.context("failed to generate jwt for user")?;
|
||||
|
||||
Ok(CreateLocalUserResponse { jwt })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,9 +131,10 @@ impl Resolve<AuthArgs> for LoginLocalUser {
|
||||
return Err(anyhow!("invalid credentials").into());
|
||||
}
|
||||
|
||||
jwt_client()
|
||||
.encode(user.id.clone())
|
||||
.context("failed at generating jwt for user")
|
||||
.map_err(Into::into)
|
||||
let jwt = jwt_client()
|
||||
.encode(user.id)
|
||||
.context("failed at generating jwt for user")?;
|
||||
|
||||
Ok(LoginLocalUserResponse { jwt })
|
||||
}
|
||||
}
|
||||
|
||||
+1
-12
@@ -109,14 +109,6 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
.komodo_database_db_name
|
||||
.unwrap_or(config.database.db_name),
|
||||
},
|
||||
init_admin_username: maybe_read_item_from_file(
|
||||
env.komodo_init_admin_username_file,
|
||||
env.komodo_init_admin_username
|
||||
).or(config.init_admin_username),
|
||||
init_admin_password: maybe_read_item_from_file(
|
||||
env.komodo_init_admin_password_file,
|
||||
env.komodo_init_admin_password
|
||||
).unwrap_or(config.init_admin_password),
|
||||
oidc_enabled: env.komodo_oidc_enabled.unwrap_or(config.oidc_enabled),
|
||||
oidc_provider: env.komodo_oidc_provider.unwrap_or(config.oidc_provider),
|
||||
oidc_redirect_host: env.komodo_oidc_redirect_host.unwrap_or(config.oidc_redirect_host),
|
||||
@@ -177,8 +169,7 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
port: env.komodo_port.unwrap_or(config.port),
|
||||
bind_ip: env.komodo_bind_ip.unwrap_or(config.bind_ip),
|
||||
timezone: env.komodo_timezone.unwrap_or(config.timezone),
|
||||
first_server: env.komodo_first_server.or(config.first_server),
|
||||
first_server_name: env.komodo_first_server_name.unwrap_or(config.first_server_name),
|
||||
first_server: env.komodo_first_server.unwrap_or(config.first_server),
|
||||
frontend_path: env.komodo_frontend_path.unwrap_or(config.frontend_path),
|
||||
jwt_ttl: env
|
||||
.komodo_jwt_ttl
|
||||
@@ -223,8 +214,6 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
.unwrap_or(config.disable_user_registration),
|
||||
disable_non_admin_create: env.komodo_disable_non_admin_create
|
||||
.unwrap_or(config.disable_non_admin_create),
|
||||
disable_init_resources: env.komodo_disable_init_resources
|
||||
.unwrap_or(config.disable_init_resources),
|
||||
enable_fancy_toml: env.komodo_enable_fancy_toml
|
||||
.unwrap_or(config.enable_fancy_toml),
|
||||
lock_login_credentials_for: env.komodo_lock_login_credentials_for
|
||||
|
||||
@@ -1136,41 +1136,7 @@ async fn execute_execution(
|
||||
.resolve(&ExecuteArgs { user, update })
|
||||
.await
|
||||
.map_err(|e| e.error)
|
||||
.context("Failed at ClearRepoCache"),
|
||||
&update_id,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
Execution::BackupCoreDatabase(req) => {
|
||||
let req = ExecuteRequest::BackupCoreDatabase(req);
|
||||
let update = init_execution_update(&req, &user).await?;
|
||||
let ExecuteRequest::BackupCoreDatabase(req) = req else {
|
||||
unreachable!()
|
||||
};
|
||||
let update_id = update.id.clone();
|
||||
handle_resolve_result(
|
||||
req
|
||||
.resolve(&ExecuteArgs { user, update })
|
||||
.await
|
||||
.map_err(|e| e.error)
|
||||
.context("Failed at BackupCoreDatabase"),
|
||||
&update_id,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
Execution::GlobalAutoUpdate(req) => {
|
||||
let req = ExecuteRequest::GlobalAutoUpdate(req);
|
||||
let update = init_execution_update(&req, &user).await?;
|
||||
let ExecuteRequest::GlobalAutoUpdate(req) = req else {
|
||||
unreachable!()
|
||||
};
|
||||
let update_id = update.id.clone();
|
||||
handle_resolve_result(
|
||||
req
|
||||
.resolve(&ExecuteArgs { user, update })
|
||||
.await
|
||||
.map_err(|e| e.error)
|
||||
.context("Failed at GlobalAutoUpdate"),
|
||||
.context("Failed at TestAlerter"),
|
||||
&update_id,
|
||||
)
|
||||
.await?
|
||||
|
||||
@@ -3,7 +3,7 @@ use async_timing_util::{
|
||||
ONE_DAY_MS, Timelength, unix_timestamp_ms, wait_until_timelength,
|
||||
};
|
||||
use database::mungos::{find::find_collect, mongodb::bson::doc};
|
||||
use futures::{StreamExt, stream::FuturesUnordered};
|
||||
use futures::future::join_all;
|
||||
use periphery_client::api::image::PruneImages;
|
||||
|
||||
use crate::{config::core_config, state::db_client};
|
||||
@@ -30,26 +30,24 @@ pub fn spawn_prune_loop() {
|
||||
}
|
||||
|
||||
async fn prune_images() -> anyhow::Result<()> {
|
||||
let mut futures = find_collect(
|
||||
&db_client().servers,
|
||||
doc! { "config.enabled": true, "config.auto_prune": true },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("failed to get servers from db")?
|
||||
.into_iter()
|
||||
.map(|server| async move {
|
||||
(
|
||||
async {
|
||||
periphery_client(&server)?.request(PruneImages {}).await
|
||||
}
|
||||
.await,
|
||||
server,
|
||||
)
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>();
|
||||
let futures = find_collect(&db_client().servers, None, None)
|
||||
.await
|
||||
.context("failed to get servers from db")?
|
||||
.into_iter()
|
||||
.filter(|server| {
|
||||
server.config.enabled && server.config.auto_prune
|
||||
})
|
||||
.map(|server| async move {
|
||||
(
|
||||
async {
|
||||
periphery_client(&server)?.request(PruneImages {}).await
|
||||
}
|
||||
.await,
|
||||
server,
|
||||
)
|
||||
});
|
||||
|
||||
while let Some((res, server)) = futures.next().await {
|
||||
for (res, server) in join_all(futures).await {
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"failed to prune images on server {} ({}) | {e:#}",
|
||||
|
||||
@@ -504,12 +504,6 @@ pub async fn init_execution_update(
|
||||
ExecuteRequest::ClearRepoCache(_data) => {
|
||||
(Operation::ClearRepoCache, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::BackupCoreDatabase(_data) => {
|
||||
(Operation::BackupCoreDatabase, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::GlobalAutoUpdate(_data) => {
|
||||
(Operation::GlobalAutoUpdate, ResourceTarget::system())
|
||||
}
|
||||
};
|
||||
|
||||
let mut update = make_update(target, operation, user);
|
||||
|
||||
@@ -65,7 +65,7 @@ async fn app() -> anyhow::Result<()> {
|
||||
// Spawn background tasks
|
||||
monitor::spawn_monitor_loop();
|
||||
resource::spawn_resource_refresh_loop();
|
||||
resource::spawn_all_resources_cache_refresh_loop();
|
||||
resource::spawn_all_resources_refresh_loop();
|
||||
resource::spawn_build_state_refresh_loop();
|
||||
resource::spawn_repo_state_refresh_loop();
|
||||
resource::spawn_procedure_state_refresh_loop();
|
||||
|
||||
@@ -7,10 +7,8 @@ use komodo_client::entities::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
monitor::deployment_status_cache,
|
||||
resource,
|
||||
state::{action_states, db_client},
|
||||
alert::send_alerts, monitor::deployment_status_cache, resource,
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
@@ -19,7 +17,6 @@ pub async fn alert_deployments(
|
||||
server_names: &HashMap<String, String>,
|
||||
) {
|
||||
let mut alerts = Vec::<Alert>::new();
|
||||
let action_states = action_states();
|
||||
for status in deployment_status_cache().get_list().await {
|
||||
// Don't alert if prev None
|
||||
let Some(prev) = status.prev else {
|
||||
@@ -34,20 +31,6 @@ pub async fn alert_deployments(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't alert if deploying
|
||||
if action_states
|
||||
.deployment
|
||||
.get(&status.curr.id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.deploying))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if status.curr.state != prev {
|
||||
// send alert
|
||||
let Ok(deployment) =
|
||||
|
||||
@@ -9,7 +9,7 @@ use komodo_client::entities::{
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
resource,
|
||||
state::{action_states, db_client, stack_status_cache},
|
||||
state::{db_client, stack_status_cache},
|
||||
};
|
||||
|
||||
#[instrument(level = "debug")]
|
||||
@@ -17,7 +17,6 @@ pub async fn alert_stacks(
|
||||
ts: i64,
|
||||
server_names: &HashMap<String, String>,
|
||||
) {
|
||||
let action_states = action_states();
|
||||
let mut alerts = Vec::<Alert>::new();
|
||||
for status in stack_status_cache().get_list().await {
|
||||
// Don't alert if prev None
|
||||
@@ -33,20 +32,6 @@ pub async fn alert_stacks(
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't alert if deploying
|
||||
if action_states
|
||||
.stack
|
||||
.get(&status.curr.id)
|
||||
.await
|
||||
.map(|s| s.get().map(|s| s.deploying))
|
||||
.transpose()
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if status.curr.state != prev {
|
||||
// send alert
|
||||
let Ok(stack) =
|
||||
|
||||
@@ -70,8 +70,7 @@ pub use procedure::{
|
||||
refresh_procedure_state_cache, spawn_procedure_state_refresh_loop,
|
||||
};
|
||||
pub use refresh::{
|
||||
refresh_all_resources_cache,
|
||||
spawn_all_resources_cache_refresh_loop,
|
||||
refresh_all_resources_cache, spawn_all_resources_refresh_loop,
|
||||
spawn_resource_refresh_loop,
|
||||
};
|
||||
pub use repo::{
|
||||
@@ -707,7 +706,6 @@ pub async fn update_meta<T: KomodoResource>(
|
||||
Ok(tag) => Ok(tag.id),
|
||||
Err(_) => CreateTag {
|
||||
name: tag.to_string(),
|
||||
color: None,
|
||||
}
|
||||
.resolve(args)
|
||||
.await
|
||||
|
||||
@@ -732,20 +732,6 @@ async fn validate_config(
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::BackupCoreDatabase(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger core database backup"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::GlobalAutoUpdate(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger global auto update"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::Sleep(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ use komodo_client::{
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
api::write::WriteArgs,
|
||||
api::{execute::pull_deployment_inner, write::WriteArgs},
|
||||
config::core_config,
|
||||
helpers::all_resources::AllResourcesById,
|
||||
state::{all_resources_cache, db_client},
|
||||
};
|
||||
|
||||
pub fn spawn_all_resources_cache_refresh_loop() {
|
||||
pub fn spawn_all_resources_refresh_loop() {
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(15));
|
||||
loop {
|
||||
@@ -57,6 +57,7 @@ pub fn spawn_resource_refresh_loop() {
|
||||
|
||||
async fn refresh_all() {
|
||||
refresh_stacks().await;
|
||||
refresh_deployments().await;
|
||||
refresh_builds().await;
|
||||
refresh_repos().await;
|
||||
refresh_syncs().await;
|
||||
@@ -86,6 +87,42 @@ async fn refresh_stacks() {
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh_deployments() {
|
||||
let servers = find_collect(&db_client().servers, None, None)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
warn!(
|
||||
"Failed to get Servers from database in refresh task | {e:#}"
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let Ok(deployments) = find_collect(&db_client().deployments, None, None)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
warn!(
|
||||
"Failed to get Deployments from database in refresh task | {e:#}"
|
||||
)
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
for deployment in deployments {
|
||||
if (deployment.config.poll_for_updates
|
||||
|| deployment.config.auto_update)
|
||||
&& let Some(server) =
|
||||
servers.iter().find(|s| s.id == deployment.config.server_id)
|
||||
{
|
||||
let name = deployment.name.clone();
|
||||
if let Err(e) = pull_deployment_inner(deployment, server).await
|
||||
{
|
||||
warn!(
|
||||
"Failed to pull latest image for Deployment {name} | {e:#}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh_builds() {
|
||||
let Ok(builds) = find_collect(&db_client().builds, None, None)
|
||||
.await
|
||||
|
||||
@@ -75,7 +75,6 @@ impl super::KomodoResource for Server {
|
||||
.unwrap_or(String::from("Unknown")),
|
||||
region: server.config.region,
|
||||
address: server.config.address,
|
||||
external_address: server.config.external_address,
|
||||
send_unreachable_alerts: server
|
||||
.config
|
||||
.send_unreachable_alerts,
|
||||
|
||||
+40
-208
@@ -1,30 +1,20 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use colored::Colorize;
|
||||
use database::mungos::{
|
||||
find::find_collect,
|
||||
mongodb::bson::{Document, doc, oid::ObjectId, to_document},
|
||||
};
|
||||
use futures::future::join_all;
|
||||
use komodo_client::{
|
||||
api::{
|
||||
auth::SignUpLocalUser,
|
||||
execute::{
|
||||
BackupCoreDatabase, Execution, GlobalAutoUpdate, RunAction,
|
||||
},
|
||||
write::{
|
||||
CreateBuilder, CreateProcedure, CreateServer, CreateTag,
|
||||
UpdateResourceMeta,
|
||||
},
|
||||
},
|
||||
api::execute::RunAction,
|
||||
api::write::{CreateBuilder, CreateServer},
|
||||
entities::{
|
||||
ResourceTarget,
|
||||
action::Action,
|
||||
builder::{PartialBuilderConfig, PartialServerBuilderConfig},
|
||||
komodo_timestamp,
|
||||
procedure::{EnabledExecution, ProcedureConfig, ProcedureStage},
|
||||
server::{PartialServerConfig, Server},
|
||||
sync::ResourceSync,
|
||||
tag::TagColor,
|
||||
update::Log,
|
||||
user::{action_user, system_user},
|
||||
},
|
||||
@@ -32,32 +22,44 @@ use komodo_client::{
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
api::{
|
||||
auth::AuthArgs,
|
||||
execute::{ExecuteArgs, ExecuteRequest},
|
||||
write::WriteArgs,
|
||||
},
|
||||
api::execute::{ExecuteArgs, ExecuteRequest},
|
||||
api::write::WriteArgs,
|
||||
config::core_config,
|
||||
helpers::random_string,
|
||||
helpers::update::init_execution_update,
|
||||
network, resource,
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
/// Runs the Actions with `run_at_startup: true`
|
||||
/// This function should be run on startup,
|
||||
/// after the db client has been initialized
|
||||
pub async fn on_startup() {
|
||||
// Configure manual network interface if specified
|
||||
network::configure_internet_gateway().await;
|
||||
|
||||
tokio::join!(
|
||||
in_progress_update_cleanup(),
|
||||
open_alert_cleanup(),
|
||||
ensure_first_server_and_builder(),
|
||||
clean_up_server_templates(),
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn run_startup_actions() {
|
||||
let startup_actions = match find_collect(
|
||||
&db_client().actions,
|
||||
doc! { "config.run_at_startup": true },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(actions) => actions,
|
||||
Err(e) => {
|
||||
error!("Failed to fetch actions for startup | {e:#?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let startup_actions =
|
||||
match database::mungos::find::find_collect::<Action>(
|
||||
&db_client().actions,
|
||||
doc! { "config.run_at_startup": true },
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(actions) => actions,
|
||||
Err(e) => {
|
||||
error!("Failed to fetch actions for startup | {e:#?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
for action in startup_actions {
|
||||
let name = action.name;
|
||||
@@ -97,21 +99,6 @@ pub async fn run_startup_actions() {
|
||||
}
|
||||
}
|
||||
|
||||
/// This function should be run on startup,
|
||||
/// after the db client has been initialized
|
||||
pub async fn on_startup() {
|
||||
// Configure manual network interface if specified
|
||||
network::configure_internet_gateway().await;
|
||||
|
||||
tokio::join!(
|
||||
in_progress_update_cleanup(),
|
||||
open_alert_cleanup(),
|
||||
clean_up_server_templates(),
|
||||
ensure_first_server_and_builder(),
|
||||
ensure_init_user_and_resources(),
|
||||
);
|
||||
}
|
||||
|
||||
async fn in_progress_update_cleanup() {
|
||||
let log = Log::error(
|
||||
"Komodo shutdown",
|
||||
@@ -197,10 +184,10 @@ async fn open_alert_cleanup() {
|
||||
|
||||
/// Ensures a default server / builder exists with the defined address
|
||||
async fn ensure_first_server_and_builder() {
|
||||
let config = core_config();
|
||||
let Some(address) = config.first_server.clone() else {
|
||||
let first_server = &core_config().first_server;
|
||||
if first_server.is_empty() {
|
||||
return;
|
||||
};
|
||||
}
|
||||
let db = db_client();
|
||||
let Ok(server) = db
|
||||
.servers
|
||||
@@ -214,9 +201,9 @@ async fn ensure_first_server_and_builder() {
|
||||
server
|
||||
} else {
|
||||
match (CreateServer {
|
||||
name: config.first_server_name.clone(),
|
||||
name: format!("server-{}", random_string(5)),
|
||||
config: PartialServerConfig {
|
||||
address: Some(address),
|
||||
address: Some(first_server.to_string()),
|
||||
enabled: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -242,7 +229,7 @@ async fn ensure_first_server_and_builder() {
|
||||
return;
|
||||
};
|
||||
if let Err(e) = (CreateBuilder {
|
||||
name: String::from("Local"),
|
||||
name: String::from("local"),
|
||||
config: PartialBuilderConfig::Server(
|
||||
PartialServerBuilderConfig {
|
||||
server_id: Some(server.id),
|
||||
@@ -261,161 +248,6 @@ async fn ensure_first_server_and_builder() {
|
||||
}
|
||||
}
|
||||
|
||||
async fn ensure_init_user_and_resources() {
|
||||
let db = db_client();
|
||||
|
||||
// Assumes if there are any existing users, procedures, or tags,
|
||||
// the default procedures do not need to be set up.
|
||||
let Ok((None, None, None)) = tokio::try_join!(
|
||||
db.users.find_one(Document::new()),
|
||||
db.procedures.find_one(Document::new()),
|
||||
db.tags.find_one(Document::new()),
|
||||
).inspect_err(|e| error!("Failed to initialize default procedures | Failed to query db | {e:?}")) else {
|
||||
return
|
||||
};
|
||||
|
||||
let config = core_config();
|
||||
|
||||
// Init admin user if set in config.
|
||||
if let Some(username) = &config.init_admin_username {
|
||||
info!("Creating init admin user...");
|
||||
SignUpLocalUser {
|
||||
username: username.clone(),
|
||||
password: config.init_admin_password.clone(),
|
||||
}
|
||||
.resolve(&AuthArgs::default())
|
||||
.await
|
||||
.expect("Failed to initialize default admin user.")
|
||||
.jwt;
|
||||
db.users
|
||||
.find_one(doc! { "username": username })
|
||||
.await
|
||||
.expect("Failed to query database for initial user")
|
||||
.expect("Failed to find initial user after creation");
|
||||
};
|
||||
|
||||
if config.disable_init_resources {
|
||||
info!("System resources init {}", "DISABLED".red());
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Creating init system resources...");
|
||||
|
||||
let write_args = WriteArgs {
|
||||
user: system_user().to_owned(),
|
||||
};
|
||||
|
||||
// Create default 'system' tag
|
||||
let default_tags = match (CreateTag {
|
||||
name: String::from("system"),
|
||||
color: Some(TagColor::Red),
|
||||
})
|
||||
.resolve(&write_args)
|
||||
.await
|
||||
{
|
||||
Ok(tag) => vec![tag.id],
|
||||
Err(e) => {
|
||||
warn!("Failed to create default tag | {:#}", e.error);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
// Backup Core Database
|
||||
async {
|
||||
let Ok(config) = ProcedureConfig::builder()
|
||||
.stages(vec![ProcedureStage {
|
||||
name: String::from("Stage 1"),
|
||||
enabled: true,
|
||||
executions: vec![
|
||||
EnabledExecution {
|
||||
execution: Execution::BackupCoreDatabase(BackupCoreDatabase {}),
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
}])
|
||||
.schedule(String::from("Every day at 01:00"))
|
||||
.build()
|
||||
.inspect_err(|e| error!("Failed to initialize backup core database procedure | Failed to build Procedure | {e:?}")) else {
|
||||
return;
|
||||
};
|
||||
let procedure = match (CreateProcedure {
|
||||
name: String::from("Backup Core Database"),
|
||||
config: config.into()
|
||||
}).resolve(&write_args).await {
|
||||
Ok(procedure) => procedure,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to initialize default database backup Procedure | Failed to create Procedure | {:#}",
|
||||
e.error
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(e) = (UpdateResourceMeta {
|
||||
target: ResourceTarget::Procedure(procedure.id),
|
||||
tags: Some(default_tags.clone()),
|
||||
description: Some(String::from(
|
||||
"Triggers the Core database backup at the scheduled time.",
|
||||
)),
|
||||
template: None,
|
||||
}).resolve(&write_args).await {
|
||||
warn!("Failed to update default database backup Procedure tags / description | {:#}", e.error);
|
||||
}
|
||||
}.await;
|
||||
|
||||
// GlobalAutoUpdate
|
||||
async {
|
||||
let Ok(config) = ProcedureConfig::builder()
|
||||
.stages(vec![ProcedureStage {
|
||||
name: String::from("Stage 1"),
|
||||
enabled: true,
|
||||
executions: vec![
|
||||
EnabledExecution {
|
||||
execution: Execution::GlobalAutoUpdate(GlobalAutoUpdate {}),
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
}])
|
||||
.schedule(String::from("Every day at 03:00"))
|
||||
.build()
|
||||
.inspect_err(|e| error!("Failed to initialize global auto update procedure | Failed to build Procedure | {e:?}")) else {
|
||||
return;
|
||||
};
|
||||
let procedure = match (CreateProcedure {
|
||||
name: String::from("Global Auto Update"),
|
||||
config: config.into(),
|
||||
})
|
||||
.resolve(&write_args)
|
||||
.await
|
||||
{
|
||||
Ok(procedure) => procedure,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to initialize global auto update Procedure | Failed to create Procedure | {:#}",
|
||||
e.error
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(e) = (UpdateResourceMeta {
|
||||
target: ResourceTarget::Procedure(procedure.id),
|
||||
tags: Some(default_tags.clone()),
|
||||
description: Some(String::from(
|
||||
"Pulls and auto updates Stacks and Deployments using 'poll_for_updates' or 'auto_update'.",
|
||||
)),
|
||||
template: None,
|
||||
})
|
||||
.resolve(&write_args)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Failed to update global auto update Procedure tags / description | {:#}",
|
||||
e.error
|
||||
);
|
||||
}
|
||||
}.await;
|
||||
}
|
||||
|
||||
/// v1.17.5 removes the ServerTemplate resource.
|
||||
/// References to this resource type need to be cleaned up
|
||||
/// to avoid type errors reading from the database.
|
||||
|
||||
@@ -134,13 +134,11 @@ pub fn action_states() -> &'static ActionStates {
|
||||
ACTION_STATES.get_or_init(ActionStates::default)
|
||||
}
|
||||
|
||||
/// Cache of ids to status
|
||||
pub type DeploymentStatusCache = Cache<
|
||||
String,
|
||||
Arc<History<CachedDeploymentStatus, DeploymentState>>,
|
||||
>;
|
||||
|
||||
/// Cache of ids to status
|
||||
pub fn deployment_status_cache() -> &'static DeploymentStatusCache {
|
||||
static DEPLOYMENT_STATUS_CACHE: OnceLock<DeploymentStatusCache> =
|
||||
OnceLock::new();
|
||||
|
||||
@@ -670,8 +670,6 @@ impl ResourceSyncTrait for Procedure {
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::ClearRepoCache(_) => {}
|
||||
Execution::BackupCoreDatabase(_) => {}
|
||||
Execution::GlobalAutoUpdate(_) => {}
|
||||
Execution::Sleep(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ pub const TOML_PRETTY_OPTIONS: toml_pretty::Options =
|
||||
toml_pretty::Options {
|
||||
tab: " ",
|
||||
skip_empty_string: true,
|
||||
// Usually we do this, but has to be changed for some cases.
|
||||
skip_empty_object: true,
|
||||
max_inline_array_length: 30,
|
||||
inline_array: false,
|
||||
};
|
||||
@@ -790,9 +788,7 @@ impl ToToml for Procedure {
|
||||
),
|
||||
Execution::None(_)
|
||||
| Execution::Sleep(_)
|
||||
| Execution::ClearRepoCache(_)
|
||||
| Execution::BackupCoreDatabase(_)
|
||||
| Execution::GlobalAutoUpdate(_) => {}
|
||||
| Execution::ClearRepoCache(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -827,13 +823,8 @@ impl ToToml for Procedure {
|
||||
for stage in stages {
|
||||
toml.push_str("\n\n[[procedure.config.stage]]\n");
|
||||
toml.push_str(
|
||||
&toml_pretty::to_string(
|
||||
stage,
|
||||
// If the execution.params are fully missing,
|
||||
// deserialization will fail.
|
||||
TOML_PRETTY_OPTIONS.skip_empty_object(false),
|
||||
)
|
||||
.context("failed to serialize procedures to toml")?,
|
||||
&toml_pretty::to_string(stage, TOML_PRETTY_OPTIONS)
|
||||
.context("failed to serialize procedures to toml")?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
FROM rust:1.89.0-bullseye AS builder
|
||||
FROM rust:1.88.0-bullseye AS builder
|
||||
|
||||
WORKDIR /builder
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn periphery_config() -> &'static PeripheryConfig {
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.collect::<Vec<_>>(),
|
||||
include_file_name: ".peripheryinclude",
|
||||
include_file_name: ".peripheryignore",
|
||||
merge_nested: args
|
||||
.merge_nested_config
|
||||
.unwrap_or(env.periphery_merge_nested_config),
|
||||
|
||||
@@ -60,14 +60,7 @@ async fn update_container_stats() {
|
||||
pub async fn get_container_stats(
|
||||
container_name: Option<String>,
|
||||
) -> anyhow::Result<Vec<ContainerStats>> {
|
||||
let format = "--format '{\"BlockIO\":\"{{ .BlockIO }}\", \
|
||||
\"CPUPerc\":\"{{ .CPUPerc }}\", \
|
||||
\"ID\":\"{{ .ID }}\", \
|
||||
\"MemPerc\":\"{{ .MemPerc }}\", \
|
||||
\"MemUsage\":\"{{ .MemUsage }}\", \
|
||||
\"Name\":\"{{ .Name }}\", \
|
||||
\"NetIO\":\"{{ .NetIO }}\",\
|
||||
\"PIDs\":\"{{ .PIDs }}\"}'";
|
||||
let format = "--format \"{{ json . }}\"";
|
||||
let container_name = match container_name {
|
||||
Some(name) => format!(" {name}"),
|
||||
None => "".to_string(),
|
||||
|
||||
@@ -11,8 +11,6 @@ pub trait KomodoAuthRequest: HasResponse {}
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct JwtResponse {
|
||||
/// User ID for signed in user.
|
||||
pub user_id: String,
|
||||
/// A token the user can use to authenticate their requests.
|
||||
pub jwt: String,
|
||||
}
|
||||
@@ -49,20 +47,19 @@ pub struct GetLoginOptionsResponse {
|
||||
|
||||
//
|
||||
|
||||
/// Sign up a new local user account. Will fail if a user with the
|
||||
/// Create a new local user account. Will fail if a user with the
|
||||
/// given username already exists.
|
||||
/// Response: [SignUpLocalUserResponse].
|
||||
/// Response: [CreateLocalUserResponse].
|
||||
///
|
||||
/// Note. This method is only available if the core api has `local_auth` enabled,
|
||||
/// and if user registration is not disabled (after the first user).
|
||||
/// Note. This method is only available if the core api has `local_auth` enabled.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
|
||||
)]
|
||||
#[empty_traits(KomodoAuthRequest)]
|
||||
#[response(SignUpLocalUserResponse)]
|
||||
#[response(CreateLocalUserResponse)]
|
||||
#[error(serror::Error)]
|
||||
pub struct SignUpLocalUser {
|
||||
pub struct CreateLocalUser {
|
||||
/// The username for the new user.
|
||||
pub username: String,
|
||||
/// The password for the new user.
|
||||
@@ -70,9 +67,9 @@ pub struct SignUpLocalUser {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
/// Response for [SignUpLocalUser].
|
||||
/// Response for [CreateLocalUser].
|
||||
#[typeshare]
|
||||
pub type SignUpLocalUserResponse = JwtResponse;
|
||||
pub type CreateLocalUserResponse = JwtResponse;
|
||||
|
||||
//
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ pub struct RunAction {
|
||||
|
||||
/// Custom arguments which are merged on top of the default arguments.
|
||||
/// CLI Format: `"VAR1=val1&VAR2=val2"`
|
||||
///
|
||||
///
|
||||
/// Webhook-triggered actions use this to pass WEBHOOK_BRANCH and WEBHOOK_BODY.
|
||||
#[clap(value_parser = args_parser)]
|
||||
pub args: Option<JsonObject>,
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::entities::update::Update;
|
||||
use super::KomodoExecuteRequest;
|
||||
|
||||
/// Clears all repos from the Core repo cache. Admin only.
|
||||
/// Response: [Update]
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug,
|
||||
@@ -26,14 +25,13 @@ use super::KomodoExecuteRequest;
|
||||
#[error(serror::Error)]
|
||||
pub struct ClearRepoCache {}
|
||||
|
||||
/// Backs up the Komodo Core database to compressed jsonl files.
|
||||
/// Admin only. Response: [Update]
|
||||
/// Backs up the database to compressed jsonl files. Admin only.
|
||||
///
|
||||
/// Mount a folder to `/backups`, and Core will use it to create
|
||||
/// timestamped database dumps, which can be restored using
|
||||
/// the Komodo CLI.
|
||||
/// `ghcr.io/moghtech/komodo-util`.
|
||||
///
|
||||
/// https://komo.do/docs/setup/backup
|
||||
/// TODO: Link to docs
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug,
|
||||
@@ -48,26 +46,4 @@ pub struct ClearRepoCache {}
|
||||
#[empty_traits(KomodoExecuteRequest)]
|
||||
#[response(Update)]
|
||||
#[error(serror::Error)]
|
||||
pub struct BackupCoreDatabase {}
|
||||
|
||||
/// Trigger a global poll for image updateson Stacks and Deployments
|
||||
/// with `poll_for_updates` or `auto_update` enabled.
|
||||
/// Admin only. Response: [Update]
|
||||
///
|
||||
/// 1. `docker compose pull` any Stacks / Deployments with `poll_for_updates` or `auto_update` enabled. This will pick up any available updates.
|
||||
/// 2. Redeploy Stacks / Deployments that have updates found.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Resolve,
|
||||
EmptyTraits,
|
||||
Parser,
|
||||
)]
|
||||
#[empty_traits(KomodoExecuteRequest)]
|
||||
#[response(Update)]
|
||||
#[error(serror::Error)]
|
||||
pub struct GlobalAutoUpdate {}
|
||||
pub struct BackupDatabase {}
|
||||
|
||||
@@ -156,10 +156,8 @@ pub enum Execution {
|
||||
// ALERTER
|
||||
TestAlerter(TestAlerter),
|
||||
|
||||
// MAINTENANCE
|
||||
// CORE
|
||||
ClearRepoCache(ClearRepoCache),
|
||||
BackupCoreDatabase(BackupCoreDatabase),
|
||||
GlobalAutoUpdate(GlobalAutoUpdate),
|
||||
|
||||
// SLEEP
|
||||
Sleep(Sleep),
|
||||
|
||||
@@ -20,8 +20,6 @@ use super::KomodoWriteRequest;
|
||||
pub struct CreateTag {
|
||||
/// The name of the tag.
|
||||
pub name: String,
|
||||
/// Tag color. Default: Slate.
|
||||
pub color: Option<TagColor>,
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -69,31 +69,6 @@ pub type DeleteUserResponse = User;
|
||||
|
||||
//
|
||||
|
||||
/// **Admin only.** Create a local user.
|
||||
/// Response: [User].
|
||||
///
|
||||
/// Note. Not to be confused with /auth/SignUpLocalUser.
|
||||
/// This method requires admin user credentials, and can
|
||||
/// bypass disabled user registration.
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
|
||||
)]
|
||||
#[empty_traits(KomodoWriteRequest)]
|
||||
#[response(CreateLocalUserResponse)]
|
||||
#[error(serror::Error)]
|
||||
pub struct CreateLocalUser {
|
||||
/// The username for the local user.
|
||||
pub username: String,
|
||||
/// A password for the local user.
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type CreateLocalUserResponse = User;
|
||||
|
||||
//
|
||||
|
||||
/// **Admin only.** Create a service user.
|
||||
/// Response: [User].
|
||||
#[typeshare]
|
||||
|
||||
@@ -164,11 +164,7 @@ pub struct ActionConfig {
|
||||
pub arguments_format: FileFormat,
|
||||
|
||||
/// Default arguments to give to the Action for use in the script at `ARGS`.
|
||||
#[serde(default, deserialize_with = "file_contents_deserializer")]
|
||||
#[partial_attr(serde(
|
||||
default,
|
||||
deserialize_with = "option_file_contents_deserializer"
|
||||
))]
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub arguments: String,
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ pub struct Container {
|
||||
/// List only non-running containers.
|
||||
#[arg(long, short = 'd', default_value_t = false)]
|
||||
pub down: bool,
|
||||
/// Include links. Makes the table very large.
|
||||
#[arg(long, short = 'l', default_value_t = false)]
|
||||
pub links: bool,
|
||||
/// Filter containers by a particular server.
|
||||
/// Supports wildcard syntax.
|
||||
/// Can be specified multiple times. (alias `s`)
|
||||
@@ -51,22 +48,7 @@ pub struct InspectContainer {
|
||||
/// The container name. If it matches multiple containers and no server is specified,
|
||||
/// each container's inspect info will be logged.
|
||||
pub container: String,
|
||||
/// Select the particular server that container is on.
|
||||
/// Select the particular server container is on (alias `s`)
|
||||
#[arg(name = "server", long, short = 's')]
|
||||
pub servers: Vec<String>,
|
||||
/// Only show the .State part of the inspect response.
|
||||
#[arg(long, short = 'u')]
|
||||
pub state: bool,
|
||||
/// Only show the .Mounts part of the inspect response.
|
||||
#[arg(long, short = 'm')]
|
||||
pub mounts: bool,
|
||||
/// Only show the .HostConfig part of the inspect response.
|
||||
#[arg(long, short = 'f')]
|
||||
pub host_config: bool,
|
||||
/// Only show the .Config part of the inspect response.
|
||||
#[arg(long, short = 'c')]
|
||||
pub config: bool,
|
||||
/// Only show the .NetworkSettings part of the inspect response.
|
||||
#[arg(long, short = 'n')]
|
||||
pub network_settings: bool,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::entities::resource::TemplatesQueryBehavior;
|
||||
|
||||
#[derive(Debug, Clone, clap::Parser)]
|
||||
pub struct List {
|
||||
/// List specific resources
|
||||
@@ -15,24 +13,6 @@ pub struct List {
|
||||
/// List only non-running / non-ok resources.
|
||||
#[arg(long, short = 'd', default_value_t = false)]
|
||||
pub down: bool,
|
||||
/// List only "in progress" / "pending" resources, like Actions / Procedures that are running (alias: `pending`)
|
||||
#[arg(
|
||||
long,
|
||||
short = 'p',
|
||||
alias = "pending",
|
||||
default_value_t = false
|
||||
)]
|
||||
pub in_progress: bool,
|
||||
/// Include links. Makes the table very large.
|
||||
#[arg(long, short = 'l', default_value_t = false)]
|
||||
pub links: bool,
|
||||
/// Whether to include resources marked as templates in results. Default: 'exclude'.
|
||||
#[arg(
|
||||
long,
|
||||
short = 'm',
|
||||
default_value_t = TemplatesQueryBehavior::Exclude,
|
||||
)]
|
||||
pub templates: TemplatesQueryBehavior,
|
||||
/// Filter by a particular name. Supports wildcard.
|
||||
/// Can be specified multiple times. (alias `n`)
|
||||
#[arg(name = "name", long, short = 'n')]
|
||||
@@ -60,9 +40,6 @@ impl From<List> for ResourceFilters {
|
||||
all: value.all,
|
||||
reverse: value.reverse,
|
||||
down: value.down,
|
||||
in_progress: value.in_progress,
|
||||
links: value.links,
|
||||
templates: value.templates,
|
||||
names: value.names,
|
||||
tags: value.tags,
|
||||
servers: value.servers,
|
||||
@@ -121,24 +98,6 @@ pub struct ResourceFilters {
|
||||
/// List only non-running / non-ok resources.
|
||||
#[arg(long, short = 'd', default_value_t = false)]
|
||||
pub down: bool,
|
||||
/// List only "in progress" / "pending" resources, like Actions / Procedures that are running (alias: `pending`)
|
||||
#[arg(
|
||||
long,
|
||||
short = 'p',
|
||||
alias = "pending",
|
||||
default_value_t = false
|
||||
)]
|
||||
pub in_progress: bool,
|
||||
/// Include links. Makes the table very large.
|
||||
#[arg(long, short = 'l', default_value_t = false)]
|
||||
pub links: bool,
|
||||
/// Whether to include resources marked as templates in results. Default: 'exclude'.
|
||||
#[arg(
|
||||
long,
|
||||
short = 'm',
|
||||
default_value_t = TemplatesQueryBehavior::Exclude,
|
||||
)]
|
||||
pub templates: TemplatesQueryBehavior,
|
||||
/// Filter by a particular name. Supports wildcard.
|
||||
/// Can be specified multiple times. (alias `n`)
|
||||
#[arg(name = "name", long, short = 'n')]
|
||||
|
||||
@@ -32,7 +32,7 @@ pub struct CliArgs {
|
||||
pub config_keyword: Option<Vec<String>>,
|
||||
|
||||
/// Whether to debug print on configuration load (on startup)
|
||||
#[arg(alias = "debug", long, short = 'd')]
|
||||
#[arg(action, alias = "debug", short = 'd')]
|
||||
pub debug_startup: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -64,13 +64,10 @@ pub struct Env {
|
||||
pub komodo_cli_key: Option<String>,
|
||||
/// Override `cli_secret`
|
||||
pub komodo_cli_secret: Option<String>,
|
||||
/// Override `table_borders`
|
||||
pub komodo_cli_table_borders: Option<CliTableBorders>,
|
||||
/// Override `backups_folder`
|
||||
pub komodo_cli_backups_folder: Option<PathBuf>,
|
||||
/// Override `max_backups`
|
||||
pub komodo_cli_max_backups: Option<u16>,
|
||||
|
||||
/// Override `database_target_uri`
|
||||
#[serde(alias = "komodo_cli_database_copy_uri")]
|
||||
pub komodo_cli_database_target_uri: Option<String>,
|
||||
@@ -191,9 +188,6 @@ pub struct CliConfig {
|
||||
/// The api secret for the CLI to use
|
||||
#[serde(alias = "secret", skip_serializing_if = "Option::is_none")]
|
||||
pub cli_secret: Option<String>,
|
||||
/// The format for the tables.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub table_borders: Option<CliTableBorders>,
|
||||
/// The root backups folder.
|
||||
///
|
||||
/// Default: `/backups`.
|
||||
@@ -277,7 +271,6 @@ impl Default for CliConfig {
|
||||
cli_key: Default::default(),
|
||||
cli_secret: Default::default(),
|
||||
cli_logging: default_log_config(),
|
||||
table_borders: Default::default(),
|
||||
backups_folder: default_backups_folder(),
|
||||
max_backups: default_max_backups(),
|
||||
database: default_database_config(),
|
||||
@@ -303,7 +296,6 @@ impl CliConfig {
|
||||
.as_ref()
|
||||
.map(|cli_secret| empty_or_redacted(cli_secret)),
|
||||
cli_logging: self.cli_logging.clone(),
|
||||
table_borders: self.table_borders,
|
||||
backups_folder: self.backups_folder.clone(),
|
||||
max_backups: self.max_backups,
|
||||
database_target: self.database_target.sanitized(),
|
||||
@@ -317,18 +309,3 @@ impl CliConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||
pub enum CliTableBorders {
|
||||
/// Only horizontal borders. Default.
|
||||
#[default]
|
||||
Horizontal,
|
||||
/// Only vertical borders.
|
||||
Vertical,
|
||||
/// Only borders around the outside of the table.
|
||||
Outside,
|
||||
/// Only borders horizontally / vertically between the rows / columns.
|
||||
Inside,
|
||||
/// All borders
|
||||
All,
|
||||
}
|
||||
|
||||
@@ -80,8 +80,6 @@ pub struct Env {
|
||||
pub komodo_timezone: Option<String>,
|
||||
/// Override `first_server`
|
||||
pub komodo_first_server: Option<String>,
|
||||
/// Override `first_server_name`
|
||||
pub komodo_first_server_name: Option<String>,
|
||||
/// Override `frontend_path`
|
||||
pub komodo_frontend_path: Option<String>,
|
||||
/// Override `jwt_secret`
|
||||
@@ -142,21 +140,11 @@ pub struct Env {
|
||||
pub komodo_disable_non_admin_create: Option<bool>,
|
||||
/// Override `disable_websocket_reconnect`
|
||||
pub komodo_disable_websocket_reconnect: Option<bool>,
|
||||
/// Override `disable_init_resources`
|
||||
pub komodo_disable_init_resources: Option<bool>,
|
||||
/// Override `enable_fancy_toml`
|
||||
pub komodo_enable_fancy_toml: Option<bool>,
|
||||
|
||||
/// Override `local_auth`
|
||||
pub komodo_local_auth: Option<bool>,
|
||||
/// Override `init_admin_username`
|
||||
pub komodo_init_admin_username: Option<String>,
|
||||
/// Override `init_admin_username` from file
|
||||
pub komodo_init_admin_username_file: Option<PathBuf>,
|
||||
/// Override `init_admin_password`
|
||||
pub komodo_init_admin_password: Option<String>,
|
||||
/// Override `init_admin_password` from file
|
||||
pub komodo_init_admin_password_file: Option<PathBuf>,
|
||||
|
||||
/// Override `oidc_enabled`
|
||||
pub komodo_oidc_enabled: Option<bool>,
|
||||
@@ -338,24 +326,14 @@ pub struct CoreConfig {
|
||||
#[serde(default)]
|
||||
pub disable_websocket_reconnect: bool,
|
||||
|
||||
/// Disable init system resource creation on fresh Komodo launch.
|
||||
/// These include the Backup Core Database and Global Auto Update procedures.
|
||||
#[serde(default)]
|
||||
pub disable_init_resources: bool,
|
||||
|
||||
/// Enable the fancy TOML syntax highlighting
|
||||
#[serde(default)]
|
||||
pub enable_fancy_toml: bool,
|
||||
|
||||
/// If defined, ensure an enabled first server exists at this address.
|
||||
/// Example: `http://periphery:8120`
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub first_server: Option<String>,
|
||||
|
||||
/// Give the first server this name.
|
||||
/// Default: `Local`
|
||||
#[serde(default = "default_first_server_name")]
|
||||
pub first_server_name: String,
|
||||
#[serde(default)]
|
||||
pub first_server: String,
|
||||
|
||||
/// The path to the built frontend folder.
|
||||
#[serde(default = "default_frontend_path")]
|
||||
@@ -372,15 +350,6 @@ pub struct CoreConfig {
|
||||
#[serde(default)]
|
||||
pub local_auth: bool,
|
||||
|
||||
/// Upon fresh launch, initalize an Admin user with this username.
|
||||
/// If this is not provided, no initial user will be created.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub init_admin_username: Option<String>,
|
||||
/// Upon fresh launch, initalize an Admin user with this password.
|
||||
/// Default: `changeme`
|
||||
#[serde(default = "default_init_admin_password")]
|
||||
pub init_admin_password: String,
|
||||
|
||||
/// Enable transparent mode, which gives all (enabled) users read access to all resources.
|
||||
#[serde(default)]
|
||||
pub transparent_mode: bool,
|
||||
@@ -399,7 +368,7 @@ pub struct CoreConfig {
|
||||
/// APIs are disabled. Used by demo to lock the 'demo' : 'demo' login.
|
||||
///
|
||||
/// To lock the api for all users, use `lock_login_credentials_for = ["__ALL__"]`
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(default)]
|
||||
pub lock_login_credentials_for: Vec<String>,
|
||||
|
||||
/// Normally all users can create resources.
|
||||
@@ -460,7 +429,7 @@ pub struct CoreConfig {
|
||||
|
||||
/// Your OIDC provider may set additional audiences other than `client_id`,
|
||||
/// they must be added here to make claims verification work.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
#[serde(default)]
|
||||
pub oidc_additional_audiences: Vec<String>,
|
||||
|
||||
// =========
|
||||
@@ -550,11 +519,7 @@ pub struct CoreConfig {
|
||||
// =================
|
||||
/// Configure git credentials used to clone private repos.
|
||||
/// Supports any git provider.
|
||||
#[serde(
|
||||
default,
|
||||
alias = "git_provider",
|
||||
skip_serializing_if = "Vec::is_empty"
|
||||
)]
|
||||
#[serde(default, alias = "git_provider")]
|
||||
pub git_providers: Vec<GitProvider>,
|
||||
|
||||
// ======================
|
||||
@@ -562,11 +527,7 @@ pub struct CoreConfig {
|
||||
// ======================
|
||||
/// Configure docker credentials used to push / pull images.
|
||||
/// Supports any docker image repository.
|
||||
#[serde(
|
||||
default,
|
||||
alias = "docker_registry",
|
||||
skip_serializing_if = "Vec::is_empty"
|
||||
)]
|
||||
#[serde(default, alias = "docker_registry")]
|
||||
pub docker_registries: Vec<DockerRegistry>,
|
||||
|
||||
// ===========
|
||||
@@ -575,7 +536,7 @@ pub struct CoreConfig {
|
||||
/// Configure core-based secrets. These will be preferentially interpolated into
|
||||
/// values if they contain a matching secret. Otherwise, the periphery will have to have the
|
||||
/// secret configured.
|
||||
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
|
||||
#[serde(default)]
|
||||
pub secrets: HashMap<String, String>,
|
||||
|
||||
// =======
|
||||
@@ -639,18 +600,10 @@ fn default_frontend_path() -> String {
|
||||
"/app/frontend".to_string()
|
||||
}
|
||||
|
||||
fn default_first_server_name() -> String {
|
||||
String::from("Local")
|
||||
}
|
||||
|
||||
fn default_jwt_ttl() -> Timelength {
|
||||
Timelength::OneDay
|
||||
}
|
||||
|
||||
fn default_init_admin_password() -> String {
|
||||
String::from("changeme")
|
||||
}
|
||||
|
||||
fn default_sync_directory() -> PathBuf {
|
||||
// unwrap ok: `/syncs` will always be valid path
|
||||
PathBuf::from_str("/syncs").unwrap()
|
||||
@@ -699,15 +652,11 @@ impl Default for CoreConfig {
|
||||
ui_write_disabled: Default::default(),
|
||||
disable_confirm_dialog: Default::default(),
|
||||
disable_websocket_reconnect: Default::default(),
|
||||
disable_init_resources: Default::default(),
|
||||
enable_fancy_toml: Default::default(),
|
||||
first_server: Default::default(),
|
||||
first_server_name: default_first_server_name(),
|
||||
frontend_path: default_frontend_path(),
|
||||
database: Default::default(),
|
||||
local_auth: Default::default(),
|
||||
init_admin_username: Default::default(),
|
||||
init_admin_password: default_init_admin_password(),
|
||||
transparent_mode: Default::default(),
|
||||
enable_new_users: Default::default(),
|
||||
disable_user_registration: Default::default(),
|
||||
@@ -758,7 +707,6 @@ impl CoreConfig {
|
||||
passkey: empty_or_redacted(&config.passkey),
|
||||
timezone: config.timezone,
|
||||
first_server: config.first_server,
|
||||
first_server_name: config.first_server_name,
|
||||
frontend_path: config.frontend_path,
|
||||
jwt_secret: empty_or_redacted(&config.jwt_secret),
|
||||
jwt_ttl: config.jwt_ttl,
|
||||
@@ -776,19 +724,12 @@ impl CoreConfig {
|
||||
ui_write_disabled: config.ui_write_disabled,
|
||||
disable_confirm_dialog: config.disable_confirm_dialog,
|
||||
disable_websocket_reconnect: config.disable_websocket_reconnect,
|
||||
disable_init_resources: config.disable_init_resources,
|
||||
enable_fancy_toml: config.enable_fancy_toml,
|
||||
enable_new_users: config.enable_new_users,
|
||||
disable_user_registration: config.disable_user_registration,
|
||||
disable_non_admin_create: config.disable_non_admin_create,
|
||||
lock_login_credentials_for: config.lock_login_credentials_for,
|
||||
local_auth: config.local_auth,
|
||||
init_admin_username: config
|
||||
.init_admin_username
|
||||
.map(|u| empty_or_redacted(&u)),
|
||||
init_admin_password: empty_or_redacted(
|
||||
&config.init_admin_password,
|
||||
),
|
||||
oidc_enabled: config.oidc_enabled,
|
||||
oidc_provider: config.oidc_provider,
|
||||
oidc_redirect_host: config.oidc_redirect_host,
|
||||
|
||||
@@ -1165,8 +1165,6 @@ pub enum Operation {
|
||||
|
||||
// maintenance
|
||||
ClearRepoCache,
|
||||
BackupCoreDatabase,
|
||||
GlobalAutoUpdate,
|
||||
|
||||
// variable
|
||||
CreateVariable,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use bson::{Document, doc};
|
||||
use clap::ValueEnum;
|
||||
use derive_builder::Builder;
|
||||
use derive_default_builder::DefaultBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::Display;
|
||||
use typeshare::typeshare;
|
||||
|
||||
use crate::{
|
||||
@@ -126,18 +124,7 @@ pub struct ResourceQuery<T: Default> {
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
ValueEnum,
|
||||
Display,
|
||||
)]
|
||||
// Only strum serializes lowercase for clap compat.
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||
pub enum TemplatesQueryBehavior {
|
||||
/// Include templates in results. Default.
|
||||
#[default]
|
||||
|
||||
@@ -34,10 +34,6 @@ pub struct ServerListItemInfo {
|
||||
pub region: String,
|
||||
/// Address of the server.
|
||||
pub address: String,
|
||||
/// External address of the server (reachable by users).
|
||||
/// Used with links.
|
||||
#[serde(default)] // API backward compat
|
||||
pub external_address: String,
|
||||
/// The Komodo Periphery version of the server.
|
||||
pub version: String,
|
||||
/// Whether server is configured to send unreachable alerts.
|
||||
@@ -70,12 +66,6 @@ pub struct ServerConfig {
|
||||
#[partial_default(default_address())]
|
||||
pub address: String,
|
||||
|
||||
/// The address to use with links for containers on the server.
|
||||
/// If empty, will use the 'address' for links.
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub external_address: String,
|
||||
|
||||
/// An optional region label
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
@@ -260,8 +250,7 @@ fn default_disk_critical() -> f64 {
|
||||
impl Default for ServerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
address: default_address(),
|
||||
external_address: Default::default(),
|
||||
address: Default::default(),
|
||||
enabled: default_enabled(),
|
||||
timeout_seconds: default_timeout_seconds(),
|
||||
ignore_mounts: Default::default(),
|
||||
|
||||
@@ -41,21 +41,16 @@ pub struct Tag {
|
||||
#[serde(default)]
|
||||
#[builder(default)]
|
||||
pub color: TagColor,
|
||||
// /// This field is not stored on database,
|
||||
// /// but rather populated at query time based on results from the other resources.
|
||||
// #[serde(default, skip_serializing_if = "is_false")]
|
||||
// #[builder(default)]
|
||||
// pub unused: bool,
|
||||
|
||||
/// This field is not stored on database,
|
||||
/// but rather populated at query time based on results from the other resources.
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
#[builder(default)]
|
||||
pub unused: bool,
|
||||
}
|
||||
|
||||
// fn is_false(b: &bool) -> bool {
|
||||
// !b
|
||||
// }
|
||||
|
||||
impl Tag {
|
||||
pub fn builder() -> TagBuilder {
|
||||
TagBuilder::default()
|
||||
}
|
||||
fn is_false(b: &bool) -> bool {
|
||||
!b
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as Types from "./types.js";
|
||||
|
||||
export type AuthResponses = {
|
||||
GetLoginOptions: Types.GetLoginOptionsResponse;
|
||||
SignUpLocalUser: Types.SignUpLocalUserResponse;
|
||||
CreateLocalUser: Types.CreateLocalUserResponse;
|
||||
LoginLocalUser: Types.LoginLocalUserResponse;
|
||||
ExchangeForJwt: Types.ExchangeForJwtResponse;
|
||||
GetUser: Types.GetUserResponse;
|
||||
@@ -179,10 +179,9 @@ export type ReadResponses = {
|
||||
|
||||
export type WriteResponses = {
|
||||
// ==== USER ====
|
||||
CreateLocalUser: Types.CreateLocalUserResponse;
|
||||
UpdateUserUsername: Types.UpdateUserUsernameResponse;
|
||||
UpdateUserPassword: Types.UpdateUserPasswordResponse;
|
||||
DeleteUser: Types.DeleteUserResponse;
|
||||
UpdateUserUsername: Types.UpdateUserUsername;
|
||||
UpdateUserPassword: Types.UpdateUserPassword;
|
||||
DeleteUser: Types.DeleteUser;
|
||||
|
||||
// ==== SERVICE USER ====
|
||||
CreateServiceUser: Types.CreateServiceUserResponse;
|
||||
@@ -293,7 +292,7 @@ export type WriteResponses = {
|
||||
DeleteResourceSync: Types.ResourceSync;
|
||||
UpdateResourceSync: Types.ResourceSync;
|
||||
RenameResourceSync: Types.Update;
|
||||
CommitSync: Types.Update;
|
||||
CommitSync: Types.ResourceSync;
|
||||
WriteSyncFileContents: Types.Update;
|
||||
RefreshResourceSyncPending: Types.ResourceSync;
|
||||
CreateSyncWebhook: Types.CreateSyncWebhookResponse;
|
||||
@@ -411,6 +410,4 @@ export type ExecuteResponses = {
|
||||
|
||||
// ==== MAINTENANCE ====
|
||||
ClearRepoCache: Types.Update;
|
||||
BackupCoreDatabase: Types.Update;
|
||||
GlobalAutoUpdate: Types.Update;
|
||||
};
|
||||
|
||||
+34
-85
@@ -448,8 +448,6 @@ export enum Operation {
|
||||
CommitSync = "CommitSync",
|
||||
RunSync = "RunSync",
|
||||
ClearRepoCache = "ClearRepoCache",
|
||||
BackupCoreDatabase = "BackupCoreDatabase",
|
||||
GlobalAutoUpdate = "GlobalAutoUpdate",
|
||||
CreateVariable = "CreateVariable",
|
||||
UpdateVariableValue = "UpdateVariableValue",
|
||||
DeleteVariable = "DeleteVariable",
|
||||
@@ -869,8 +867,6 @@ export type Execution =
|
||||
| { type: "BatchDestroyStack", params: BatchDestroyStack }
|
||||
| { type: "TestAlerter", params: TestAlerter }
|
||||
| { type: "ClearRepoCache", params: ClearRepoCache }
|
||||
| { type: "BackupCoreDatabase", params: BackupCoreDatabase }
|
||||
| { type: "GlobalAutoUpdate", params: GlobalAutoUpdate }
|
||||
| { type: "Sleep", params: Sleep };
|
||||
|
||||
/** Allows to enable / disabled procedures in the sequence / parallel vec on the fly */
|
||||
@@ -1024,6 +1020,19 @@ export interface GitProviderAccount {
|
||||
|
||||
export type CreateGitProviderAccountResponse = GitProviderAccount;
|
||||
|
||||
/** JSON containing an authentication token. */
|
||||
export interface JwtResponse {
|
||||
/** A token the user can use to authenticate their requests. */
|
||||
jwt: string;
|
||||
}
|
||||
|
||||
/** Response for [CreateLocalUser]. */
|
||||
export type CreateLocalUserResponse = JwtResponse;
|
||||
|
||||
export type CreateProcedureResponse = Procedure;
|
||||
|
||||
export type CreateRepoWebhookResponse = NoData;
|
||||
|
||||
export type UserConfig =
|
||||
/** User that logs in with username / password */
|
||||
| { type: "Local", data: {
|
||||
@@ -1079,12 +1088,6 @@ export interface User {
|
||||
updated_at?: I64;
|
||||
}
|
||||
|
||||
export type CreateLocalUserResponse = User;
|
||||
|
||||
export type CreateProcedureResponse = Procedure;
|
||||
|
||||
export type CreateRepoWebhookResponse = NoData;
|
||||
|
||||
export type CreateServiceUserResponse = User;
|
||||
|
||||
export type CreateStackWebhookResponse = NoData;
|
||||
@@ -1321,14 +1324,6 @@ export interface DeploymentQuerySpecifics {
|
||||
|
||||
export type DeploymentQuery = ResourceQuery<DeploymentQuerySpecifics>;
|
||||
|
||||
/** JSON containing an authentication token. */
|
||||
export interface JwtResponse {
|
||||
/** User ID for signed in user. */
|
||||
user_id: string;
|
||||
/** A token the user can use to authenticate their requests. */
|
||||
jwt: string;
|
||||
}
|
||||
|
||||
/** Response for [ExchangeForJwt]. */
|
||||
export type ExchangeForJwtResponse = JwtResponse;
|
||||
|
||||
@@ -1953,11 +1948,6 @@ export interface ServerConfig {
|
||||
* Default: http://localhost:8120
|
||||
*/
|
||||
address: string;
|
||||
/**
|
||||
* The address to use with links for containers on the server.
|
||||
* If empty, will use the 'address' for links.
|
||||
*/
|
||||
external_address?: string;
|
||||
/** An optional region label */
|
||||
region?: string;
|
||||
/**
|
||||
@@ -2464,6 +2454,11 @@ export interface Tag {
|
||||
owner?: string;
|
||||
/** Hex color code with alpha for UI display */
|
||||
color?: TagColor;
|
||||
/**
|
||||
* This field is not stored on database,
|
||||
* but rather populated at query time based on results from the other resources.
|
||||
*/
|
||||
unused?: boolean;
|
||||
}
|
||||
|
||||
export type GetTagResponse = Tag;
|
||||
@@ -3745,11 +3740,6 @@ export interface ServerListItemInfo {
|
||||
region: string;
|
||||
/** Address of the server. */
|
||||
address: string;
|
||||
/**
|
||||
* External address of the server (reachable by users).
|
||||
* Used with links.
|
||||
*/
|
||||
external_address?: string;
|
||||
/** The Komodo Periphery version of the server. */
|
||||
version: string;
|
||||
/** Whether server is configured to send unreachable alerts. */
|
||||
@@ -3957,9 +3947,6 @@ export type ServerQuery = ResourceQuery<ServerQuerySpecifics>;
|
||||
|
||||
export type SetLastSeenUpdateResponse = NoData;
|
||||
|
||||
/** Response for [SignUpLocalUser]. */
|
||||
export type SignUpLocalUserResponse = JwtResponse;
|
||||
|
||||
export interface StackQuerySpecifics {
|
||||
/**
|
||||
* Query only for Stacks on these Servers.
|
||||
@@ -4100,16 +4087,15 @@ export interface AwsBuilderConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs up the Komodo Core database to compressed jsonl files.
|
||||
* Admin only. Response: [Update]
|
||||
* Backs up the database to compressed jsonl files. Admin only.
|
||||
*
|
||||
* Mount a folder to `/backups`, and Core will use it to create
|
||||
* timestamped database dumps, which can be restored using
|
||||
* the Komodo CLI.
|
||||
* `ghcr.io/moghtech/komodo-util`.
|
||||
*
|
||||
* TODO: Link to docs
|
||||
*/
|
||||
export interface BackupCoreDatabase {
|
||||
export interface BackupDatabase {
|
||||
}
|
||||
|
||||
/** Builds multiple Repos in parallel that match pattern. Response: [BatchExecutionResponse]. */
|
||||
@@ -4365,10 +4351,7 @@ export interface CancelRepoBuild {
|
||||
repo: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all repos from the Core repo cache. Admin only.
|
||||
* Response: [Update]
|
||||
*/
|
||||
/** Clears all repos from the Core repo cache. Admin only. */
|
||||
export interface ClearRepoCache {
|
||||
}
|
||||
|
||||
@@ -4904,17 +4887,19 @@ export interface CreateGitProviderAccount {
|
||||
}
|
||||
|
||||
/**
|
||||
* **Admin only.** Create a local user.
|
||||
* Response: [User].
|
||||
* Create a new local user account. Will fail if a user with the
|
||||
* given username already exists.
|
||||
* Response: [CreateLocalUserResponse].
|
||||
*
|
||||
* Note. Not to be confused with /auth/SignUpLocalUser.
|
||||
* This method requires admin user credentials, and can
|
||||
* bypass disabled user registration.
|
||||
* Note. This method is only available if the core api has `local_auth` enabled.
|
||||
*/
|
||||
export interface CreateLocalUser {
|
||||
/** The username for the local user. */
|
||||
/** The username for the new user. */
|
||||
username: string;
|
||||
/** A password for the local user. */
|
||||
/**
|
||||
* The password for the new user.
|
||||
* This cannot be retreived later.
|
||||
*/
|
||||
password: string;
|
||||
}
|
||||
|
||||
@@ -5035,8 +5020,6 @@ export interface CreateSyncWebhook {
|
||||
export interface CreateTag {
|
||||
/** The name of the tag. */
|
||||
name: string;
|
||||
/** Tag color. Default: Slate. */
|
||||
color?: TagColor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6439,17 +6422,6 @@ export interface GetVersionResponse {
|
||||
version: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a global poll for image updateson Stacks and Deployments
|
||||
* with `poll_for_updates` or `auto_update` enabled.
|
||||
* Admin only. Response: [Update]
|
||||
*
|
||||
* 1. `docker compose pull` any Stacks / Deployments with `poll_for_updates` or `auto_update` enabled. This will pick up any available updates.
|
||||
* 2. Redeploy Stacks / Deployments that have updates found.
|
||||
*/
|
||||
export interface GlobalAutoUpdate {
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect the docker container associated with the Deployment.
|
||||
* Response: [Container].
|
||||
@@ -7551,9 +7523,7 @@ export interface RunAction {
|
||||
/** Id or name */
|
||||
action: string;
|
||||
/**
|
||||
* Custom arguments which are merged on top of the default arguments.
|
||||
* CLI Format: `"VAR1=val1&VAR2=val2"`
|
||||
*
|
||||
* Custom arguments which can override the defaults.
|
||||
* Webhook-triggered actions use this to pass WEBHOOK_BRANCH and WEBHOOK_BODY.
|
||||
*/
|
||||
args?: JsonObject;
|
||||
@@ -7736,24 +7706,6 @@ export interface SetUsersInUserGroup {
|
||||
users: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign up a new local user account. Will fail if a user with the
|
||||
* given username already exists.
|
||||
* Response: [SignUpLocalUserResponse].
|
||||
*
|
||||
* Note. This method is only available if the core api has `local_auth` enabled,
|
||||
* and if user registration is not disabled (after the first user).
|
||||
*/
|
||||
export interface SignUpLocalUser {
|
||||
/** The username for the new user. */
|
||||
username: string;
|
||||
/**
|
||||
* The password for the new user.
|
||||
* This cannot be retreived later.
|
||||
*/
|
||||
password: string;
|
||||
}
|
||||
|
||||
/** Info for network interface usage. */
|
||||
export interface SingleNetworkInterfaceUsage {
|
||||
/** The network interface name */
|
||||
@@ -8312,7 +8264,7 @@ export interface WriteSyncFileContents {
|
||||
|
||||
export type AuthRequest =
|
||||
| { type: "GetLoginOptions", params: GetLoginOptions }
|
||||
| { type: "SignUpLocalUser", params: SignUpLocalUser }
|
||||
| { type: "CreateLocalUser", params: CreateLocalUser }
|
||||
| { type: "LoginLocalUser", params: LoginLocalUser }
|
||||
| { type: "ExchangeForJwt", params: ExchangeForJwt }
|
||||
| { type: "GetUser", params: GetUser };
|
||||
@@ -8389,9 +8341,7 @@ export type ExecuteRequest =
|
||||
| { type: "BatchRunAction", params: BatchRunAction }
|
||||
| { type: "TestAlerter", params: TestAlerter }
|
||||
| { type: "RunSync", params: RunSync }
|
||||
| { type: "ClearRepoCache", params: ClearRepoCache }
|
||||
| { type: "BackupCoreDatabase", params: BackupCoreDatabase }
|
||||
| { type: "GlobalAutoUpdate", params: GlobalAutoUpdate };
|
||||
| { type: "ClearRepoCache", params: ClearRepoCache };
|
||||
|
||||
/**
|
||||
* One representative IANA zone for each distinct base UTC offset in the tz database.
|
||||
@@ -8653,7 +8603,6 @@ export type UserRequest =
|
||||
| { type: "DeleteApiKey", params: DeleteApiKey };
|
||||
|
||||
export type WriteRequest =
|
||||
| { type: "CreateLocalUser", params: CreateLocalUser }
|
||||
| { type: "UpdateUserUsername", params: UpdateUserUsername }
|
||||
| { type: "UpdateUserPassword", params: UpdateUserPassword }
|
||||
| { type: "DeleteUser", params: DeleteUser }
|
||||
|
||||
@@ -38,8 +38,6 @@ KOMODO_TITLE=Komodo
|
||||
## Create a server matching this address as the "first server".
|
||||
## Use `https://host.docker.internal:8120` when using systemd-managed Periphery.
|
||||
KOMODO_FIRST_SERVER=https://periphery:8120
|
||||
## Give the first server a custom name.
|
||||
KOMODO_FIRST_SERVER_NAME=Local
|
||||
## Make all buttons just double-click, rather than the full confirmation dialog.
|
||||
KOMODO_DISABLE_CONFIRM_DIALOG=false
|
||||
|
||||
@@ -63,12 +61,6 @@ KOMODO_JWT_TTL="1-day"
|
||||
|
||||
## Enable login with username + password.
|
||||
KOMODO_LOCAL_AUTH=true
|
||||
## Set the initial admin username created upon first launch.
|
||||
## Comment out to disable initial user creation,
|
||||
## and create first user using signup button.
|
||||
KOMODO_INIT_ADMIN_USERNAME=admin
|
||||
## Set the initial admin password
|
||||
KOMODO_INIT_ADMIN_PASSWORD=changeme
|
||||
## Disable new user signups.
|
||||
KOMODO_DISABLE_USER_REGISTRATION=false
|
||||
## All new logins are auto enabled
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.
|
||||
@@ -55,11 +55,6 @@ passkey = "default-passkey-changeme"
|
||||
## Optional, no default.
|
||||
first_server = ""
|
||||
|
||||
## Give the first server a custom name.
|
||||
## Env: KOMODO_FIRST_SERVER_NAME
|
||||
## Default: Local
|
||||
first_server_name = "Local"
|
||||
|
||||
## Disables write support on resources in the UI.
|
||||
## This protects users that that would normally have write priviledges during their UI usage,
|
||||
## when they intend to fully rely on ResourceSyncs to manage config.
|
||||
@@ -79,13 +74,6 @@ disable_confirm_dialog = false
|
||||
## Default: false
|
||||
disable_websocket_reconnect = false
|
||||
|
||||
|
||||
## Disable init system resource creation on fresh Komodo launch.
|
||||
## These include the 'Backup Core Database' and 'Global Auto Update' procedures.
|
||||
## Env: KOMODO_DISABLE_INIT_RESOURCES
|
||||
## Default: false
|
||||
disable_init_resources = false
|
||||
|
||||
## Configure the directory for sync files (inside the container).
|
||||
## There shouldn't be a need to change this, just mount a volume.
|
||||
## Env: KOMODO_SYNC_DIRECTORY
|
||||
@@ -127,16 +115,6 @@ internet_interface = ""
|
||||
## Default: false
|
||||
local_auth = false
|
||||
|
||||
## Initialize the first admin user when starting up Komodo for the first time.
|
||||
## Env: KOMODO_INIT_ADMIN_USERNAME or KOMODO_INIT_ADMIN_USERNAME_FILE
|
||||
## Default: None
|
||||
# init_admin_username = "admin"
|
||||
|
||||
## Set password for first admin user
|
||||
## Env: KOMODO_INIT_ADMIN_PASSWORD or KOMODO_INIT_ADMIN_PASSWORD_FILE
|
||||
## Default: changeme
|
||||
init_admin_password = "changeme"
|
||||
|
||||
## Normally new users will be registered, but not enabled until an Admin enables them.
|
||||
## With `disable_user_registration = true`, only the first user to log in will registered as a user.
|
||||
## Env: KOMODO_DISABLE_USER_REGISTRATION
|
||||
|
||||
@@ -18,13 +18,8 @@ default_profile = "Default"
|
||||
# default_profile = "Alt"
|
||||
|
||||
# Set base values if they aren't defined in profile
|
||||
# Default: 14
|
||||
max_backups = 7
|
||||
|
||||
# Options: HorizontalOnly, VeriticalOnly, OutsideOnly, InsideOnly, AllBorders
|
||||
# Default: HorizontalOnly
|
||||
table_format = "HorizontalOnly"
|
||||
|
||||
[[profile]]
|
||||
name = "Default"
|
||||
aliases = ["d"] # Use `km -p d ...`
|
||||
|
||||
+10
-7
@@ -1,11 +1,5 @@
|
||||
import * as TOML from "jsr:@std/toml";
|
||||
|
||||
const branch = await new Deno.Command("bash", {
|
||||
args: ["-c", "git rev-parse --abbrev-ref HEAD"],
|
||||
})
|
||||
.output()
|
||||
.then((r) => new TextDecoder("utf-8").decode(r.stdout).trim());
|
||||
|
||||
const cargo_toml_str = await Deno.readTextFile("Cargo.toml");
|
||||
const prev_version = (
|
||||
TOML.parse(cargo_toml_str) as {
|
||||
@@ -38,7 +32,16 @@ echo ""
|
||||
git push
|
||||
echo ""
|
||||
|
||||
km run -y action deploy-komodo "KOMODO_BRANCH=${branch}&KOMODO_VERSION=${version}&KOMODO_TAG=${tag}-${next_count}"
|
||||
km set var KOMODO_VERSION ${version} -y
|
||||
echo ""
|
||||
|
||||
km set var KOMODO_TAG ${tag}-${next_count} -y
|
||||
echo ""
|
||||
|
||||
km run -y commit variables
|
||||
echo ""
|
||||
|
||||
km run -y action deploy-komodo
|
||||
`
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Komodo Core exposes an RPC-like HTTP API to read data, write configuration, and execute actions.
|
||||
There are typesafe clients available in
|
||||
[**Rust**](/docs/ecosystem/api#rust-client) and [**Typescript**](/docs/ecosystem/api#typescript-client).
|
||||
[**Rust**](/docs/api#rust-client) and [**Typescript**](/docs/api#typescript-client).
|
||||
|
||||
The full API documentation is [**available here**](https://docs.rs/komodo_client/latest/komodo_client/api/index.html).
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ Komodo just needs a bit of information in order to build your image.
|
||||
### Provider configuration
|
||||
Komodo supports cloning repos over http/s, from any provider that supports cloning private repos using `git clone https://<Token>@git-provider.net/<Owner>/<Repo>`.
|
||||
|
||||
Accounts / access tokens can be configured in either the [core config](../../setup/advanced.mdx#mount-a-config-file)
|
||||
or in the [periphery config](../../setup/connect-servers.mdx#manual-install-steps---binaries).
|
||||
Accounts / access tokens can be configured in either the [core config](../setup/advanced.mdx#mount-a-config-file)
|
||||
or in the [periphery config](../connect-servers.mdx#manual-install-steps---binaries).
|
||||
|
||||
### Repo configuration
|
||||
To specify the git repo to build, just give it the name of the repo and the branch under *repo config*. The name is given like `moghtech/komodo`, it includes the username / organization that owns the repo.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Komodo CLI
|
||||
|
||||
The Komodo CLI, `km`, can be used to:
|
||||
The Komodo CLI (`km`) can be used to:
|
||||
- Quickly **run executions** and update **resources** and **variables**.
|
||||
- **Reset user passwords** and elevate users to **Super Admin**.
|
||||
- Perform Database **backup**, **restore**, and **copy**.
|
||||
@@ -24,11 +24,6 @@ This way, it inherits the Core database config in order to easily perform backup
|
||||
|
||||
### Install
|
||||
|
||||
There are binaries available for **Linux** (x86_64 / aarch64), **MacOS** (apple silicon), as
|
||||
well as a distroless image: **`ghcr.io/moghtech/komodo-cli`**.
|
||||
|
||||
#### Linux
|
||||
|
||||
You can install the binary using the following command:
|
||||
|
||||
System-wide, as root, to `/usr/local/bin/km`:
|
||||
@@ -36,26 +31,13 @@ System-wide, as root, to `/usr/local/bin/km`:
|
||||
curl -sSL https://raw.githubusercontent.com/moghtech/komodo/main/scripts/install-cli.py | python3
|
||||
```
|
||||
|
||||
Or as non-root, to `${HOME}/.local/bin/km`:
|
||||
Or as non-root, to `${HOME}/.local/bin/km`
|
||||
```bash
|
||||
curl -sSL https://raw.githubusercontent.com/moghtech/komodo/main/scripts/install-cli.py | python3 - --user
|
||||
```
|
||||
|
||||
#### MacOS (Homebrew)
|
||||
|
||||
Add the `moghtech/komodo` tap, then install `km`:
|
||||
```bash
|
||||
brew tap moghtech/komodo && \
|
||||
brew install km
|
||||
```
|
||||
|
||||
#### Container
|
||||
|
||||
You can alias a docker run command:
|
||||
```bash
|
||||
alias km='docker run --rm -v $HOME/.config/komodo:/config ghcr.io/moghtech/komodo-cli km'
|
||||
km config
|
||||
```
|
||||
The CLI is also available packaged in into multi-arch docker images.
|
||||
- `ghcr.io/moghtech/komodo-cli`.
|
||||
|
||||
### Configure
|
||||
|
||||
@@ -72,20 +54,6 @@ When run, CLI will scan the **current working directory** in addition to `${HOME
|
||||
parse them into a general representation, and then merge them together. Files which are detected later are merged later,
|
||||
meaning they will override on conflicting fields. By default, files in `${HOME}/.config/komodo` come first
|
||||
in the merge ordering, meaning they are **lower priority** than those detected in the current working directory.
|
||||
You can also override these default paths by passing `km -c /path/to/1/base.config.yaml -c ./overrides ...`.
|
||||
|
||||
If you want `km` to find configuration files in another directory,
|
||||
you can use make `.kminclude` file inside one of the configured directories.
|
||||
|
||||
```
|
||||
# Supports comments
|
||||
|
||||
./.komodo # relative to directory containing `kmignore`
|
||||
|
||||
/etc/komodo/komodo.cli.toml
|
||||
```
|
||||
|
||||
Note that wildcards in these paths are **not supported**.
|
||||
|
||||
#### Profiles
|
||||
|
||||
@@ -115,7 +83,7 @@ import RemoteCodeFile from "@site/src/components/RemoteCodeFile";
|
||||
|
||||
<RemoteCodeFile
|
||||
title="https://github.com/moghtech/komodo/blob/main/config/komodo.cli.toml"
|
||||
url="https://raw.githubusercontent.com/moghtech/komodo/main/config/komodo.cli.toml"
|
||||
url="https://raw.githubusercontent.com/moghtech/komodo/1.19.0/config/komodo.cli.toml"
|
||||
language="toml"
|
||||
/>
|
||||
```
|
||||
@@ -39,11 +39,6 @@ You can find more information (and view the script) in the [readme](https://gith
|
||||
This script can be run multiple times without issue, and it won't change existing config after the first run. Just run it again after a Komodo version release, and it will update the periphery version.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
For deployment to many servers, a tool like [Ansible](https://docs.ansible.com/) should be used.
|
||||
An example of such a setup can be found here: https://github.com/bpbradley/ansible-role-komodo
|
||||
:::
|
||||
|
||||
### Install the Periphery agent - container
|
||||
|
||||
You can use a docker compose file:
|
||||
@@ -22,7 +22,7 @@ Running Komodo from [source](https://github.com/moghtech/komodo) requires either
|
||||
|
||||
## Docker
|
||||
|
||||
After making changes to the project, run `run dev-compose-build` to rebuild Komodo and then `run dev-compose-exposed` to start a Komodo container with the UI accessible at `localhost:9120`. Any changes made to source files will require re-running the `dev-compose-build` and `dev-compose-exposed` commands.
|
||||
After making changes to the project, run `run -r dev-compose-build` to rebuild Komodo and then `run -r dev-compose-exposed` to start a Komodo container with the UI accessible at `localhost:9120`. Any changes made to source files will require re-running the `dev-compose-build` and `dev-compose-exposed` commands.
|
||||
|
||||
## Devcontainer
|
||||
|
||||
@@ -39,17 +39,17 @@ To run a full Komodo instance from a non-container environment run commands in t
|
||||
* Ensure dependencies are up to date
|
||||
* `rustup update` -- ensure rust toolchain is up to date
|
||||
* Build and Run backend
|
||||
* `run dev-core` -- Build and run Core API
|
||||
* `run dev-periphery` -- Build and run Periphery API
|
||||
* `run -r dev-core` -- Build and run Core API
|
||||
* `run -r dev-periphery` -- Build and run Periphery API
|
||||
* Build Frontend
|
||||
* Install **typeshare-cli**: `cargo install typeshare-cli`
|
||||
* **Run this once** -- `run link-client` -- generates TS client and links to the frontend
|
||||
* **Run this once** -- `run -r link-client` -- generates TS client and links to the frontend
|
||||
* After running the above once:
|
||||
* `run gen-client` -- Rebuild client
|
||||
* `run dev-frontend` -- Start in dev (watch) mode
|
||||
* `run build-frontend` -- Typecheck and build
|
||||
* `run -r gen-client` -- Rebuild client
|
||||
* `run -r dev-frontend` -- Start in dev (watch) mode
|
||||
* `run -r build-frontend` -- Typecheck and build
|
||||
|
||||
|
||||
## Docsite Development
|
||||
|
||||
Use `run dev-docsite` to start the [Docusaurus](https://docusaurus.io/) Komodo docs site in development mode. Changes made to files in `./docsite` will be automatically reloaded by the server.
|
||||
Use `run -r dev-docsite` to start the [Docusaurus](https://docusaurus.io/) Komodo docs site in development mode. Changes made to files in `./docsite` will be automatically reloaded by the server.
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
slug: /ecosystem
|
||||
---
|
||||
|
||||
# Ecosystem
|
||||
|
||||
```mdx-code-block
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
|
||||
<DocCardList />
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
# File Paths
|
||||
|
||||
When working with Komodo, you might have to configure file or directory paths.
|
||||
|
||||
## Relative Paths
|
||||
|
||||
Where possible, it is better to use relative file paths. Using relative file paths removes the connection between the process being run and the particular server it runs on, making it easier to move things between servers.
|
||||
|
||||
Where you see relative paths:
|
||||
|
||||
- setting the build directory and path of the Dockerfile
|
||||
- setting a pre build command path
|
||||
|
||||
For all of the above, the path can be given relative to the root of the configured repo
|
||||
|
||||
The one exception is the Dockerfile path, which is given relative to the build directory (This is done by Docker itself, and this pattern matches usage of the Docker CLI).
|
||||
|
||||
There are 3 kinds of paths to pass:
|
||||
|
||||
1. to specify the root of the repo, use `.` as the path
|
||||
2. to specify a folder in the repo, pass it with **no** preceding `/`. For example, `example_folder` or `folder1/folder2`
|
||||
3. to specify an absolute path on the servers filesystem, use a preceding slash, eg. `/home/ubuntu/example`. This way should only be used if absolutely necessary, like when passing host paths when configuring docker volumes.
|
||||
|
||||
### Implementation
|
||||
|
||||
Relative file paths are joined with the path of the repo on the system using a Rust [PathBuf](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push).
|
||||
|
||||
## Docker Volume Paths
|
||||
|
||||
These are passed directly to the Docker CLI using `--volume /path/on/system:/path/in/container`. So for these, the same rules apply as when using Docker on the command line. Paths here should usually be given as absolute. It's also probably best to avoid usage of `~` or environment variables like `$HOME`, as this may lead to unexpected behavior.
|
||||
@@ -43,6 +43,6 @@ Komodo exposes powerful functionality over the Core's REST and Websocket API, en
|
||||
|
||||
## Permissioning
|
||||
|
||||
Komodo is a system designed to be used by many users, whether they are developers, operations personnel, or administrators. The ability to affect an applications state is very powerful, so Komodo has a granular permissioning system to only provide this functionality to the intended users. The permissioning system is explained in detail in the [permissioning](/docs/resources/permissioning) section.
|
||||
Komodo is a system designed to be used by many users, whether they are developers, operations personnel, or administrators. The ability to affect an applications state is very powerful, so Komodo has a granular permissioning system to only provide this functionality to the intended users. The permissioning system is explained in detail in the [permissioning](/docs/permissioning) section.
|
||||
|
||||
User sign-on is possible using username / password, or with Oauth (Github and Google). See [Core Setup](./setup/index.mdx).
|
||||
@@ -1,8 +1,5 @@
|
||||
# Community
|
||||
# Other Resources
|
||||
|
||||
### 3rd party tools
|
||||
- [Ansible Role Komodo](https://github.com/bpbradley/ansible-role-komodo) by [bpbradley](https://github.com/bpbradley)
|
||||
- [Komodo Import](https://foxxmd.github.io/komodo-import/docs/quickstart/) by [FoxxMD](https://github.com/FoxxMD)
|
||||
### Posts and Guides
|
||||
- [Migrating to Komodo](https://blog.foxxmd.dev/posts/migrating-to-komodo) by [FoxxMD](https://github.com/FoxxMD)
|
||||
- [FAQ, Tips, and Tricks](https://blog.foxxmd.dev/posts/komodo-tips-tricks) by [FoxxMD](https://github.com/FoxxMD)
|
||||
@@ -9,7 +9,7 @@ While Komodo can assign permissions to specific users directly, it is recommende
|
||||
Users can then be **added to multiple User Groups** and they **inherit the group's permissions**, similar to linux permissions.
|
||||
There is also an `Everyone` mode for User Groups, if this is enabled then **all users implicitly gain the groups permissions**.
|
||||
|
||||
For permissioning at scale, users can define [**User Groups in Resource Syncs**](/docs/resources/sync-resources#user-group).
|
||||
For permissioning at scale, users can define [**User Groups in Resource Syncs**](/docs/sync-resources#user-group).
|
||||
|
||||
## Permission Levels
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Procedures and Actions
|
||||
|
||||
For orchestrations involving multiple resources and executions,
|
||||
Komodo offers the `Procedure` and `Action` resource types.
|
||||
For orchestrations involving multiple Resources, Komodo offers the `Procedure` and `Action` resource types.
|
||||
|
||||
## Procedures
|
||||
|
||||
@@ -10,18 +10,18 @@ Many resources need access to git repos / docker registries. There is an in-buil
|
||||
All resources which depend on git repos / docker registries are able to use these credentials to access private repos.
|
||||
:::
|
||||
|
||||
## [Server](setup/connect-servers)
|
||||
## Server
|
||||
|
||||
- Configure the connection to periphery agents.
|
||||
- Set alerting thresholds.
|
||||
- Can be attached to by **Deployments**, **Stacks**, **Repos**, and **Builders**.
|
||||
|
||||
## [Deployment](resources/deploy-containers/index.mdx)
|
||||
## Deployment
|
||||
|
||||
- Deploy a docker container on the attached Server.
|
||||
- Manage services at the container level, perform orchestration using **Procedures** and **ResourceSyncs**.
|
||||
|
||||
## [Stack](resources/docker-compose)
|
||||
## Stack
|
||||
|
||||
- Deploy with docker compose.
|
||||
- Provide the compose file in UI, or move the files to a git repo and use a webhook for auto redeploy on push.
|
||||
@@ -33,29 +33,29 @@ All resources which depend on git repos / docker registries are able to use thes
|
||||
- Put scripts in git repos, and run them on a Server, or using a Builder.
|
||||
- Can build binaries, perform automation, really whatever you can think of.
|
||||
|
||||
## [Build](resources/build-images/index.mdx)
|
||||
## Build
|
||||
|
||||
- Build application source into docker images, and push them to the configured registry.
|
||||
- The source can be any git repo containing a Dockerfile.
|
||||
|
||||
## [Builder](resources/build-images/builders)
|
||||
## Builder
|
||||
|
||||
- Either points to a connected server, or holds configuration to launch a single-use AWS instance to build the image.
|
||||
- Can be attached to **Builds** and **Repos**.
|
||||
|
||||
## [Procedure](resources/procedures#procedures)
|
||||
## Procedure
|
||||
|
||||
- Compose many actions on other resource type, like `RunBuild` or `DeployStack`, and run it on button push (or with a webhook).
|
||||
- Can run one or more actions in parallel "stages", and compose a series of parallel stages to run sequentially.
|
||||
|
||||
## [Action](resources/procedures#actions)
|
||||
## Action
|
||||
|
||||
- Write scripts calling the Komodo API in Typescript
|
||||
- Use a pre-initialized Komodo client within the script, no api keys necessary.
|
||||
- Type aware in UI editor. Get suggestions and see in depth docs as you type.
|
||||
- The Typescript client is also [published on NPM](https://www.npmjs.com/package/komodo_client).
|
||||
|
||||
## [Resource Sync](resources/sync-resources)
|
||||
## ResourceSync
|
||||
|
||||
- Orchestrate all your configuration declaratively by defining it in `toml` files, which are checked into a git repo.
|
||||
- Can deploy **Deployments** and **Stacks** if changes are suggested.
|
||||
@@ -1,47 +0,0 @@
|
||||
# Automatic Updates
|
||||
|
||||
Starting from **v1.19.0**, new Komodo installs will automatically create the
|
||||
**Global Auto Update** [Procedure](../resources/procedures#procedures), scheduled daily.
|
||||
If you don't have it, this is the Toml:
|
||||
|
||||
```toml
|
||||
[[procedure]]
|
||||
name = "Global Auto Update"
|
||||
description = "Pulls and auto updates Stacks and Deployments using 'poll_for_updates' or 'auto_update'."
|
||||
tags = ["system"]
|
||||
config.schedule = "Every day at 03:00"
|
||||
|
||||
[[procedure.config.stage]]
|
||||
name = "Stage 1"
|
||||
enabled = true
|
||||
executions = [
|
||||
{ execution.type = "GlobalAutoUpdate", execution.params = {}, enabled = true }
|
||||
]
|
||||
```
|
||||
|
||||
:::info
|
||||
You are also able to integrate `GlobalAutoUpdate` into other Procedures
|
||||
to coordinate the timing with other processes, such as backup. There is
|
||||
nothing special about this Procedure, it's just created by default for
|
||||
guidance / convenience.
|
||||
:::
|
||||
|
||||
### How does it work?
|
||||
|
||||
Both Stacks and Deployments allow you to configure **Poll for Updates** or **Auto Update**.
|
||||
When [**GlobalAutoUpdate**](https://docs.rs/komodo_client/latest/komodo_client/api/execute/struct.GlobalAutoUpdate.html)
|
||||
is run, Komodo will loop through all the resources with either of these options enabled,
|
||||
and run [**PullStack**](https://docs.rs/komodo_client/latest/komodo_client/api/execute/struct.PullStack.html) / [**PullDeployment**](https://docs.rs/komodo_client/latest/komodo_client/api/execute/struct.PullDeployment.html)
|
||||
in order to pick up any newer images **at the same tag**.
|
||||
Note that in order to work, it requires use of a "Rolling" image tag, such as `:latest`.
|
||||
|
||||
:::info
|
||||
If you use git sources Stacks and want to automatically update image tags, check out
|
||||
[Renovate](https://github.com/renovatebot/renovate?tab=readme-ov-file#what-is-the-mend-renovate-cli)
|
||||
:::
|
||||
|
||||
For resources with **Poll for Updates** enabled and an Alerter configured, it will
|
||||
send an alert that a newer image is available, and display the update available indicator in the UI
|
||||
|
||||
For resource with **Auto Update** enabled, it will go ahead and Redeploy *just the services* with
|
||||
newer images (by default). If an Alerter is configured, it will also send an alert that this occured.
|
||||
@@ -1,127 +1,3 @@
|
||||
# Backup and Restore
|
||||
|
||||
:::info
|
||||
Database backup and restore is actually a function of the [Komodo CLI](../ecosystem/cli),
|
||||
which is packaged in with the Komodo Core image for convenience.
|
||||
:::
|
||||
|
||||
Starting from **v1.19.0**, new Komodo installs will automatically create the
|
||||
**Backup Core Database** [Procedure](../resources/procedures#procedures), scheduled daily.
|
||||
If you don't have it, this is the Toml:
|
||||
|
||||
```toml
|
||||
[[procedure]]
|
||||
name = "Backup Core Database"
|
||||
description = "Triggers the Core database backup at the scheduled time."
|
||||
tags = ["system"]
|
||||
config.schedule = "Every day at 01:00"
|
||||
|
||||
[[procedure.config.stage]]
|
||||
name = "Stage 1"
|
||||
enabled = true
|
||||
executions = [
|
||||
{ execution.type = "BackupCoreDatabase", execution.params = {}, enabled = true }
|
||||
]
|
||||
```
|
||||
|
||||
:::info
|
||||
You are also able to integrate `BackupCoreDatabase` into other Procedures, for example to trigger
|
||||
this process before launching a backup container. There is nothing special about this Procedure,
|
||||
it's just created by default for guidance / convenience.
|
||||
:::
|
||||
|
||||
## Backups
|
||||
|
||||
When Komodo takes a database backup, it creates a **folder named for the time the backup was taken**,
|
||||
and dumps the gzip-compressed documents to files in this folder.
|
||||
In order to store the backups to disk, **mount a host path to `/backups`** in the Komodo Core container.
|
||||
|
||||
Due to its larger size and relative unimportance, the `Stats` collection (containing historical server cpu / mem / disk usage)
|
||||
is not included in dated backups. Just latest Stats are maintained at the top level of the backup folder.
|
||||
|
||||
In order to prevent unbounded growth, the backup process implements a pruning feature which will ensure
|
||||
only the most recent 14 backup folders are kept. To change this number, set `max_backups` (`KOMODO_CLI_MAX_BACKUPS`)
|
||||
in `core.config.toml`, `komodo.cli.toml`, or in the Core container environment.
|
||||
|
||||
```
|
||||
# Folder structure
|
||||
/backups
|
||||
| 2025-08-12_03-00-01
|
||||
| | Action.gz
|
||||
| | Alerter.gz
|
||||
| | ...
|
||||
| 2025-08-13_03-00-01
|
||||
| 2025-08-14_03-00-01
|
||||
| ...
|
||||
| Stats.gz
|
||||
```
|
||||
|
||||
:::warning
|
||||
Currently no encryption is supported,
|
||||
so you may want to encrypt the files before backing up remotely if your backup solution doesn't support that natively.
|
||||
:::
|
||||
|
||||
## Remote Backups
|
||||
|
||||
Since database backup is actually a function of the [Komodo CLI](../ecosystem/cli), you can also backup directly to
|
||||
a remote server using the `ghcr.io/moghtech/komodo-cli` image. This service will backup once and then exit, so the scheduled deployment should still happen using a Procedure or Action:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
cli:
|
||||
image: ghcr.io/moghtech/komodo-cli
|
||||
command: km database backup -y
|
||||
volumes:
|
||||
- /path/to/komodo/backups:/backups
|
||||
environment:
|
||||
## Database port must be reachable.
|
||||
KOMODO_DATABASE_ADDRESS: komodo.example.com:27017
|
||||
KOMODO_DATABASE_USERNAME: <db username>
|
||||
KOMODO_DATABASE_PASSWORD: <db password>
|
||||
KOMODO_DATABASE_DB_NAME: komodo
|
||||
KOMODO_CLI_MAX_BACKUPS: 30 # set to your preference
|
||||
```
|
||||
|
||||
## Restore
|
||||
|
||||
The Komodo CLI handles database restores as well.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
cli:
|
||||
image: ghcr.io/moghtech/komodo-cli
|
||||
## Optionally specify a specific folder with `--restore-folder`,
|
||||
## otherwise restores the most recent backup.
|
||||
command: km database restore -y # --restore-folder 2025-08-14_03-00-01
|
||||
volumes:
|
||||
# Same mount to backup files as above
|
||||
- /path/to/komodo/backups:/backups
|
||||
environment:
|
||||
## Database port must be reachable.
|
||||
## Note the different env vars needed compared to backup.
|
||||
## This is to prevent any accidental restores.
|
||||
KOMODO_CLI_DATABASE_TARGET_ADDRESS: komodo.example.com:27017
|
||||
KOMODO_CLI_DATABASE_TARGET_USERNAME: <db username>
|
||||
KOMODO_CLI_DATABASE_TARGET_PASSWORD: <db password>
|
||||
KOMODO_CLI_DATABASE_TARGET_DB_NAME: komodo-restore
|
||||
```
|
||||
|
||||
:::warning
|
||||
The restore process can be run multiple times with same backup files, and won't create any extra copies.
|
||||
HOWEVER it will not "clear" the target database beforehand. If the restore database is already populated,
|
||||
those old documents will also remain. You may want to drop / delete the target database
|
||||
before restoring to it in this case.
|
||||
:::
|
||||
|
||||
## Consistency
|
||||
|
||||
So long as the backup process completes successfully, the files produces can always be restored
|
||||
no matter how active the Komodo instance is at the time of backup. However writes that happen during
|
||||
the backup process, such as updates to the resource configuration, may or may not be included in the backup
|
||||
depending on the timing.
|
||||
|
||||
While it should be rare that this causes any kind of issue when it comes to restoring, if your
|
||||
Komodo undergoes a lot of usage at all hours and you are worried about consistency,
|
||||
you could consider [locking](https://www.mongodb.com/docs/manual/reference/method/db.fsyncLock/#mongodb-method-db.fsyncLock)
|
||||
Mongo before the backup. Just make sure to [unlock](https://www.mongodb.com/docs/manual/reference/method/db.fsyncUnlock/)
|
||||
the database afterwards.
|
||||
The [Komodo CLI](../cli)
|
||||
@@ -14,7 +14,7 @@ wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/fe
|
||||
wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/compose.env
|
||||
```
|
||||
2. Edit the variables in `komodo/compose.env`.
|
||||
3. Deploy using command:
|
||||
3. Deploy:
|
||||
|
||||
```bash
|
||||
docker compose -p komodo -f komodo/ferretdb.compose.yaml --env-file komodo/compose.env up -d
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Setup Komodo Core
|
||||
# Setup Komodo
|
||||
|
||||
To run Komodo, you will need Docker. See [the docker install docs](https://docs.docker.com/engine/install/).
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# MongoDB
|
||||
|
||||
[**MongoDB**](https://www.mongodb.com) is the standard database for Komodo.
|
||||
[**MongoDB**](https://www.mongodb.com) is the traditional database for Komodo.
|
||||
Komodo Core communicates with the database using the MongoDB driver.
|
||||
|
||||
1. Copy `komodo/mongo.compose.yaml` and `komodo/compose.env` to your host:
|
||||
@@ -9,7 +9,7 @@ wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/mo
|
||||
wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/compose.env
|
||||
```
|
||||
2. Edit the variables in `komodo/compose.env`.
|
||||
3. Deploy using command:
|
||||
3. Deploy:
|
||||
|
||||
```bash
|
||||
docker compose -p komodo -f komodo/mongo.compose.yaml --env-file komodo/compose.env up -d
|
||||
|
||||
@@ -6,9 +6,7 @@ A variable / secret in Komodo is just a key-value pair.
|
||||
KEY_1 = "value_1"
|
||||
```
|
||||
|
||||
You can interpolate the value into any resource Environment
|
||||
(and most other user configurable inputs, such as Repo `On Clone` and `On Pull`, or Stack `Extra Args`)
|
||||
using double brackets around the key to trigger interpolation:
|
||||
You can interpolate the value into any Environment (and most other user configurable inputs, such as Repo `On Clone` and `On Pull`, or Stack `Extra Args`) using double brackets around the key to trigger interpolation:
|
||||
|
||||
```toml
|
||||
# Before interpolation
|
||||
@@ -22,7 +20,7 @@ SOME_ENV_VAR = value_1
|
||||
|
||||
- **In the UI**, you can go to `Settings` page, `Variables` tab. Here, you can create some Variables to store in the Komodo database.
|
||||
- There is a "secret" option you can check, this will **prevent the value from exposure in any updates / logs**, as well as prevent access to the value to any **non-admin** Komodo users.
|
||||
- Variables can also be managed in ResourceSyncs (see [example](/docs/resources/sync-resources#deployments)) but should only be done for non-secret variables, to avoid committing sensitive data. You should manage secrets using one of the following options.
|
||||
- Variables can also be managed in ResourceSyncs (see [example](/docs/sync-resources#deployments)) but should only be done for non-secret variables, to avoid committing sensitive data. You should manage secrets using one of the following options.
|
||||
|
||||
- **Mount a config file to Core**: https://komo.do/docs/setup/advanced#mount-a-config-file
|
||||
- In the Komodo Core config file, you can configure `secrets` using a block like:
|
||||
+36
-54
@@ -1,4 +1,4 @@
|
||||
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
|
||||
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
@@ -13,9 +13,10 @@ import type { SidebarsConfig } from "@docusaurus/plugin-content-docs";
|
||||
const sidebars: SidebarsConfig = {
|
||||
docs: [
|
||||
"intro",
|
||||
"resources",
|
||||
{
|
||||
type: "category",
|
||||
label: "Setup",
|
||||
label: "Setup Komodo Core",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "setup/index",
|
||||
@@ -23,69 +24,50 @@ const sidebars: SidebarsConfig = {
|
||||
items: [
|
||||
"setup/mongo",
|
||||
"setup/ferretdb",
|
||||
"setup/connect-servers",
|
||||
"setup/backup",
|
||||
"setup/advanced",
|
||||
"setup/version-upgrades",
|
||||
"setup/backup",
|
||||
],
|
||||
},
|
||||
"connect-servers",
|
||||
{
|
||||
type: "category",
|
||||
label: "Build Images",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "build-images/index",
|
||||
},
|
||||
items: [
|
||||
"build-images/configuration",
|
||||
"build-images/pre-build",
|
||||
"build-images/builders",
|
||||
"build-images/versioning",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Resources",
|
||||
label: "Deploy Containers",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "resources/index",
|
||||
id: "deploy-containers/index",
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: "category",
|
||||
label: "Build Images",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "resources/build-images/index",
|
||||
},
|
||||
items: [
|
||||
"resources/build-images/configuration",
|
||||
"resources/build-images/pre-build",
|
||||
"resources/build-images/builders",
|
||||
"resources/build-images/versioning",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Deploy Containers",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "resources/deploy-containers/index",
|
||||
},
|
||||
items: [
|
||||
"resources/deploy-containers/configuration",
|
||||
"resources/deploy-containers/lifetime-management",
|
||||
],
|
||||
},
|
||||
"resources/docker-compose",
|
||||
"resources/auto-update",
|
||||
"resources/variables",
|
||||
"resources/procedures",
|
||||
"resources/sync-resources",
|
||||
"resources/webhooks",
|
||||
"resources/permissioning",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Ecosystem",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "ecosystem/index",
|
||||
},
|
||||
items: [
|
||||
"ecosystem/cli",
|
||||
"ecosystem/api",
|
||||
"ecosystem/community",
|
||||
"ecosystem/development",
|
||||
"deploy-containers/configuration",
|
||||
"deploy-containers/lifetime-management",
|
||||
// "deploy-containers/choosing-builder",
|
||||
// "deploy-containers/versioning",
|
||||
],
|
||||
},
|
||||
"docker-compose",
|
||||
"variables",
|
||||
"procedures",
|
||||
"permissioning",
|
||||
"sync-resources",
|
||||
"webhooks",
|
||||
"api",
|
||||
"cli",
|
||||
"version-upgrades",
|
||||
"development",
|
||||
"other-resources"
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM rust:1.89.0 as builder
|
||||
FROM rust:1.88.0 as builder
|
||||
WORKDIR /builder
|
||||
|
||||
COPY . .
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM rust:1.89.0 as builder
|
||||
FROM rust:1.88.0 as builder
|
||||
WORKDIR /builder
|
||||
|
||||
COPY . .
|
||||
|
||||
+4
-7
@@ -1,7 +1,7 @@
|
||||
import * as Types from "./types.js";
|
||||
export type AuthResponses = {
|
||||
GetLoginOptions: Types.GetLoginOptionsResponse;
|
||||
SignUpLocalUser: Types.SignUpLocalUserResponse;
|
||||
CreateLocalUser: Types.CreateLocalUserResponse;
|
||||
LoginLocalUser: Types.LoginLocalUserResponse;
|
||||
ExchangeForJwt: Types.ExchangeForJwtResponse;
|
||||
GetUser: Types.GetUserResponse;
|
||||
@@ -134,10 +134,9 @@ export type ReadResponses = {
|
||||
ListDockerRegistryAccounts: Types.ListDockerRegistryAccountsResponse;
|
||||
};
|
||||
export type WriteResponses = {
|
||||
CreateLocalUser: Types.CreateLocalUserResponse;
|
||||
UpdateUserUsername: Types.UpdateUserUsernameResponse;
|
||||
UpdateUserPassword: Types.UpdateUserPasswordResponse;
|
||||
DeleteUser: Types.DeleteUserResponse;
|
||||
UpdateUserUsername: Types.UpdateUserUsername;
|
||||
UpdateUserPassword: Types.UpdateUserPassword;
|
||||
DeleteUser: Types.DeleteUser;
|
||||
CreateServiceUser: Types.CreateServiceUserResponse;
|
||||
UpdateServiceUserDescription: Types.UpdateServiceUserDescriptionResponse;
|
||||
CreateApiKeyForServiceUser: Types.CreateApiKeyForServiceUserResponse;
|
||||
@@ -310,6 +309,4 @@ export type ExecuteResponses = {
|
||||
DestroyStackService: Types.Update;
|
||||
TestAlerter: Types.Update;
|
||||
ClearRepoCache: Types.Update;
|
||||
BackupCoreDatabase: Types.Update;
|
||||
GlobalAutoUpdate: Types.Update;
|
||||
};
|
||||
|
||||
Vendored
+30
-88
@@ -453,8 +453,6 @@ export declare enum Operation {
|
||||
CommitSync = "CommitSync",
|
||||
RunSync = "RunSync",
|
||||
ClearRepoCache = "ClearRepoCache",
|
||||
BackupCoreDatabase = "BackupCoreDatabase",
|
||||
GlobalAutoUpdate = "GlobalAutoUpdate",
|
||||
CreateVariable = "CreateVariable",
|
||||
UpdateVariableValue = "UpdateVariableValue",
|
||||
DeleteVariable = "DeleteVariable",
|
||||
@@ -995,12 +993,6 @@ export type Execution =
|
||||
} | {
|
||||
type: "ClearRepoCache";
|
||||
params: ClearRepoCache;
|
||||
} | {
|
||||
type: "BackupCoreDatabase";
|
||||
params: BackupCoreDatabase;
|
||||
} | {
|
||||
type: "GlobalAutoUpdate";
|
||||
params: GlobalAutoUpdate;
|
||||
} | {
|
||||
type: "Sleep";
|
||||
params: Sleep;
|
||||
@@ -1143,6 +1135,15 @@ export interface GitProviderAccount {
|
||||
token?: string;
|
||||
}
|
||||
export type CreateGitProviderAccountResponse = GitProviderAccount;
|
||||
/** JSON containing an authentication token. */
|
||||
export interface JwtResponse {
|
||||
/** A token the user can use to authenticate their requests. */
|
||||
jwt: string;
|
||||
}
|
||||
/** Response for [CreateLocalUser]. */
|
||||
export type CreateLocalUserResponse = JwtResponse;
|
||||
export type CreateProcedureResponse = Procedure;
|
||||
export type CreateRepoWebhookResponse = NoData;
|
||||
export type UserConfig =
|
||||
/** User that logs in with username / password */
|
||||
{
|
||||
@@ -1211,9 +1212,6 @@ export interface User {
|
||||
all?: Record<ResourceTarget["type"], PermissionLevelAndSpecifics | PermissionLevel>;
|
||||
updated_at?: I64;
|
||||
}
|
||||
export type CreateLocalUserResponse = User;
|
||||
export type CreateProcedureResponse = Procedure;
|
||||
export type CreateRepoWebhookResponse = NoData;
|
||||
export type CreateServiceUserResponse = User;
|
||||
export type CreateStackWebhookResponse = NoData;
|
||||
export type CreateSyncWebhookResponse = NoData;
|
||||
@@ -1429,13 +1427,6 @@ export interface DeploymentQuerySpecifics {
|
||||
update_available?: boolean;
|
||||
}
|
||||
export type DeploymentQuery = ResourceQuery<DeploymentQuerySpecifics>;
|
||||
/** JSON containing an authentication token. */
|
||||
export interface JwtResponse {
|
||||
/** User ID for signed in user. */
|
||||
user_id: string;
|
||||
/** A token the user can use to authenticate their requests. */
|
||||
jwt: string;
|
||||
}
|
||||
/** Response for [ExchangeForJwt]. */
|
||||
export type ExchangeForJwtResponse = JwtResponse;
|
||||
/** Response containing pretty formatted toml contents. */
|
||||
@@ -2075,11 +2066,6 @@ export interface ServerConfig {
|
||||
* Default: http://localhost:8120
|
||||
*/
|
||||
address: string;
|
||||
/**
|
||||
* The address to use with links for containers on the server.
|
||||
* If empty, will use the 'address' for links.
|
||||
*/
|
||||
external_address?: string;
|
||||
/** An optional region label */
|
||||
region?: string;
|
||||
/**
|
||||
@@ -2567,6 +2553,11 @@ export interface Tag {
|
||||
owner?: string;
|
||||
/** Hex color code with alpha for UI display */
|
||||
color?: TagColor;
|
||||
/**
|
||||
* This field is not stored on database,
|
||||
* but rather populated at query time based on results from the other resources.
|
||||
*/
|
||||
unused?: boolean;
|
||||
}
|
||||
export type GetTagResponse = Tag;
|
||||
export type GetUpdateResponse = Update;
|
||||
@@ -3716,11 +3707,6 @@ export interface ServerListItemInfo {
|
||||
region: string;
|
||||
/** Address of the server. */
|
||||
address: string;
|
||||
/**
|
||||
* External address of the server (reachable by users).
|
||||
* Used with links.
|
||||
*/
|
||||
external_address?: string;
|
||||
/** The Komodo Periphery version of the server. */
|
||||
version: string;
|
||||
/** Whether server is configured to send unreachable alerts. */
|
||||
@@ -3894,8 +3880,6 @@ export interface ServerQuerySpecifics {
|
||||
/** Server-specific query */
|
||||
export type ServerQuery = ResourceQuery<ServerQuerySpecifics>;
|
||||
export type SetLastSeenUpdateResponse = NoData;
|
||||
/** Response for [SignUpLocalUser]. */
|
||||
export type SignUpLocalUserResponse = JwtResponse;
|
||||
export interface StackQuerySpecifics {
|
||||
/**
|
||||
* Query only for Stacks on these Servers.
|
||||
@@ -4000,16 +3984,15 @@ export interface AwsBuilderConfig {
|
||||
secrets?: string[];
|
||||
}
|
||||
/**
|
||||
* Backs up the Komodo Core database to compressed jsonl files.
|
||||
* Admin only. Response: [Update]
|
||||
* Backs up the database to compressed jsonl files. Admin only.
|
||||
*
|
||||
* Mount a folder to `/backups`, and Core will use it to create
|
||||
* timestamped database dumps, which can be restored using
|
||||
* the Komodo CLI.
|
||||
* `ghcr.io/moghtech/komodo-util`.
|
||||
*
|
||||
* TODO: Link to docs
|
||||
*/
|
||||
export interface BackupCoreDatabase {
|
||||
export interface BackupDatabase {
|
||||
}
|
||||
/** Builds multiple Repos in parallel that match pattern. Response: [BatchExecutionResponse]. */
|
||||
export interface BatchBuildRepo {
|
||||
@@ -4247,10 +4230,7 @@ export interface CancelRepoBuild {
|
||||
/** Can be id or name */
|
||||
repo: string;
|
||||
}
|
||||
/**
|
||||
* Clears all repos from the Core repo cache. Admin only.
|
||||
* Response: [Update]
|
||||
*/
|
||||
/** Clears all repos from the Core repo cache. Admin only. */
|
||||
export interface ClearRepoCache {
|
||||
}
|
||||
/**
|
||||
@@ -4752,17 +4732,19 @@ export interface CreateGitProviderAccount {
|
||||
account: _PartialGitProviderAccount;
|
||||
}
|
||||
/**
|
||||
* **Admin only.** Create a local user.
|
||||
* Response: [User].
|
||||
* Create a new local user account. Will fail if a user with the
|
||||
* given username already exists.
|
||||
* Response: [CreateLocalUserResponse].
|
||||
*
|
||||
* Note. Not to be confused with /auth/SignUpLocalUser.
|
||||
* This method requires admin user credentials, and can
|
||||
* bypass disabled user registration.
|
||||
* Note. This method is only available if the core api has `local_auth` enabled.
|
||||
*/
|
||||
export interface CreateLocalUser {
|
||||
/** The username for the local user. */
|
||||
/** The username for the new user. */
|
||||
username: string;
|
||||
/** A password for the local user. */
|
||||
/**
|
||||
* The password for the new user.
|
||||
* This cannot be retreived later.
|
||||
*/
|
||||
password: string;
|
||||
}
|
||||
/**
|
||||
@@ -4869,8 +4851,6 @@ export interface CreateSyncWebhook {
|
||||
export interface CreateTag {
|
||||
/** The name of the tag. */
|
||||
name: string;
|
||||
/** Tag color. Default: Slate. */
|
||||
color?: TagColor;
|
||||
}
|
||||
/**
|
||||
* Configures the behavior of [CreateTerminal] if the
|
||||
@@ -6138,16 +6118,6 @@ export interface GetVersionResponse {
|
||||
/** The version of the core api. */
|
||||
version: string;
|
||||
}
|
||||
/**
|
||||
* Trigger a global poll for image updateson Stacks and Deployments
|
||||
* with `poll_for_updates` or `auto_update` enabled.
|
||||
* Admin only. Response: [Update]
|
||||
*
|
||||
* 1. `docker compose pull` any Stacks / Deployments with `poll_for_updates` or `auto_update` enabled. This will pick up any available updates.
|
||||
* 2. Redeploy Stacks / Deployments that have updates found.
|
||||
*/
|
||||
export interface GlobalAutoUpdate {
|
||||
}
|
||||
/**
|
||||
* Inspect the docker container associated with the Deployment.
|
||||
* Response: [Container].
|
||||
@@ -7141,9 +7111,7 @@ export interface RunAction {
|
||||
/** Id or name */
|
||||
action: string;
|
||||
/**
|
||||
* Custom arguments which are merged on top of the default arguments.
|
||||
* CLI Format: `"VAR1=val1&VAR2=val2"`
|
||||
*
|
||||
* Custom arguments which can override the defaults.
|
||||
* Webhook-triggered actions use this to pass WEBHOOK_BRANCH and WEBHOOK_BODY.
|
||||
*/
|
||||
args?: JsonObject;
|
||||
@@ -7312,23 +7280,6 @@ export interface SetUsersInUserGroup {
|
||||
/** The user ids or usernames to hard set as the group's users. */
|
||||
users: string[];
|
||||
}
|
||||
/**
|
||||
* Sign up a new local user account. Will fail if a user with the
|
||||
* given username already exists.
|
||||
* Response: [SignUpLocalUserResponse].
|
||||
*
|
||||
* Note. This method is only available if the core api has `local_auth` enabled,
|
||||
* and if user registration is not disabled (after the first user).
|
||||
*/
|
||||
export interface SignUpLocalUser {
|
||||
/** The username for the new user. */
|
||||
username: string;
|
||||
/**
|
||||
* The password for the new user.
|
||||
* This cannot be retreived later.
|
||||
*/
|
||||
password: string;
|
||||
}
|
||||
/** Info for network interface usage. */
|
||||
export interface SingleNetworkInterfaceUsage {
|
||||
/** The network interface name */
|
||||
@@ -7843,8 +7794,8 @@ export type AuthRequest = {
|
||||
type: "GetLoginOptions";
|
||||
params: GetLoginOptions;
|
||||
} | {
|
||||
type: "SignUpLocalUser";
|
||||
params: SignUpLocalUser;
|
||||
type: "CreateLocalUser";
|
||||
params: CreateLocalUser;
|
||||
} | {
|
||||
type: "LoginLocalUser";
|
||||
params: LoginLocalUser;
|
||||
@@ -8048,12 +7999,6 @@ export type ExecuteRequest = {
|
||||
} | {
|
||||
type: "ClearRepoCache";
|
||||
params: ClearRepoCache;
|
||||
} | {
|
||||
type: "BackupCoreDatabase";
|
||||
params: BackupCoreDatabase;
|
||||
} | {
|
||||
type: "GlobalAutoUpdate";
|
||||
params: GlobalAutoUpdate;
|
||||
};
|
||||
/**
|
||||
* One representative IANA zone for each distinct base UTC offset in the tz database.
|
||||
@@ -8562,9 +8507,6 @@ export type UserRequest = {
|
||||
params: DeleteApiKey;
|
||||
};
|
||||
export type WriteRequest = {
|
||||
type: "CreateLocalUser";
|
||||
params: CreateLocalUser;
|
||||
} | {
|
||||
type: "UpdateUserUsername";
|
||||
params: UpdateUserUsername;
|
||||
} | {
|
||||
|
||||
@@ -167,8 +167,6 @@ export var Operation;
|
||||
Operation["CommitSync"] = "CommitSync";
|
||||
Operation["RunSync"] = "RunSync";
|
||||
Operation["ClearRepoCache"] = "ClearRepoCache";
|
||||
Operation["BackupCoreDatabase"] = "BackupCoreDatabase";
|
||||
Operation["GlobalAutoUpdate"] = "GlobalAutoUpdate";
|
||||
Operation["CreateVariable"] = "CreateVariable";
|
||||
Operation["UpdateVariableValue"] = "UpdateVariableValue";
|
||||
Operation["DeleteVariable"] = "DeleteVariable";
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@ui/dropdown-menu";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { AlertLevel } from ".";
|
||||
import { ResourceLink } from "@components/resources/common";
|
||||
import { UsableResource } from "@types";
|
||||
import { Dialog } from "@ui/dialog";
|
||||
import { Types } from "komodo_client";
|
||||
import { useState } from "react";
|
||||
import { AlertDetailsDialogContent } from "./details";
|
||||
|
||||
export const TopbarAlerts = () => {
|
||||
const { data } = useRead(
|
||||
"ListAlerts",
|
||||
{ query: { resolved: false } },
|
||||
{ refetchInterval: 3_000 }
|
||||
);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// If this is set, details will open.
|
||||
const [alert, setAlert] = useState<Types.Alert>();
|
||||
|
||||
if (!data || data.alerts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger asChild disabled={!data?.alerts.length}>
|
||||
<Button variant="ghost" size="icon" className="relative">
|
||||
<AlertTriangle className="w-4 h-4" />
|
||||
{!!data?.alerts.length && (
|
||||
<div className="absolute top-0 right-0 w-4 h-4 bg-red-500 flex items-center justify-center text-[10px] text-white rounded-full">
|
||||
{data.alerts.length}
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent sideOffset={20}>
|
||||
{data?.alerts.map((alert) => (
|
||||
<DropdownMenuItem
|
||||
key={alert._id?.$oid}
|
||||
className="flex items-center gap-8 border-b last:border-none cursor-pointer"
|
||||
onClick={() => setAlert(alert)}
|
||||
>
|
||||
<div className="w-24">
|
||||
<AlertLevel level={alert.level} />
|
||||
</div>
|
||||
<div className="w-64">
|
||||
<div className="w-fit">
|
||||
<ResourceLink
|
||||
type={alert.target.type as UsableResource}
|
||||
id={alert.target.id}
|
||||
onClick={() => setOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="w-64">{alert.data.type}</p>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<AlertDetails alert={alert} onClose={() => setAlert(undefined)} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const AlertDetails = ({
|
||||
alert,
|
||||
onClose,
|
||||
}: {
|
||||
alert: Types.Alert | undefined;
|
||||
onClose: () => void;
|
||||
}) => (
|
||||
<>
|
||||
{alert && (
|
||||
<Dialog open={!!alert} onOpenChange={(o) => !o && onClose()}>
|
||||
<AlertDetailsDialogContent alert={alert} onClose={onClose} />
|
||||
</Dialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -211,14 +211,12 @@ export const NewLayout = ({
|
||||
enabled,
|
||||
onConfirm,
|
||||
onOpenChange,
|
||||
configureLabel = "a unique name",
|
||||
}: {
|
||||
entityType: string;
|
||||
children: ReactNode;
|
||||
enabled: boolean;
|
||||
onConfirm: () => Promise<unknown>;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
configureLabel?: string;
|
||||
}) => {
|
||||
const [open, set] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -238,9 +236,7 @@ export const NewLayout = ({
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>New {entityType}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Enter {configureLabel} for the new {entityType}.
|
||||
</DialogDescription>
|
||||
<DialogDescription>Enter a unique name for the new {entityType}.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-6 py-8">{children}</div>
|
||||
<DialogFooter>
|
||||
|
||||
@@ -133,14 +133,11 @@ export const ActionConfig = ({ id }: { id: string }) => {
|
||||
</div>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{Object.values(Types.FileFormat)
|
||||
// Don't allow selection of Toml, as this option will break resource sync
|
||||
.filter((f) => f !== Types.FileFormat.Toml)
|
||||
.map((format) => (
|
||||
<SelectItem value={format}>
|
||||
{snake_case_to_upper_space_case(format)}
|
||||
</SelectItem>
|
||||
))}
|
||||
{Object.values(Types.FileFormat).map((format) => (
|
||||
<SelectItem value={format}>
|
||||
{snake_case_to_upper_space_case(format)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user