mirror of
https://github.com/moghtech/komodo.git
synced 2026-08-10 01:29:51 -05:00
Compare commits
547
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a5290e124 | ||
|
|
538e11ef3b | ||
|
|
9208e9bf86 | ||
|
|
31640e3df8 | ||
|
|
42751d1b90 | ||
|
|
673ab5c17d | ||
|
|
ad00f38758 | ||
|
|
b2459b5a64 | ||
|
|
5923dd0e22 | ||
|
|
51294edd73 | ||
|
|
baf77bf701 | ||
|
|
ddd99dc85d | ||
|
|
a4c902282d | ||
|
|
d7a2d4aadc | ||
|
|
c930468811 | ||
|
|
27efc81451 | ||
|
|
e44bd324f7 | ||
|
|
b64c7e7289 | ||
|
|
5c1a17fc91 | ||
|
|
50c3d77aed | ||
|
|
bba1e3ae0c | ||
|
|
fc26fede66 | ||
|
|
9cee723a1a | ||
|
|
b922e86d29 | ||
|
|
85dd3c0fe9 | ||
|
|
4e3ecf7e6c | ||
|
|
18ffe635c8 | ||
|
|
8758c9ff8b | ||
|
|
5da48ece82 | ||
|
|
9a48edc43f | ||
|
|
93430f6dc3 | ||
|
|
d893ae9241 | ||
|
|
24ed3cfcb1 | ||
|
|
36ee7ed72e | ||
|
|
5895fb843e | ||
|
|
98f53020fb | ||
|
|
1d10873e77 | ||
|
|
f5ee42029f | ||
|
|
525402f6f2 | ||
|
|
9f877cc188 | ||
|
|
69d6b8e6ec | ||
|
|
526bcc1907 | ||
|
|
f7badf5579 | ||
|
|
86fd7a155d | ||
|
|
628afc3baa | ||
|
|
04f944fcc2 | ||
|
|
f39447b513 | ||
|
|
612acd02ad | ||
|
|
90557ae514 | ||
|
|
0c292f7157 | ||
|
|
673ee05fe9 | ||
|
|
94b5bd6660 | ||
|
|
be40110004 | ||
|
|
2d4a34b820 | ||
|
|
2feb08725b | ||
|
|
161393805f | ||
|
|
f4b692080d | ||
|
|
e1c6c866a2 | ||
|
|
7106328390 | ||
|
|
338acba5c6 | ||
|
|
dcf6f2431e | ||
|
|
58b520e651 | ||
|
|
8be0846733 | ||
|
|
5b651f2527 | ||
|
|
026ba4fca8 | ||
|
|
4bca07d4ba | ||
|
|
9091f14711 | ||
|
|
8b78bb838e | ||
|
|
d384f7ca78 | ||
|
|
48475b55d5 | ||
|
|
64d7959c75 | ||
|
|
d1856c2325 | ||
|
|
61b7fcdb0a | ||
|
|
2693f13829 | ||
|
|
7fea06861b | ||
|
|
abd155193a | ||
|
|
7a4de0343d | ||
|
|
c638e3876e | ||
|
|
fc4ea40b9c | ||
|
|
62b84328d8 | ||
|
|
a5ee0de394 | ||
|
|
bf0ab26d22 | ||
|
|
0dd2ade574 | ||
|
|
f0172c2721 | ||
|
|
363d330868 | ||
|
|
2843711796 | ||
|
|
4f044ccde8 | ||
|
|
9be21b54bd | ||
|
|
057ecbcfef | ||
|
|
e91d63f262 | ||
|
|
3b799edb30 | ||
|
|
41c7faf48d | ||
|
|
e1d745aee0 | ||
|
|
01df3add41 | ||
|
|
577973841b | ||
|
|
3e5311f413 | ||
|
|
46e99783f2 | ||
|
|
a6590804a7 | ||
|
|
bd752b7c72 | ||
|
|
de83b3a5a0 | ||
|
|
c91963fdbb | ||
|
|
973aae82fd | ||
|
|
c3626e0949 | ||
|
|
5ba96a58e4 | ||
|
|
c6a1cf2867 | ||
|
|
bd39aec1fd | ||
|
|
079895a64d | ||
|
|
5af1c646a5 | ||
|
|
747e03b5c4 | ||
|
|
78c6761eca | ||
|
|
a949891d26 | ||
|
|
9d256e420a | ||
|
|
b460504b5a | ||
|
|
e60316f2c1 | ||
|
|
c45e3eab16 | ||
|
|
ec12f63272 | ||
|
|
f2f77b4eb6 | ||
|
|
677a65f8b0 | ||
|
|
a5a1090177 | ||
|
|
ccb18fc88a | ||
|
|
0ca583fe5e | ||
|
|
4013cd3f1e | ||
|
|
5854a7de99 | ||
|
|
8c52232852 | ||
|
|
fb9e5b1860 | ||
|
|
a1c00a8d32 | ||
|
|
775f5bf703 | ||
|
|
effe737ffe | ||
|
|
436959cbcd | ||
|
|
9957287443 | ||
|
|
c77de8683c | ||
|
|
087bcb7044 | ||
|
|
0dfcaaf06a | ||
|
|
6624a821a5 | ||
|
|
c32af84e02 | ||
|
|
9d9add7b34 | ||
|
|
db099dbe1e | ||
|
|
413a8abc84 | ||
|
|
36243e83c1 | ||
|
|
076113a1de | ||
|
|
c998edaa73 | ||
|
|
4de32b08e5 | ||
|
|
262999c58f | ||
|
|
be57f52e6e | ||
|
|
3591789316 | ||
|
|
83f2bd65fe | ||
|
|
5e5cdb81f8 | ||
|
|
721038a1df | ||
|
|
7de98ad519 | ||
|
|
8c62f2b5c5 | ||
|
|
85787781ee | ||
|
|
25e2d4e926 | ||
|
|
b9b8d45cbc | ||
|
|
2f2e863dbf | ||
|
|
a3a01f1625 | ||
|
|
aec8fa2bf1 | ||
|
|
7ff2dba82f | ||
|
|
9c86b2d239 | ||
|
|
b1fec7c663 | ||
|
|
8341c6b802 | ||
|
|
73285c4374 | ||
|
|
32d48cdb02 | ||
|
|
8e081fd09c | ||
|
|
04531f1dea | ||
|
|
80f439d472 | ||
|
|
d5e03d6d16 | ||
|
|
a9f55bb8e6 | ||
|
|
9e765f93f5 | ||
|
|
b3aa8e906f | ||
|
|
03fe442aa0 | ||
|
|
d268009a6a | ||
|
|
f0697e812a | ||
|
|
78766463d6 | ||
|
|
0fa1edba2c | ||
|
|
bbd968cac3 | ||
|
|
5f24fc1be3 | ||
|
|
7ecd2b0b0b | ||
|
|
7bf44d2e04 | ||
|
|
24e0672384 | ||
|
|
04f081631f | ||
|
|
b1af956b63 | ||
|
|
370712b29f | ||
|
|
2b6c552964 | ||
|
|
434a1d8ea9 | ||
|
|
0b7f28360f | ||
|
|
3c8ef0ab29 | ||
|
|
930b2423c3 | ||
|
|
546747b5f2 | ||
|
|
c6df866755 | ||
|
|
ea5e684915 | ||
|
|
64db8933de | ||
|
|
7a5580de57 | ||
|
|
b1656bb174 | ||
|
|
559ce103da | ||
|
|
75e278a57b | ||
|
|
430f3ddc34 | ||
|
|
6c30c202e9 | ||
|
|
c5401de1c5 | ||
|
|
7a3d9e0ef6 | ||
|
|
595e3ece42 | ||
|
|
a3bc895755 | ||
|
|
3e3def03ec | ||
|
|
bc672d9649 | ||
|
|
ea6dee4d51 | ||
|
|
b985f18c74 | ||
|
|
45909b2f04 | ||
|
|
2b5a54ce89 | ||
|
|
a18f33b95e | ||
|
|
f35b00ea95 | ||
|
|
70fab08520 | ||
|
|
0331780a5f | ||
|
|
06cdfd2bbc | ||
|
|
1555202569 | ||
|
|
5139622aad | ||
|
|
61ce2ee3db | ||
|
|
3171c14f2b | ||
|
|
521db748d8 | ||
|
|
35bf224080 | ||
|
|
e0b31cfe51 | ||
|
|
0a890078b0 | ||
|
|
df97ced7a4 | ||
|
|
d4e5e2e6d8 | ||
|
|
19aa60dcb5 | ||
|
|
fc19c53e6f | ||
|
|
4f0af960db | ||
|
|
e2ec5258fb | ||
|
|
49b6545a02 | ||
|
|
0aabaa9e62 | ||
|
|
dc65986eab | ||
|
|
1d8f28437d | ||
|
|
c1502e89c2 | ||
|
|
0bd15fc442 | ||
|
|
5a3621b02e | ||
|
|
38192e2dac | ||
|
|
5d271d5547 | ||
|
|
11fb67a35b | ||
|
|
a80499dcc4 | ||
|
|
8c76b8487f | ||
|
|
2b32d9042a | ||
|
|
dc48f1f2ca | ||
|
|
8e7b7bdcf1 | ||
|
|
f11d64f72e | ||
|
|
2ffae85180 | ||
|
|
bd79d0f1e0 | ||
|
|
e890b1f675 | ||
|
|
3b7de25c30 | ||
|
|
793bb99f31 | ||
|
|
d465c9f273 | ||
|
|
ce641a8974 | ||
|
|
1b89ceb122 | ||
|
|
2dbc011d26 | ||
|
|
246da88ae1 | ||
|
|
a8c16f64b1 | ||
|
|
a5b711a348 | ||
|
|
9666e9ad83 | ||
|
|
7479640c73 | ||
|
|
4823825035 | ||
|
|
23897a7acf | ||
|
|
20d5588b5c | ||
|
|
f7e15ccde5 | ||
|
|
cf7623b1fc | ||
|
|
d3c464c05d | ||
|
|
5c9d416aa4 | ||
|
|
aabcd88312 | ||
|
|
9d2624c6bc | ||
|
|
ee11fb0b6c | ||
|
|
45adfbddd0 | ||
|
|
d26d035dc6 | ||
|
|
e673ba0adf | ||
|
|
f876facfa7 | ||
|
|
3a47d57478 | ||
|
|
a707028277 | ||
|
|
0c6276c677 | ||
|
|
fc9c6706f1 | ||
|
|
7674269ce9 | ||
|
|
3b511c5adc | ||
|
|
87221a10e9 | ||
|
|
450cb6a148 | ||
|
|
f252cefb21 | ||
|
|
7855e9d688 | ||
|
|
feb263c15f | ||
|
|
4f8d1c22cc | ||
|
|
60bd47834e | ||
|
|
4d632a6b61 | ||
|
|
381dd76723 | ||
|
|
077e28a5fe | ||
|
|
6b02aaed7d | ||
|
|
e466944c05 | ||
|
|
8ff94b7465 | ||
|
|
b17df5ed7b | ||
|
|
207dc30206 | ||
|
|
c3eb386bdb | ||
|
|
4279e46892 | ||
|
|
8d3d2fee12 | ||
|
|
1df36c4266 | ||
|
|
36f7ad33c7 | ||
|
|
ec34b2c139 | ||
|
|
d14c28d1f2 | ||
|
|
68f7a0e9ce | ||
|
|
50f0376f0a | ||
|
|
bbd53747ad | ||
|
|
6a2adf1f83 | ||
|
|
128b15b94f | ||
|
|
8d74b377b7 | ||
|
|
d7e972e5c6 | ||
|
|
e5cb4aac5a | ||
|
|
d0f62f8326 | ||
|
|
47c4091a4b | ||
|
|
973480e2b3 | ||
|
|
b9e1cc87d2 | ||
|
|
05d20c8603 | ||
|
|
fe2d68a001 | ||
|
|
26fd5b2a6d | ||
|
|
76457bcb61 | ||
|
|
ebd2c2238d | ||
|
|
b7fc1bef7b | ||
|
|
50b9f2e1bf | ||
|
|
41ce86f6ab | ||
|
|
7a21c01e52 | ||
|
|
e63e282510 | ||
|
|
5456b36c18 | ||
|
|
fcfb58a7e9 | ||
|
|
2203004a74 | ||
|
|
996fb49823 | ||
|
|
35d22c77a2 | ||
|
|
44ab89600f | ||
|
|
0900e48cb8 | ||
|
|
c530a46a27 | ||
|
|
f69c8db3ea | ||
|
|
48f2f651e1 | ||
|
|
bdb5b4185e | ||
|
|
42a7b8c19b | ||
|
|
ded17e4840 | ||
|
|
80fb1e6889 | ||
|
|
1dc861f538 | ||
|
|
3da63395fd | ||
|
|
c40cbc4d77 | ||
|
|
05e352e88c | ||
|
|
5884c09fb8 | ||
|
|
f8add38043 | ||
|
|
501f734e8b | ||
|
|
de62732ac8 | ||
|
|
bfa61058cd | ||
|
|
72ca6d9910 | ||
|
|
4d1ac32ad3 | ||
|
|
927e5959fa | ||
|
|
37ccc6e1ef | ||
|
|
deaa8754f3 | ||
|
|
dd8ac67c72 | ||
|
|
be4457c9cf | ||
|
|
1868421815 | ||
|
|
366f7a12b4 | ||
|
|
75119370df | ||
|
|
9e85b9d4c8 | ||
|
|
8afbbf23dc | ||
|
|
770a1116a1 | ||
|
|
0b4aebbc24 | ||
|
|
f1696e26e4 | ||
|
|
1a7b682301 | ||
|
|
b0110b05aa | ||
|
|
561b490f26 | ||
|
|
cac1f0b42e | ||
|
|
28886fb304 | ||
|
|
fb84d4cf7d | ||
|
|
31e9624556 | ||
|
|
3864bb7115 | ||
|
|
cea8601246 | ||
|
|
a546364bf3 | ||
|
|
c8c62ea562 | ||
|
|
845e8780c7 | ||
|
|
db60347566 | ||
|
|
c3ea0239d6 | ||
|
|
e9d13449bf | ||
|
|
2daa92a639 | ||
|
|
6473080078 | ||
|
|
d3957f65dc | ||
|
|
cb34969f1e | ||
|
|
55a0a8cd05 | ||
|
|
89f08372c6 | ||
|
|
6a3ce2d426 | ||
|
|
4928378d46 | ||
|
|
eea222cfba | ||
|
|
6e9cc2dc77 | ||
|
|
55d45084d0 | ||
|
|
9657a44049 | ||
|
|
51fa9ae3c2 | ||
|
|
5fd256444e | ||
|
|
059716f178 | ||
|
|
0bee1fe2c5 | ||
|
|
1e58c1a958 | ||
|
|
ed1431db0a | ||
|
|
dc769ff159 | ||
|
|
098f23ac4c | ||
|
|
03f577d22f | ||
|
|
95ca217362 | ||
|
|
6d61045764 | ||
|
|
34e075eaf3 | ||
|
|
232dc0bb4e | ||
|
|
0cc0ee2aab | ||
|
|
edebe925ff | ||
|
|
5fd45bbc7b | ||
|
|
0a490dadb2 | ||
|
|
23847c15bc | ||
|
|
0d238aee4f | ||
|
|
98ad6cf5fa | ||
|
|
e35b81630b | ||
|
|
1215852fe4 | ||
|
|
4164b76ff5 | ||
|
|
26a9daffeb | ||
|
|
8bb9f16e9b | ||
|
|
b6eaf76497 | ||
|
|
073893da0e | ||
|
|
e71547f1c2 | ||
|
|
1991627990 | ||
|
|
3434d827a3 | ||
|
|
1ef8b9878a | ||
|
|
07ddaa8377 | ||
|
|
142c08cde4 | ||
|
|
1aa1422faa | ||
|
|
1394e8a6b1 | ||
|
|
420ee10211 | ||
|
|
e918461dc5 | ||
|
|
4dc9ca27be | ||
|
|
f49b186f2f | ||
|
|
6e039b41f1 | ||
|
|
e7cd77b022 | ||
|
|
556cbd04c7 | ||
|
|
4e3d181466 | ||
|
|
5d4326f46f | ||
|
|
4bb486ad0a | ||
|
|
d29c5112d8 | ||
|
|
d41315b8a4 | ||
|
|
847404388c | ||
|
|
eef8ec59b8 | ||
|
|
9eb32f9ff5 | ||
|
|
859bfe67ef | ||
|
|
21ea469cd4 | ||
|
|
7fb902b892 | ||
|
|
c9c4ac47ee | ||
|
|
f228cd31f3 | ||
|
|
4feecb4b97 | ||
|
|
e2680d0942 | ||
|
|
7422c0730d | ||
|
|
37ac0dc7e3 | ||
|
|
dccaca1df4 | ||
|
|
886aea4c36 | ||
|
|
cbca070bae | ||
|
|
b4bdd401f6 | ||
|
|
e546166240 | ||
|
|
21689ce0ad | ||
|
|
941787db64 | ||
|
|
d4b1aacac3 | ||
|
|
30f89461bf | ||
|
|
a42d1397e9 | ||
|
|
b29313c28f | ||
|
|
08a246a90c | ||
|
|
1a08df28d0 | ||
|
|
a226ffc256 | ||
|
|
b385ee5ec3 | ||
|
|
c78c34357d | ||
|
|
4b7c692f00 | ||
|
|
1ac98a096e | ||
|
|
281a2dc1ce | ||
|
|
0fe91378a6 | ||
|
|
11e76d1cf2 | ||
|
|
a3bcd71105 | ||
|
|
3ecc56dd76 | ||
|
|
7239cbb19b | ||
|
|
a0540f7011 | ||
|
|
37aea7605e | ||
|
|
78be913541 | ||
|
|
c34f5ebf49 | ||
|
|
e5822cefb8 | ||
|
|
4baab194cf | ||
|
|
a896583da6 | ||
|
|
7b2674c38b | ||
|
|
d1e32989e3 | ||
|
|
e802bb3882 | ||
|
|
27a38b1bf5 | ||
|
|
2bc8a754be | ||
|
|
7a2a54bec1 | ||
|
|
6a15150d59 | ||
|
|
1b1dca76da | ||
|
|
a032f0f4ff | ||
|
|
2749d49435 | ||
|
|
d88e42ef2d | ||
|
|
a370e7d121 | ||
|
|
d139ad2b3d | ||
|
|
8d2d180398 | ||
|
|
37ca4ca986 | ||
|
|
33e73b8543 | ||
|
|
cf6e36e90c | ||
|
|
9eb8b32f4a | ||
|
|
b400add6f1 | ||
|
|
24adb89d25 | ||
|
|
4674b2badb | ||
|
|
65d1a69cb9 | ||
|
|
0da5718991 | ||
|
|
6b26cd120c | ||
|
|
28e1bb19a4 | ||
|
|
166107ac07 | ||
|
|
d77201880f | ||
|
|
1d7629e9b2 | ||
|
|
198f690ca5 | ||
|
|
531c79a144 | ||
|
|
d685862713 | ||
|
|
af0f245b5b | ||
|
|
cba36861b7 | ||
|
|
2c2c1d47b4 | ||
|
|
3a6b997241 | ||
|
|
7122f79b9d | ||
|
|
9bcee8122b | ||
|
|
a49c98946e | ||
|
|
7d222a7241 | ||
|
|
33501dac3e | ||
|
|
4675dfa736 | ||
|
|
0be51dc784 | ||
|
|
52453d1320 | ||
|
|
25da97ac1a | ||
|
|
02db5a11d3 | ||
|
|
89a5272246 | ||
|
|
ae51ea1ad6 | ||
|
|
3bdb4bea16 | ||
|
|
677bb14b5d | ||
|
|
6700700a80 | ||
|
|
996d4aa129 | ||
|
|
75894a7282 | ||
|
|
2a065edcf1 | ||
|
|
6f3703acfb | ||
|
|
59e989ecdf | ||
|
|
951ff34a9e | ||
|
|
2d83105500 | ||
|
|
3d455f5142 | ||
|
|
01de8c4a9b | ||
|
|
d5de338561 | ||
|
|
58c1afb8ef | ||
|
|
230f357b5a | ||
|
|
991c95fff0 | ||
|
|
f6243fe6b1 | ||
|
|
9feeccba6e | ||
|
|
673c7f3a6b | ||
|
|
39f900d651 | ||
|
|
8a06a0d6ce | ||
|
|
7789ee4f4a | ||
|
|
0472b6a7f7 | ||
|
|
d1d2227d36 | ||
|
|
cea7c5fc5e |
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
dev:
|
||||
image: mcr.microsoft.com/devcontainers/rust:1-bookworm
|
||||
image: mcr.microsoft.com/devcontainers/rust:1-1-bullseye
|
||||
volumes:
|
||||
# Mount the root folder that contains .git
|
||||
- ../:/workspace:cached
|
||||
@@ -10,19 +10,13 @@ services:
|
||||
- stacks:/etc/komodo/stacks
|
||||
command: sleep infinity
|
||||
ports:
|
||||
- "9120:9120"
|
||||
- "9121:9121"
|
||||
environment:
|
||||
KOMODO_HOST: http://localhost:9120
|
||||
KOMODO_FIRST_SERVER: http://localhost:8120
|
||||
KOMODO_DATABASE_ADDRESS: db
|
||||
KOMODO_ENABLE_NEW_USERS: true
|
||||
KOMODO_LOCAL_AUTH: true
|
||||
KOMODO_JWT_SECRET: a_random_secret
|
||||
VITE_KOMODO_HOST: http://localhost:9120
|
||||
KOMODO_CORS_ALLOWED_ORIGINS: http://localhost:5173
|
||||
KOMODO_CORS_ALLOW_CREDENTIALS: true
|
||||
PERIPHERY_SSL_ENABLED: false
|
||||
KOMODO_SESSION_ALLOW_CROSS_SITE: true
|
||||
links:
|
||||
- db
|
||||
# ...
|
||||
@@ -36,4 +30,4 @@ volumes:
|
||||
data:
|
||||
repo-cache:
|
||||
repos:
|
||||
stacks:
|
||||
stacks:
|
||||
@@ -10,7 +10,7 @@
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "22.12.0"
|
||||
"version": "20.12.2"
|
||||
},
|
||||
"ghcr.io/devcontainers-community/features/deno:1": {
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
"forwardPorts": [
|
||||
5173,
|
||||
9120
|
||||
9121
|
||||
],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
@@ -37,16 +36,11 @@
|
||||
|
||||
"runServices": [
|
||||
"db"
|
||||
],
|
||||
]
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
|
||||
"remoteEnv": {
|
||||
// Avoid out of memory error when running `yarn build`
|
||||
"NODE_OPTIONS": "--max-old-space-size=4096"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
cargo install typeshare-cli
|
||||
|
||||
sudo mkdir -p /etc/komodo/keys
|
||||
sudo chown -R $(whoami) /etc/komodo
|
||||
|
||||
sudo mkdir -p /config/keys
|
||||
sudo chown -R $(whoami) /config
|
||||
cargo install typeshare-cli
|
||||
@@ -0,0 +1,123 @@
|
||||
# Komodo AI Coding Instructions
|
||||
|
||||
## Project Overview
|
||||
Komodo is a distributed system for building and deploying software across multiple servers. It consists of:
|
||||
- **Core** (`bin/core`): Central Rust/Axum API server managing resources, scheduling, monitoring
|
||||
- **Periphery** (`bin/periphery`): Rust agent deployed to managed servers, executes docker/git operations
|
||||
- **Frontend** (`frontend`): React/TypeScript SPA using TanStack Query and shadcn/ui components
|
||||
- **Client Libraries** (`client/core/{rs,ts}`): Type-safe API clients generated from shared entity definitions
|
||||
|
||||
## Architecture Fundamentals
|
||||
|
||||
### Core ↔ Periphery Communication
|
||||
- Core and Periphery communicate via **WebSocket connections** that can be initiated from either direction
|
||||
- Periphery can connect outbound to Core (common) or Core can connect inbound to Periphery (less common)
|
||||
- Connection management is in `bin/core/src/connection` and `bin/periphery/src/connection`
|
||||
- Use `state::periphery_connections()` in Core to send requests to Periphery agents
|
||||
|
||||
### Type Generation Pipeline
|
||||
**Critical**: Rust entities are the source of truth for all types across the stack:
|
||||
1. Entities defined in `client/core/rs/src/entities/` with `#[typeshare]` macro
|
||||
2. Run `node ./client/core/ts/generate_types.mjs` (uses typeshare CLI) → generates `client/core/ts/src/types.ts`
|
||||
3. TypeScript client is built and linked to frontend via `yarn link komodo_client`
|
||||
4. Frontend imports types: `import { Types } from "komodo_client"`
|
||||
|
||||
**When modifying entities**: Always run the `gen-client` (alias: `gc`) runfile task to regenerate TypeScript types.
|
||||
|
||||
### API Pattern (Resolver-Based)
|
||||
Core and Periphery use `mogh_resolver::Resolve` trait for RPC-style endpoints:
|
||||
- Request enums in `client/*/rs/src/api/` implement `Resolve<Response = T, Error = E>`
|
||||
- Core handlers in `bin/core/src/api/{read,write,execute}/*.rs`
|
||||
- Periphery handlers in `bin/periphery/src/api/*.rs`
|
||||
- Router maps `/{variant}` paths to enum variants automatically
|
||||
|
||||
Example request handling:
|
||||
```rust
|
||||
#[derive(Serialize, Deserialize, Resolve)]
|
||||
#[response(MyResponse)]
|
||||
#[error(anyhow::Error)]
|
||||
pub struct MyRequest { /* fields */ }
|
||||
```
|
||||
|
||||
### State Management
|
||||
Core maintains singleton state via `OnceLock` patterns in `bin/core/src/state.rs`:
|
||||
- `db_client()`: MongoDB connection
|
||||
- `periphery_connections()`: WebSocket connections to Periphery agents
|
||||
- `action_states()`, `build_states()`, etc.: In-memory caches using `CloneCache<K, V>`
|
||||
- Always use `state::*()` accessors, never instantiate directly
|
||||
|
||||
## Development Workflows
|
||||
|
||||
### Running Services Locally
|
||||
```bash
|
||||
# Start all local services (MongoDB, Core, Periphery, Frontend)
|
||||
run dc # dev-core: runs Core in release mode with .dev/core.config.toml
|
||||
run dp # dev-periphery: runs Periphery with .dev/periphery.config.toml
|
||||
|
||||
# Or use docker-compose for full stack
|
||||
run dev-compose # deploys dev.compose.yaml with ferretdb
|
||||
```
|
||||
|
||||
### Frontend Development
|
||||
```bash
|
||||
cd frontend && yarn dev # Vite dev server on port 5173
|
||||
|
||||
# After changing Rust entities, regenerate client:
|
||||
run gc # gen-client: typeshare → build TS client → copy to frontend/public/client
|
||||
```
|
||||
|
||||
### Building & Deploying
|
||||
- Deno scripts in `action/` automate version bumps and deployments
|
||||
- `run dk` (deploy-komodo): Full build and deploy via Komodo Action
|
||||
- `run bk` (build-komodo): Build binaries without deploying
|
||||
|
||||
## Code Conventions
|
||||
|
||||
### Error Handling
|
||||
- Use `anyhow::Result<T>` for application code
|
||||
- Use `serror` for serializable errors sent over API boundaries
|
||||
- Response helper: `response::Response::from(value)` for JSON responses
|
||||
- Never use `unwrap()` in production code paths; prefer `context()` for error messages
|
||||
|
||||
### Naming & Structure
|
||||
- **Entities**: CamelCase structs in `client/core/rs/src/entities/*/mod.rs`
|
||||
- **API requests**: CamelCase request types in `client/core/rs/src/api/*.rs`
|
||||
- **Config derivations**: Use `derive_builder::Builder` for config update structs
|
||||
- **Variants**: `derive_variants::EnumVariants` generates `TypeVariant` enums for discriminants
|
||||
|
||||
### Frontend Patterns
|
||||
- **API hooks**: `useRead("GetStack", { stack: "my-stack" })` for queries
|
||||
- **Mutations**: `useWrite("UpdateStack", { onSuccess: () => invalidate(...) })`
|
||||
- **Components**: Located in `frontend/src/components/`, use shadcn/ui primitives from `@ui/*`
|
||||
- **State**: Prefer TanStack Query for server state, Jotai atoms for local UI state
|
||||
|
||||
## Key Files & Directories
|
||||
|
||||
### Must-Read for Context
|
||||
- [readme.md](readme.md): Project overview and deployment info
|
||||
- [runfile.toml](runfile.toml): All development task definitions
|
||||
- [bin/core/src/state.rs](bin/core/src/state.rs): Core's global state singletons
|
||||
- [client/core/rs/src/entities/mod.rs](client/core/rs/src/entities/mod.rs): All entity definitions
|
||||
|
||||
### Common Touch Points
|
||||
- **Adding new resource type**: Update entities, Core API handlers, Frontend pages/components
|
||||
- **Changing API**: Modify request/response types in `client/core/rs/src/api/`, regenerate client
|
||||
- **Frontend features**: Usually start in `frontend/src/pages/`, add hooks in `frontend/src/lib/hooks/`
|
||||
- **Periphery capabilities**: Add to `periphery_client::api` enum and implement handler in `bin/periphery/src/api/`
|
||||
|
||||
## Configuration
|
||||
- Core config: `config/core.config.toml` (see `client/core/rs/src/entities/config/core.rs` for schema)
|
||||
- Periphery config: `config/periphery.config.toml`
|
||||
- Environment variable pattern: Settings support `_FILE` suffix for Docker secrets (see `lib/environment_file`)
|
||||
|
||||
## Testing & Validation
|
||||
- No comprehensive test suite currently
|
||||
- Validate changes by running locally with `run dc` / `run dp`
|
||||
- Check Core logs for startup errors (connection to DB, Periphery agents)
|
||||
- Frontend errors appear in browser console and TanStack Query devtools
|
||||
|
||||
## Common Pitfalls
|
||||
- **Forgetting to regenerate TS types** after Rust entity changes → frontend type errors
|
||||
- **Not using `state::*()` accessors** → panics from uninitialized `OnceLock`
|
||||
- **Breaking WebSocket message format** between Core/Periphery → connection failures
|
||||
- **Yarn link issues**: If frontend can't find `komodo_client`, re-run `run link-client`
|
||||
@@ -1,56 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build & Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
# - name: Cache cargo registry
|
||||
# uses: actions/cache@v4
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.cargo/registry
|
||||
# ~/.cargo/git
|
||||
# target
|
||||
# key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-cargo-
|
||||
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
|
||||
fmt:
|
||||
name: Format
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
Vendored
+14
-14
@@ -106,62 +106,62 @@
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Init UI Client",
|
||||
"label": "Init Frontend Client",
|
||||
"type": "shell",
|
||||
"command": "yarn link komodo_client && yarn install",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/ui",
|
||||
"cwd": "${workspaceFolder}/frontend",
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Init UI",
|
||||
"label": "Init Frontend",
|
||||
"dependsOn": [
|
||||
"Build TS Client Types",
|
||||
"Init TS Client",
|
||||
"Init UI Client"
|
||||
"Init Frontend Client"
|
||||
],
|
||||
"dependsOrder": "sequence",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Build UI",
|
||||
"label": "Build Frontend",
|
||||
"type": "shell",
|
||||
"command": "yarn build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/ui",
|
||||
"cwd": "${workspaceFolder}/frontend",
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Prepare UI For Run",
|
||||
"label": "Prepare Frontend For Run",
|
||||
"type": "shell",
|
||||
"command": "cp -r ./client/core/ts/dist/. ui/public/client/.",
|
||||
"command": "cp -r ./client/core/ts/dist/. frontend/public/client/.",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}",
|
||||
},
|
||||
"dependsOn": [
|
||||
"Build TS Client Types",
|
||||
"Build UI"
|
||||
"Build Frontend"
|
||||
],
|
||||
"dependsOrder": "sequence",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Run UI",
|
||||
"label": "Run Frontend",
|
||||
"type": "shell",
|
||||
"command": "yarn dev",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/ui",
|
||||
"cwd": "${workspaceFolder}/frontend",
|
||||
},
|
||||
"dependsOn": ["Prepare UI For Run"],
|
||||
"dependsOn": ["Prepare Frontend For Run"],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Init",
|
||||
"dependsOn": [
|
||||
"Build Backend",
|
||||
"Init UI"
|
||||
"Init Frontend"
|
||||
],
|
||||
"dependsOrder": "sequence",
|
||||
"problemMatcher": []
|
||||
@@ -171,7 +171,7 @@
|
||||
"dependsOn": [
|
||||
"Run Core",
|
||||
"Run Periphery",
|
||||
"Run UI"
|
||||
"Run Frontend"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
|
||||
Generated
+315
-540
File diff suppressed because it is too large
Load Diff
+38
-38
@@ -8,7 +8,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "2.1.0"
|
||||
version = "2.0.0-dev-112"
|
||||
edition = "2024"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
@@ -36,34 +36,34 @@ slack = { version = "2.0.0", package = "slack_client_rs", default-features = fal
|
||||
mogh_error = { version = "1.0.3", default-features = false }
|
||||
derive_default_builder = "0.1.8"
|
||||
async_timing_util = "1.1.0"
|
||||
mogh_auth_client = "1.2.2"
|
||||
mogh_auth_server = "1.2.13"
|
||||
mogh_secret_file = "1.0.1"
|
||||
mogh_validations = "1.0.1"
|
||||
mogh_rate_limit = "1.0.1"
|
||||
mogh_auth_client = "1.2.1"
|
||||
mogh_auth_server = "1.2.4"
|
||||
mogh_secret_file = "1.0.0"
|
||||
mogh_validations = "1.0.0"
|
||||
mogh_rate_limit = "1.0.0"
|
||||
partial_derive2 = "0.4.5"
|
||||
mongo_indexed = "2.0.2"
|
||||
mogh_resolver = "1.0.0"
|
||||
mogh_config = "1.0.5"
|
||||
mogh_logger = "1.3.3"
|
||||
mogh_server = "1.4.5"
|
||||
mogh_config = "1.0.1"
|
||||
mogh_logger = "1.3.0"
|
||||
mogh_server = "1.1.2"
|
||||
toml_pretty = "2.0.0"
|
||||
mogh_cache = "1.1.1"
|
||||
mogh_pki = "1.1.3"
|
||||
mogh_pki = "1.1.0"
|
||||
mungos = "3.2.2"
|
||||
svi = "1.2.0"
|
||||
|
||||
# ASYNC
|
||||
reqwest = { version = "0.13.2", default-features = false, features = ["json", "stream", "form", "query", "rustls"] }
|
||||
tokio = { version = "1.50.0", features = ["full"] }
|
||||
reqwest = { version = "0.13.1", default-features = false, features = ["json", "stream", "form", "query", "rustls"] }
|
||||
tokio = { version = "1.49.0", features = ["full"] }
|
||||
tokio-util = { version = "0.7.18", features = ["io", "codec"] }
|
||||
tokio-stream = { version = "0.1.18", features = ["sync"] }
|
||||
pin-project-lite = "0.2.17"
|
||||
futures-util = "0.3.32"
|
||||
arc-swap = "1.9.0"
|
||||
pin-project-lite = "0.2.16"
|
||||
futures-util = "0.3.31"
|
||||
arc-swap = "1.8.0"
|
||||
|
||||
# SERVER
|
||||
tokio-tungstenite = { version = "0.29.0", features = ["rustls-tls-native-roots"] }
|
||||
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-native-roots"] }
|
||||
axum = { version = "0.8.8", features = ["ws", "json", "macros"] }
|
||||
axum-extra = { version = "0.12.5", features = ["typed-header"] }
|
||||
|
||||
@@ -75,36 +75,36 @@ utoipa = "5.4.0"
|
||||
ipnetwork = { version = "0.21.1", features = ["serde"] }
|
||||
indexmap = { version = "2.13.0", features = ["serde"] }
|
||||
serde = { version = "1.0.227", features = ["derive"] }
|
||||
strum = { version = "0.28.0", features = ["derive"] }
|
||||
strum = { version = "0.27.2", features = ["derive"] }
|
||||
bson = { version = "2.15.0" } # must keep in sync with mongodb version
|
||||
toml = "1.1.2"
|
||||
toml = "0.9.11"
|
||||
serde_yaml_ng = "0.10.0"
|
||||
serde_json = "1.0.149"
|
||||
serde_qs = "1.1.0"
|
||||
url = "2.5.8"
|
||||
serde_json = "1.0.148"
|
||||
serde_qs = "0.15.0"
|
||||
url = "2.5.7"
|
||||
|
||||
# ERROR
|
||||
anyhow = "1.0.102"
|
||||
anyhow = "1.0.100"
|
||||
thiserror = "2.0.18"
|
||||
|
||||
# LOGGING
|
||||
tracing = "0.1.44"
|
||||
|
||||
# CONFIG
|
||||
clap = { version = "4.5.60", features = ["derive"] }
|
||||
clap = { version = "4.5.54", features = ["derive"] }
|
||||
dotenvy = "0.15.7"
|
||||
envy = "0.4.2"
|
||||
|
||||
# CRYPTO / AUTH
|
||||
uuid = { version = "1.23.0", features = ["v4", "fast-rng", "serde"] }
|
||||
rustls = { version = "0.23.37", features = ["aws-lc-rs"] }
|
||||
data-encoding = "2.10.0"
|
||||
uuid = { version = "1.19.0", features = ["v4", "fast-rng", "serde"] }
|
||||
rustls = { version = "0.23.36", features = ["aws-lc-rs"] }
|
||||
data-encoding = "2.9.0"
|
||||
urlencoding = "2.1.3"
|
||||
bcrypt = "0.19.0"
|
||||
bcrypt = "0.18.0"
|
||||
hmac = "0.12.1"
|
||||
sha1 = "0.10.6"
|
||||
sha2 = "0.10.9"
|
||||
rand = "0.10.0"
|
||||
rand = "0.9.2"
|
||||
hex = "0.4.3"
|
||||
|
||||
# SYSTEM
|
||||
@@ -112,27 +112,27 @@ hickory-resolver = "0.25.2"
|
||||
portable-pty = "0.9.0"
|
||||
shell-escape = "0.1.5"
|
||||
crossterm = "0.29.0"
|
||||
bollard = "0.20.2"
|
||||
sysinfo = "0.38.4"
|
||||
bollard = "0.20.0"
|
||||
sysinfo = "0.37.1"
|
||||
shlex = "1.3.0"
|
||||
|
||||
# CLOUD
|
||||
aws-config = "1.8.15"
|
||||
aws-sdk-ec2 = "1.220.1"
|
||||
aws-credential-types = "1.2.14"
|
||||
aws-config = "1.8.12"
|
||||
aws-sdk-ec2 = "1.200.0"
|
||||
aws-credential-types = "1.2.11"
|
||||
|
||||
## CRON
|
||||
english-to-cron = "0.1.7"
|
||||
chrono-tz = "0.10.4"
|
||||
chrono = "0.4.44"
|
||||
chrono = "0.4.43"
|
||||
croner = "3.0.1"
|
||||
|
||||
# MISC
|
||||
async-compression = { version = "0.4.41", features = ["tokio", "gzip"] }
|
||||
async-compression = { version = "0.4.37", features = ["tokio", "gzip"] }
|
||||
derive_builder = "0.20.2"
|
||||
comfy-table = "7.2.2"
|
||||
typeshare = "1.0.5"
|
||||
wildcard = "0.3.0"
|
||||
colored = "3.1.1"
|
||||
bytes = "1.11.1"
|
||||
regex = "1.12.3"
|
||||
colored = "3.0.0"
|
||||
bytes = "1.11.0"
|
||||
regex = "1.12.2"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Builds the Komodo Core, Periphery, and Util binaries
|
||||
## for a specific architecture. Requires OpenSSL 3 or later.
|
||||
|
||||
FROM rust:1.94.1-bookworm AS builder
|
||||
FROM rust:1.91.1-bookworm AS builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
## Uses chef for dependency caching to help speed up back-to-back builds.
|
||||
|
||||
FROM lukemathwalker/cargo-chef:latest-rust-1.94.1-bookworm AS chef
|
||||
FROM lukemathwalker/cargo-chef:latest-rust-1.90.0-bookworm AS chef
|
||||
WORKDIR /builder
|
||||
|
||||
# Plan just the RECIPE to see if things have changed
|
||||
|
||||
@@ -32,9 +32,7 @@ tracing.workspace = true
|
||||
colored.workspace = true
|
||||
dotenvy.workspace = true
|
||||
anyhow.workspace = true
|
||||
bcrypt.workspace = true
|
||||
chrono.workspace = true
|
||||
rustls.workspace = true
|
||||
tokio.workspace = true
|
||||
serde.workspace = true
|
||||
clap.workspace = true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM rust:1.94.1-bullseye AS builder
|
||||
FROM rust:1.91.1-bullseye AS builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Assumes the latest binaries for x86_64 and aarch64 are already built (by binaries.Dockerfile).
|
||||
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
ARG X86_64_BINARIES=${BINARIES_IMAGE}-x86_64
|
||||
ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Assumes the latest binaries for the required arch are already built (by binaries.Dockerfile).
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
|
||||
# This is required to work with COPY --from
|
||||
FROM ${BINARIES_IMAGE} AS binaries
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
use anyhow::Context as _;
|
||||
use komodo_client::api::read::GetCoreInfo;
|
||||
|
||||
pub async fn handle() -> anyhow::Result<()> {
|
||||
let client = super::komodo_client().await?;
|
||||
let info = client.read(GetCoreInfo {}).await?;
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&info)
|
||||
.context("Failed to serialize core info to JSON")?
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
use anyhow::Context as _;
|
||||
use database::bson::doc;
|
||||
use komodo_client::{
|
||||
api::read::FindUser,
|
||||
entities::{
|
||||
api_key::ApiKey, config::cli::args::create::CreateApiKey,
|
||||
komodo_timestamp, random_string,
|
||||
},
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::config::cli_config;
|
||||
|
||||
pub async fn create(
|
||||
CreateApiKey {
|
||||
name,
|
||||
for_user,
|
||||
expires,
|
||||
use_api,
|
||||
}: &CreateApiKey,
|
||||
) -> anyhow::Result<()> {
|
||||
let expires = if let Some(expires_days) = expires {
|
||||
// now + expires in ms
|
||||
komodo_timestamp() + expires_days * 24 * 60 * 60 * 1000
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if *use_api {
|
||||
// USE API
|
||||
let client = crate::command::komodo_client().await?;
|
||||
|
||||
let keys = if let Some(username) = for_user {
|
||||
// For service user
|
||||
let user = client
|
||||
.read(FindUser {
|
||||
user: username.to_string(),
|
||||
})
|
||||
.await?;
|
||||
client
|
||||
.write(
|
||||
komodo_client::api::write::CreateApiKeyForServiceUser {
|
||||
user_id: user.id,
|
||||
name: name.clone().unwrap_or_default(),
|
||||
expires,
|
||||
},
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
// For self
|
||||
client
|
||||
.auth_manage(komodo_client::api::auth::manage::CreateApiKey {
|
||||
name: name.clone().unwrap_or_default(),
|
||||
expires: expires as u64,
|
||||
})
|
||||
.await?
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&keys)
|
||||
.context("Failed to serialize api keys to JSON")?
|
||||
);
|
||||
} else {
|
||||
// USE DATABASE
|
||||
let db = database::Client::new(&cli_config().database).await?;
|
||||
|
||||
let user = db
|
||||
.users
|
||||
.find_one(doc! { "username": for_user })
|
||||
.await
|
||||
.context("Failed to query database for user")?
|
||||
.context("No user found with given username")?;
|
||||
|
||||
let key = format!("K_{}_K", random_string(40));
|
||||
let secret = format!("S_{}_S", random_string(40));
|
||||
let hashed_secret = bcrypt::hash(&secret, 10)
|
||||
.context("Failed at hashing secret string")?;
|
||||
|
||||
db.api_keys
|
||||
.insert_one(&ApiKey {
|
||||
name: name.clone().unwrap_or_default(),
|
||||
user_id: user.id,
|
||||
key: key.clone(),
|
||||
secret: hashed_secret.clone(),
|
||||
created_at: komodo_timestamp(),
|
||||
expires,
|
||||
})
|
||||
.await?;
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(
|
||||
&json!({ "key": key, "secret": secret })
|
||||
)
|
||||
.context("Failed to serialize api keys to JSON")?
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
use komodo_client::entities::config::cli::args::create::CreateCommand;
|
||||
|
||||
mod api_key;
|
||||
mod onboarding_key;
|
||||
|
||||
pub async fn handle(command: &CreateCommand) -> anyhow::Result<()> {
|
||||
match command {
|
||||
CreateCommand::ApiKey(api_key) => api_key::create(api_key).await,
|
||||
CreateCommand::OnboardingKey(onboarding_key) => {
|
||||
onboarding_key::create(onboarding_key).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
use anyhow::Context as _;
|
||||
use komodo_client::entities::{
|
||||
config::cli::args::create::CreateOnboardingKey, komodo_timestamp,
|
||||
};
|
||||
|
||||
pub async fn create(
|
||||
CreateOnboardingKey {
|
||||
name,
|
||||
expires,
|
||||
private_key,
|
||||
tags,
|
||||
privileged,
|
||||
copy_server,
|
||||
create_builder,
|
||||
}: &CreateOnboardingKey,
|
||||
) -> anyhow::Result<()> {
|
||||
let expires = if let Some(expires_days) = expires {
|
||||
// now + expires in ms
|
||||
komodo_timestamp() + expires_days * 24 * 60 * 60 * 1000
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// USE API
|
||||
let client = crate::command::komodo_client().await?;
|
||||
|
||||
let key = client
|
||||
.write(komodo_client::api::write::CreateOnboardingKey {
|
||||
name: name.clone().unwrap_or_default(),
|
||||
expires,
|
||||
private_key: private_key.clone(),
|
||||
tags: tags.clone(),
|
||||
privileged: *privileged,
|
||||
copy_server: copy_server.clone().unwrap_or_default(),
|
||||
create_builder: *create_builder,
|
||||
})
|
||||
.await?;
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&key)
|
||||
.context("Failed to serialize onboarding key to JSON")?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
+550
-131
@@ -28,139 +28,558 @@ pub async fn handle(
|
||||
}
|
||||
|
||||
println!("\n{}: Execution", "Mode".dimmed());
|
||||
|
||||
macro_rules! handle_execution {
|
||||
(
|
||||
execute: [$($ExecVariant:ident),* $(,)?],
|
||||
batch: [$($BatchVariant:ident),* $(,)?],
|
||||
) => {{
|
||||
// Print data
|
||||
match execution {
|
||||
$(Execution::$ExecVariant(data) => println!("{}: {data:?}", "Data".dimmed()),)*
|
||||
$(Execution::$BatchVariant(data) => println!("{}: {data:?}", "Data".dimmed()),)*
|
||||
Execution::CommitSync(data) => println!("{}: {data:?}", "Data".dimmed()),
|
||||
Execution::Sleep(data) => println!("{}: {data:?}", "Data".dimmed()),
|
||||
Execution::None(data) => println!("{}: {data:?}", "Data".dimmed()),
|
||||
}
|
||||
|
||||
$crate::command::wait_for_enter("run execution", yes)?;
|
||||
|
||||
info!("Running Execution...");
|
||||
|
||||
let client = $crate::command::komodo_client().await?;
|
||||
|
||||
// Execute and get result
|
||||
match execution.clone() {
|
||||
$(
|
||||
Execution::$ExecVariant(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
)*
|
||||
$(
|
||||
Execution::$BatchVariant(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
)*
|
||||
Execution::CommitSync(request) => client
|
||||
.write(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Sleep(request) => {
|
||||
let duration =
|
||||
Duration::from_millis(request.duration_ms as u64);
|
||||
tokio::time::sleep(duration).await;
|
||||
println!("Finished sleeping!");
|
||||
std::process::exit(0)
|
||||
}
|
||||
Execution::None(_) => unreachable!(),
|
||||
}
|
||||
}};
|
||||
match execution {
|
||||
Execution::None(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunAction(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchRunAction(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunProcedure(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchRunProcedure(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchRunBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CancelBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::Deploy(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDeploy(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PullDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DestroyDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDestroyDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CloneRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchCloneRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PullRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchPullRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BuildRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchBuildRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CancelRepoBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DestroyContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeleteNetwork(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneNetworks(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeleteImage(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneImages(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeleteVolume(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneVolumes(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneDockerBuilders(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneBuildx(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneSystem(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunSync(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CommitSync(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeployStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDeployStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeployStackIfChanged(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PullStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchPullStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DestroyStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDestroyStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunStackService(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::TestAlerter(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::SendAlert(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmNodes(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmStacks(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmServices(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CreateSwarmConfig(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateSwarmConfig(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CreateSwarmSecret(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateSwarmSecret(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::ClearRepoCache(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BackupCoreDatabase(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::GlobalAutoUpdate(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateAllServerKeys(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateCoreKeys(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::Sleep(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
}
|
||||
|
||||
let res = handle_execution!(
|
||||
execute: [
|
||||
RunAction,
|
||||
RunProcedure,
|
||||
RunBuild,
|
||||
CancelBuild,
|
||||
Deploy,
|
||||
PullDeployment,
|
||||
StartDeployment,
|
||||
RestartDeployment,
|
||||
PauseDeployment,
|
||||
UnpauseDeployment,
|
||||
StopDeployment,
|
||||
DestroyDeployment,
|
||||
CloneRepo,
|
||||
PullRepo,
|
||||
BuildRepo,
|
||||
CancelRepoBuild,
|
||||
StartContainer,
|
||||
RestartContainer,
|
||||
PauseContainer,
|
||||
UnpauseContainer,
|
||||
StopContainer,
|
||||
DestroyContainer,
|
||||
StartAllContainers,
|
||||
RestartAllContainers,
|
||||
PauseAllContainers,
|
||||
UnpauseAllContainers,
|
||||
StopAllContainers,
|
||||
PruneContainers,
|
||||
DeleteNetwork,
|
||||
PruneNetworks,
|
||||
DeleteImage,
|
||||
PruneImages,
|
||||
DeleteVolume,
|
||||
PruneVolumes,
|
||||
PruneDockerBuilders,
|
||||
PruneBuildx,
|
||||
PruneSystem,
|
||||
RunSync,
|
||||
DeployStack,
|
||||
DeployStackIfChanged,
|
||||
PullStack,
|
||||
StartStack,
|
||||
RestartStack,
|
||||
PauseStack,
|
||||
UnpauseStack,
|
||||
StopStack,
|
||||
DestroyStack,
|
||||
RunStackService,
|
||||
TestAlerter,
|
||||
SendAlert,
|
||||
RemoveSwarmNodes,
|
||||
UpdateSwarmNode,
|
||||
RemoveSwarmStacks,
|
||||
RemoveSwarmServices,
|
||||
CreateSwarmConfig,
|
||||
RotateSwarmConfig,
|
||||
RemoveSwarmConfigs,
|
||||
CreateSwarmSecret,
|
||||
RotateSwarmSecret,
|
||||
RemoveSwarmSecrets,
|
||||
ClearRepoCache,
|
||||
BackupCoreDatabase,
|
||||
GlobalAutoUpdate,
|
||||
RotateAllServerKeys,
|
||||
RotateCoreKeys,
|
||||
],
|
||||
batch: [
|
||||
BatchRunAction,
|
||||
BatchRunProcedure,
|
||||
BatchRunBuild,
|
||||
BatchDeploy,
|
||||
BatchDestroyDeployment,
|
||||
BatchCloneRepo,
|
||||
BatchPullRepo,
|
||||
BatchBuildRepo,
|
||||
BatchDeployStack,
|
||||
BatchDeployStackIfChanged,
|
||||
BatchPullStack,
|
||||
BatchDestroyStack,
|
||||
],
|
||||
);
|
||||
super::wait_for_enter("run execution", yes)?;
|
||||
|
||||
info!("Running Execution...");
|
||||
|
||||
let client = super::komodo_client().await?;
|
||||
|
||||
let res = match execution.clone() {
|
||||
Execution::RunAction(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunAction(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::RunProcedure(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunProcedure(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::RunBuild(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunBuild(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::CancelBuild(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Deploy(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeploy(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::PullDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDestroyDeployment(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::CloneRepo(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchCloneRepo(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::PullRepo(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchPullRepo(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::BuildRepo(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchBuildRepo(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::CancelRepoBuild(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteNetwork(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneNetworks(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteImage(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneImages(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteVolume(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneVolumes(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneDockerBuilders(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneBuildx(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneSystem(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RunSync(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::CommitSync(request) => client
|
||||
.write(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeployStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeployStack(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::DeployStackIfChanged(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeployStackIfChanged(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::PullStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchPullStack(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::StartStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDestroyStack(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::RunStackService(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::TestAlerter(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::SendAlert(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmNodes(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmStacks(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmServices(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::CreateSwarmConfig(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RotateSwarmConfig(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmConfigs(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::CreateSwarmSecret(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RotateSwarmSecret(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmSecrets(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::ClearRepoCache(request) => client
|
||||
.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::RotateAllServerKeys(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RotateCoreKeys(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Sleep(request) => {
|
||||
let duration =
|
||||
Duration::from_millis(request.duration_ms as u64);
|
||||
tokio::time::sleep(duration).await;
|
||||
println!("Finished sleeping!");
|
||||
std::process::exit(0)
|
||||
}
|
||||
Execution::None(_) => unreachable!(),
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(ExecutionResult::Single(update)) => {
|
||||
|
||||
@@ -15,8 +15,6 @@ use wildcard::Wildcard;
|
||||
use crate::config::cli_config;
|
||||
|
||||
pub mod container;
|
||||
pub mod core_info;
|
||||
pub mod create;
|
||||
pub mod database;
|
||||
pub mod execute;
|
||||
pub mod list;
|
||||
|
||||
@@ -2,7 +2,7 @@ use anyhow::{Context, anyhow};
|
||||
use colored::Colorize;
|
||||
use komodo_client::{
|
||||
api::{
|
||||
read::{GetServer, ListAllDockerContainers, ListServers},
|
||||
read::{ListAllDockerContainers, ListServers},
|
||||
terminal::InitTerminal,
|
||||
},
|
||||
entities::{
|
||||
@@ -26,7 +26,7 @@ pub async fn handle_connect(
|
||||
recreate,
|
||||
}: &Connect,
|
||||
) -> anyhow::Result<()> {
|
||||
handle_terminal_forwarding(server, async {
|
||||
handle_terminal_forwarding(async {
|
||||
super::komodo_client()
|
||||
.await?
|
||||
.connect_server_terminal(
|
||||
@@ -56,28 +56,25 @@ pub async fn handle_exec(
|
||||
}: &Exec,
|
||||
) -> anyhow::Result<()> {
|
||||
let server = get_server(server.clone(), container).await?;
|
||||
handle_terminal_forwarding(
|
||||
&format!("{server}/{container}"),
|
||||
async {
|
||||
super::komodo_client()
|
||||
.await?
|
||||
.connect_container_terminal(
|
||||
server,
|
||||
container.to_string(),
|
||||
None,
|
||||
Some(InitTerminal {
|
||||
command: Some(shell.to_string()),
|
||||
recreate: if *recreate {
|
||||
TerminalRecreateMode::Always
|
||||
} else {
|
||||
TerminalRecreateMode::DifferentCommand
|
||||
},
|
||||
mode: Some(ContainerTerminalMode::Exec),
|
||||
}),
|
||||
)
|
||||
.await
|
||||
},
|
||||
)
|
||||
handle_terminal_forwarding(async {
|
||||
super::komodo_client()
|
||||
.await?
|
||||
.connect_container_terminal(
|
||||
server,
|
||||
container.to_string(),
|
||||
None,
|
||||
Some(InitTerminal {
|
||||
command: Some(shell.to_string()),
|
||||
recreate: if *recreate {
|
||||
TerminalRecreateMode::Always
|
||||
} else {
|
||||
TerminalRecreateMode::DifferentCommand
|
||||
},
|
||||
mode: Some(ContainerTerminalMode::Exec),
|
||||
}),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -89,28 +86,25 @@ pub async fn handle_attach(
|
||||
}: &Attach,
|
||||
) -> anyhow::Result<()> {
|
||||
let server = get_server(server.clone(), container).await?;
|
||||
handle_terminal_forwarding(
|
||||
&format!("{server}/{container}-attach"),
|
||||
async {
|
||||
super::komodo_client()
|
||||
.await?
|
||||
.connect_container_terminal(
|
||||
server,
|
||||
container.to_string(),
|
||||
None,
|
||||
Some(InitTerminal {
|
||||
command: None,
|
||||
recreate: if *recreate {
|
||||
TerminalRecreateMode::Always
|
||||
} else {
|
||||
TerminalRecreateMode::DifferentCommand
|
||||
},
|
||||
mode: Some(ContainerTerminalMode::Attach),
|
||||
}),
|
||||
)
|
||||
.await
|
||||
},
|
||||
)
|
||||
handle_terminal_forwarding(async {
|
||||
super::komodo_client()
|
||||
.await?
|
||||
.connect_container_terminal(
|
||||
server,
|
||||
container.to_string(),
|
||||
None,
|
||||
Some(InitTerminal {
|
||||
command: None,
|
||||
recreate: if *recreate {
|
||||
TerminalRecreateMode::Always
|
||||
} else {
|
||||
TerminalRecreateMode::DifferentCommand
|
||||
},
|
||||
mode: Some(ContainerTerminalMode::Attach),
|
||||
}),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -138,14 +132,11 @@ async fn get_server(
|
||||
}
|
||||
|
||||
if containers.len() == 1 {
|
||||
let server_id = containers
|
||||
return containers
|
||||
.pop()
|
||||
.context("Shouldn't happen")?
|
||||
.server_id
|
||||
.context("Container doesn't have server_id")?;
|
||||
let server_name =
|
||||
client.read(GetServer { server: server_id }).await?.name;
|
||||
return Ok(server_name);
|
||||
.context("Container doesn't have server_id");
|
||||
}
|
||||
|
||||
let servers = containers
|
||||
@@ -172,7 +163,6 @@ async fn get_server(
|
||||
async fn handle_terminal_forwarding<
|
||||
C: Future<Output = anyhow::Result<TerminalWebsocket>>,
|
||||
>(
|
||||
label: &str,
|
||||
connect: C,
|
||||
) -> anyhow::Result<()> {
|
||||
// Need to forward multiple sources into ws write
|
||||
@@ -257,16 +247,6 @@ async fn handle_terminal_forwarding<
|
||||
|
||||
let forward_read = async {
|
||||
let mut stdout = tokio::io::stdout();
|
||||
|
||||
// Write connection message
|
||||
if let Err(e) = write_connection_message(&mut stdout, label)
|
||||
.await
|
||||
.context("Failed to write text to stdout")
|
||||
{
|
||||
cancel.cancel();
|
||||
return Some(e);
|
||||
}
|
||||
|
||||
while let Some(msg) =
|
||||
future_or_cancel(ws_read.receive_stdout(), &cancel).await
|
||||
{
|
||||
@@ -317,31 +297,6 @@ async fn handle_terminal_forwarding<
|
||||
std::process::exit(0)
|
||||
}
|
||||
|
||||
async fn write_connection_message(
|
||||
stdout: &mut tokio::io::Stdout,
|
||||
label: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
// Use message without ansi for correct length
|
||||
let message_clean = format!("# Connected to {label} (km) #");
|
||||
let bounder = "=".repeat(message_clean.chars().count());
|
||||
|
||||
let message = format!(
|
||||
"# {} to {} {} #",
|
||||
"Connected".green().bold(),
|
||||
label.bold(),
|
||||
"(km)".dimmed()
|
||||
);
|
||||
|
||||
stdout
|
||||
.write_all(
|
||||
format!("\n{bounder}\r\n{message}\r\n{bounder}\r\n").as_bytes(),
|
||||
)
|
||||
.await?;
|
||||
let _ = stdout.flush().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resize_message() -> anyhow::Result<TerminalStdinMessage> {
|
||||
let (cols, rows) = crossterm::terminal::size()
|
||||
.context("Failed to get terminal size")?;
|
||||
|
||||
@@ -264,9 +264,6 @@ pub fn cli_config() -> &'static CliConfig {
|
||||
ansi: env
|
||||
.komodo_cli_logging_ansi
|
||||
.unwrap_or(config.cli_logging.ansi),
|
||||
timestamps: env
|
||||
.komodo_cli_logging_timestamps
|
||||
.unwrap_or(config.cli_logging.timestamps),
|
||||
otlp_endpoint: env
|
||||
.komodo_cli_logging_otlp_endpoint
|
||||
.unwrap_or(config.cli_logging.otlp_endpoint),
|
||||
|
||||
@@ -12,9 +12,6 @@ mod config;
|
||||
|
||||
async fn app() -> anyhow::Result<()> {
|
||||
dotenvy::dotenv().ok();
|
||||
rustls::crypto::aws_lc_rs::default_provider()
|
||||
.install_default()
|
||||
.expect("Failed to install default crypto provider");
|
||||
mogh_logger::init(&config::cli_config().cli_logging)?;
|
||||
let args = config::cli_args();
|
||||
let env = config::cli_env();
|
||||
@@ -45,7 +42,6 @@ async fn app() -> anyhow::Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
args::Command::CoreInfo => command::core_info::handle().await,
|
||||
args::Command::Container(container) => {
|
||||
command::container::handle(container).await
|
||||
}
|
||||
@@ -56,9 +52,6 @@ async fn app() -> anyhow::Result<()> {
|
||||
args::Command::Execute(args) => {
|
||||
command::execute::handle(&args.execution, args.yes).await
|
||||
}
|
||||
args::Command::Create { command } => {
|
||||
command::create::handle(command).await
|
||||
}
|
||||
args::Command::Update { command } => {
|
||||
command::update::handle(command).await
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
# Build Core
|
||||
FROM rust:1.94.1-trixie AS core-builder
|
||||
FROM rust:1.91.1-trixie AS core-builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
@@ -17,13 +17,13 @@ RUN cargo build -p komodo_core --release && \
|
||||
cargo build -p komodo_cli --release && \
|
||||
cargo strip
|
||||
|
||||
# Build UI
|
||||
FROM node:22.12-alpine AS ui-builder
|
||||
# Build Frontend
|
||||
FROM node:22.12-alpine AS frontend-builder
|
||||
WORKDIR /builder
|
||||
COPY ./ui ./ui
|
||||
COPY ./frontend ./frontend
|
||||
COPY ./client/core/ts ./client
|
||||
RUN cd client && yarn && yarn build && yarn link
|
||||
RUN cd ui && yarn link komodo_client && yarn && yarn build
|
||||
RUN cd frontend && yarn link komodo_client && yarn && yarn build
|
||||
|
||||
# Final Image
|
||||
FROM debian:trixie-slim
|
||||
@@ -37,7 +37,7 @@ WORKDIR /app
|
||||
|
||||
# Copy
|
||||
COPY ./config/core.config.toml /config/.default.config.toml
|
||||
COPY --from=ui-builder /builder/ui/dist /app/ui
|
||||
COPY --from=frontend-builder /builder/frontend/dist /app/frontend
|
||||
COPY --from=core-builder /builder/target/release/core /usr/local/bin/core
|
||||
COPY --from=core-builder /builder/target/release/km /usr/local/bin/km
|
||||
COPY --from=denoland/deno:bin /deno /usr/local/bin/deno
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
## Sets up the necessary runtime container dependencies for Komodo Core.
|
||||
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
|
||||
ARG UI_IMAGE=ghcr.io/moghtech/komodo-ui:2
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
ARG FRONTEND_IMAGE=ghcr.io/moghtech/komodo-frontend:latest
|
||||
ARG X86_64_BINARIES=${BINARIES_IMAGE}-x86_64
|
||||
ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
|
||||
|
||||
# This is required to work with COPY --from
|
||||
FROM ${X86_64_BINARIES} AS x86_64
|
||||
FROM ${AARCH64_BINARIES} AS aarch64
|
||||
FROM ${UI_IMAGE} AS ui
|
||||
FROM ${FRONTEND_IMAGE} AS frontend
|
||||
|
||||
# Final Image
|
||||
FROM debian:trixie-slim
|
||||
@@ -33,9 +33,9 @@ COPY --from=x86_64 /km /app/km/linux/amd64
|
||||
COPY --from=aarch64 /km /app/km/linux/arm64
|
||||
RUN mv /app/km/${TARGETPLATFORM} /usr/local/bin/km && rm -r /app/km
|
||||
|
||||
# Copy default config / static ui / deno binary
|
||||
# Copy default config / static frontend / deno binary
|
||||
COPY ./config/core.config.toml /config/.default.config.toml
|
||||
COPY --from=ui /ui /app/ui
|
||||
COPY --from=frontend /frontend /app/frontend
|
||||
COPY --from=denoland/deno:bin /deno /usr/local/bin/deno
|
||||
|
||||
# Set $DENO_DIR and preload external Deno deps
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
## Assumes the latest binaries for the required arch are already built (by binaries.Dockerfile).
|
||||
## Sets up the necessary runtime container dependencies for Komodo Core.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
|
||||
# This is required to work with COPY --from
|
||||
FROM ${BINARIES_IMAGE} AS binaries
|
||||
|
||||
# Build UI
|
||||
FROM node:22.12-alpine AS ui-builder
|
||||
# Build Frontend
|
||||
FROM node:22.12-alpine AS frontend-builder
|
||||
WORKDIR /builder
|
||||
COPY ./ui ./ui
|
||||
COPY ./frontend ./frontend
|
||||
COPY ./client/core/ts ./client
|
||||
RUN cd client && yarn && yarn build && yarn link
|
||||
RUN cd ui && yarn link komodo_client && yarn && yarn build
|
||||
RUN cd frontend && yarn link komodo_client && yarn && yarn build
|
||||
|
||||
FROM debian:trixie-slim
|
||||
|
||||
@@ -22,7 +22,7 @@ RUN sh ./debian-deps.sh && rm ./debian-deps.sh
|
||||
|
||||
# Copy
|
||||
COPY ./config/core.config.toml /config/.default.config.toml
|
||||
COPY --from=ui-builder /builder/ui/dist /app/ui
|
||||
COPY --from=frontend-builder /builder/frontend/dist /app/frontend
|
||||
COPY --from=binaries /core /usr/local/bin/core
|
||||
COPY --from=binaries /km /usr/local/bin/km
|
||||
COPY --from=denoland/deno:bin /deno /usr/local/bin/deno
|
||||
|
||||
@@ -20,14 +20,12 @@ pub async fn send_alert(
|
||||
let link = resource_link(ResourceTargetVariant::Swarm, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | Swarm **{name}** is now **healthy**\n{link}"
|
||||
)
|
||||
format!("{level} | Swarm **{name}** is now **healthy**\n{link}")
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\n**error**: {e:#?}"))
|
||||
.map(|e| format!("\n**error**: {e}"))
|
||||
.unwrap_or_default();
|
||||
format!(
|
||||
"{level} | Swarm **{name}** is **unhealthy** ❌\n{link}{err}"
|
||||
@@ -36,6 +34,28 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Periphery version now matches Core version ✅\n{link}"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Version mismatch detected ⚠️\nPeriphery: **{server_version}** | Core: **{core_version}**\n{link}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
@@ -62,28 +82,6 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Periphery version now matches Core version ✅\n{link}"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Version mismatch detected ⚠️\nPeriphery: **{server_version}** | Core: **{core_version}**\n{link}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AlertData::ServerCpu {
|
||||
id,
|
||||
name,
|
||||
@@ -142,7 +140,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"📦 Deployment **{name}** is now **{to}**{target}\nprevious: **{from}**\n{link}"
|
||||
@@ -163,7 +161,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment **{name}** has an update available{target}\nimage: **{image}**\n{link}"
|
||||
@@ -184,7 +182,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment **{name}** was updated automatically ⏫{target}\nimage: **{image}**\n{link}"
|
||||
@@ -207,7 +205,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"🥞 Stack **{name}** is now {to}{target}\nprevious: **{from}**\n{link}"
|
||||
@@ -229,7 +227,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack **{name}** has an update available{target}\nservice: **{service}**\nimage: **{image}**\n{link}"
|
||||
@@ -253,7 +251,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack **{name}** was updated automatically ⏫{target}\n{images_label}: **{images}**\n{link}"
|
||||
|
||||
+32
-34
@@ -260,35 +260,9 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.map(|e| format!("\nerror: {e}"))
|
||||
.unwrap_or_default();
|
||||
format!(
|
||||
"{level} | Swarm {name} is unhealthy ❌\n{link}{err}"
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
err,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!("{level} | {name}{region} is now connected\n{link}")
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
format!(
|
||||
"{level} | {name}{region} is unreachable ❌\n{link}{err}"
|
||||
)
|
||||
format!("{level} | Swarm {name} is unhealthy ❌\n{link}{err}")
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
@@ -315,6 +289,30 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
}
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
err,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!("{level} | {name}{region} is now connected\n{link}")
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
format!(
|
||||
"{level} | {name}{region} is unreachable ❌\n{link}{err}"
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerCpu {
|
||||
id,
|
||||
name,
|
||||
@@ -373,7 +371,7 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"📦Deployment {name} is now {to_state}{target}\nprevious: {from}\n{link}",
|
||||
@@ -394,7 +392,7 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment {name} has an update available{target}\nimage: {image}\n{link}",
|
||||
@@ -415,7 +413,7 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment {name} was updated automatically{target}\nimage: {image}\n{link}",
|
||||
@@ -438,7 +436,7 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"🥞 Stack {name} is now {to_state}{target}\nprevious: {from}\n{link}",
|
||||
@@ -460,7 +458,7 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack {name} has an update available{target}\nservice: {service}\nimage: {image}\n{link}",
|
||||
@@ -484,7 +482,7 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack {name} was updated automatically ⏫{target}\n{images_label}: {images_str}\n{link}",
|
||||
|
||||
+36
-36
@@ -42,7 +42,7 @@ pub async fn send_alert(
|
||||
format!("{level} | Swarm *{name}* is *unhealthy* ❌");
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.map(|e| format!("\nerror: {e}"))
|
||||
.unwrap_or_default();
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
@@ -59,6 +59,35 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let text = match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Periphery version now matches Core version ✅"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}"
|
||||
)
|
||||
}
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Server,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
@@ -100,35 +129,6 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let text = match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Periphery version now matches Core version ✅"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}"
|
||||
)
|
||||
}
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Server,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::ServerCpu {
|
||||
id,
|
||||
name,
|
||||
@@ -289,7 +289,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
@@ -317,7 +317,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
@@ -345,7 +345,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
@@ -374,7 +374,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
@@ -402,7 +402,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
@@ -435,7 +435,7 @@ pub async fn send_alert(
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
format!("")
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
|
||||
@@ -66,14 +66,14 @@ impl Resolve<ExecuteArgs> for BatchRunAction {
|
||||
"BatchRunAction",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchRunAction>(&self.pattern, user)
|
||||
@@ -87,7 +87,7 @@ impl Resolve<ExecuteArgs> for RunAction {
|
||||
"RunAction",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
action = self.action,
|
||||
@@ -95,11 +95,7 @@ impl Resolve<ExecuteArgs> for RunAction {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let mut action = get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
|
||||
@@ -28,7 +28,7 @@ impl Resolve<ExecuteArgs> for TestAlerter {
|
||||
"TestAlerter",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
alerter = self.alerter,
|
||||
@@ -36,11 +36,7 @@ impl Resolve<ExecuteArgs> for TestAlerter {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let alerter = get_check_permissions::<Alerter>(
|
||||
&self.alerter,
|
||||
@@ -98,7 +94,7 @@ impl Resolve<ExecuteArgs> for SendAlert {
|
||||
"SendAlert",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
request = format!("{self:?}"),
|
||||
@@ -106,11 +102,7 @@ impl Resolve<ExecuteArgs> for SendAlert {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let alerters = list_full_for_user::<Alerter>(
|
||||
Default::default(),
|
||||
|
||||
@@ -75,14 +75,14 @@ impl Resolve<ExecuteArgs> for BatchRunBuild {
|
||||
"BatchRunBuild",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchRunBuild>(&self.pattern, user)
|
||||
@@ -96,7 +96,7 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
"RunBuild",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
build = self.build,
|
||||
@@ -104,11 +104,7 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let mut build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
@@ -526,7 +522,7 @@ impl Resolve<ExecuteArgs> for CancelBuild {
|
||||
"CancelBuild",
|
||||
skip(user, update),
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
build = self.build,
|
||||
@@ -534,11 +530,7 @@ impl Resolve<ExecuteArgs> for CancelBuild {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
@@ -634,7 +626,7 @@ async fn handle_post_build_redeploy(build_id: &str) {
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ use crate::{
|
||||
swarm::swarm_request,
|
||||
update::update_update,
|
||||
},
|
||||
monitor::{refresh_server_cache, refresh_swarm_cache},
|
||||
monitor::{update_cache_for_server, update_cache_for_swarm},
|
||||
resource::{self, setup_deployment_execution},
|
||||
state::action_states,
|
||||
};
|
||||
@@ -55,14 +55,14 @@ impl Resolve<ExecuteArgs> for BatchDeploy {
|
||||
"BatchDeploy",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDeploy>(&self.pattern, user)
|
||||
@@ -76,7 +76,7 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
"Deploy",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -86,11 +86,7 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (mut deployment, swarm_or_server) =
|
||||
setup_deployment_execution(
|
||||
@@ -115,7 +111,7 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
// This block resolves the attached Build to an actual versioned image
|
||||
@@ -226,7 +222,7 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
.await
|
||||
{
|
||||
Ok(logs) => {
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
update.logs.extend(logs)
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -250,7 +246,7 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
.await
|
||||
{
|
||||
Ok(log) => {
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.logs.push(log)
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -405,7 +401,7 @@ pub async fn pull_deployment_inner(
|
||||
Err(e) => Log::error("Pull image", format_serror(&e.into())),
|
||||
};
|
||||
|
||||
refresh_server_cache(server, true).await;
|
||||
update_cache_for_server(server, true).await;
|
||||
anyhow::Ok(log)
|
||||
}
|
||||
.await;
|
||||
@@ -422,7 +418,7 @@ impl Resolve<ExecuteArgs> for PullDeployment {
|
||||
"PullDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -430,11 +426,7 @@ impl Resolve<ExecuteArgs> for PullDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -462,7 +454,7 @@ impl Resolve<ExecuteArgs> for PullDeployment {
|
||||
action_state.update(|state| state.pulling = true)?;
|
||||
|
||||
let mut update = update.clone();
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let log = pull_deployment_inner(deployment, &server).await?;
|
||||
@@ -480,7 +472,7 @@ impl Resolve<ExecuteArgs> for StartDeployment {
|
||||
"StartDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -488,11 +480,7 @@ impl Resolve<ExecuteArgs> for StartDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -521,7 +509,7 @@ impl Resolve<ExecuteArgs> for StartDeployment {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let log = match periphery_client(&server)
|
||||
@@ -539,7 +527,7 @@ impl Resolve<ExecuteArgs> for StartDeployment {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -552,7 +540,7 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
|
||||
"RestartDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -560,11 +548,7 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -593,7 +577,7 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let log = match periphery_client(&server)
|
||||
@@ -613,7 +597,7 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -626,7 +610,7 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
|
||||
"PauseDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -634,11 +618,7 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -667,7 +647,7 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let log = match periphery_client(&server)
|
||||
@@ -685,7 +665,7 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -698,7 +678,7 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
|
||||
"UnpauseDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -706,11 +686,7 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -739,7 +715,7 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let log = match periphery_client(&server)
|
||||
@@ -759,7 +735,7 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -772,7 +748,7 @@ impl Resolve<ExecuteArgs> for StopDeployment {
|
||||
"StopDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -782,11 +758,7 @@ impl Resolve<ExecuteArgs> for StopDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -815,7 +787,7 @@ impl Resolve<ExecuteArgs> for StopDeployment {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let log = match periphery_client(&server)
|
||||
@@ -841,7 +813,7 @@ impl Resolve<ExecuteArgs> for StopDeployment {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -865,14 +837,14 @@ impl Resolve<ExecuteArgs> for BatchDestroyDeployment {
|
||||
"BatchDestroyDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDestroyDeployment>(
|
||||
@@ -889,7 +861,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
|
||||
"DestroyDeployment",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
deployment = self.deployment,
|
||||
@@ -899,11 +871,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -927,7 +895,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let log = match swarm_or_server {
|
||||
@@ -942,7 +910,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
|
||||
.await
|
||||
{
|
||||
Ok(log) => {
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
log
|
||||
}
|
||||
Err(e) => Log::error(
|
||||
@@ -970,7 +938,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
|
||||
.await
|
||||
{
|
||||
Ok(log) => {
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
log
|
||||
}
|
||||
Err(e) => Log::error(
|
||||
|
||||
@@ -15,7 +15,7 @@ use komodo_client::{
|
||||
},
|
||||
entities::{
|
||||
SwarmOrServer, deployment::DeploymentState, server::ServerState,
|
||||
stack::StackState, swarm::SwarmState,
|
||||
stack::StackState,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
@@ -39,7 +39,7 @@ use crate::{
|
||||
resource::rotate_server_keys,
|
||||
state::{
|
||||
db_client, deployment_status_cache, server_status_cache,
|
||||
stack_status_cache, swarm_status_cache,
|
||||
stack_status_cache,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -54,18 +54,14 @@ impl Resolve<ExecuteArgs> for ClearRepoCache {
|
||||
"ClearRepoCache",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
@@ -137,18 +133,14 @@ impl Resolve<ExecuteArgs> for BackupCoreDatabase {
|
||||
"BackupCoreDatabase",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
@@ -194,18 +186,14 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
"GlobalAutoUpdate",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
@@ -244,7 +232,6 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
.context("Failed to query for stacks from database")?;
|
||||
|
||||
let server_status_cache = server_status_cache();
|
||||
let swarm_status_cache = swarm_status_cache();
|
||||
let stack_status_cache = stack_status_cache();
|
||||
|
||||
// Will be edited later at update.logs[0]
|
||||
@@ -256,7 +243,7 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only check running stacks.
|
||||
// Only pull running stacks.
|
||||
if !matches!(status.curr.state, StackState::Running) {
|
||||
continue;
|
||||
}
|
||||
@@ -268,58 +255,43 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
&servers,
|
||||
)?;
|
||||
|
||||
// Ensure server / swarm is reachable
|
||||
match &swarm_or_server {
|
||||
SwarmOrServer::None => continue,
|
||||
SwarmOrServer::Server(server) => {
|
||||
if !server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
if !swarm_status_cache
|
||||
.get(&swarm.id)
|
||||
.await
|
||||
.map(|s| {
|
||||
matches!(
|
||||
s.state,
|
||||
SwarmState::Healthy | SwarmState::Unhealthy
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let SwarmOrServer::None = &swarm_or_server {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(e) = check_stack_for_update_inner(
|
||||
stack.id,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
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()
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Check Stack {}", stack.name),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
}
|
||||
if let Err(e) = check_stack_for_update_inner(
|
||||
stack.id,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Check Stack {}", 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!(
|
||||
"Checked Stack {} ✅",
|
||||
bold(&stack.name)
|
||||
));
|
||||
update.logs[0].stdout.push_str(&format!(
|
||||
"Checked Stack {} ✅",
|
||||
bold(&stack.name)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +308,7 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only check running deployments.
|
||||
// Only pull running deployments.
|
||||
if !matches!(status.curr.state, DeploymentState::Running) {
|
||||
continue;
|
||||
}
|
||||
@@ -348,38 +320,11 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
&servers,
|
||||
)?;
|
||||
|
||||
// Ensure server / swarm is reachable
|
||||
match &swarm_or_server {
|
||||
SwarmOrServer::None => continue,
|
||||
SwarmOrServer::Server(server) => {
|
||||
if !server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
if !swarm_status_cache
|
||||
.get(&swarm.id)
|
||||
.await
|
||||
.map(|s| {
|
||||
matches!(
|
||||
s.state,
|
||||
SwarmState::Healthy | SwarmState::Unhealthy
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let SwarmOrServer::None = &swarm_or_server {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = deployment.name.clone();
|
||||
|
||||
if let Err(e) = check_deployment_for_update_inner(
|
||||
deployment,
|
||||
&swarm_or_server,
|
||||
@@ -422,18 +367,14 @@ impl Resolve<ExecuteArgs> for RotateAllServerKeys {
|
||||
"RotateAllServerKeys",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
@@ -540,7 +481,7 @@ impl Resolve<ExecuteArgs> for RotateCoreKeys {
|
||||
"RotateCoreKeys",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
force = self.force,
|
||||
@@ -548,11 +489,7 @@ impl Resolve<ExecuteArgs> for RotateCoreKeys {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
|
||||
@@ -49,9 +49,9 @@ mod sync;
|
||||
use super::Variant;
|
||||
|
||||
pub struct ExecuteArgs {
|
||||
/// The task id.
|
||||
/// Unique for every '/execute' call.
|
||||
pub task_id: Uuid,
|
||||
/// The execution id.
|
||||
/// Unique for every /execute call.
|
||||
pub id: Uuid,
|
||||
pub user: User,
|
||||
pub update: Update,
|
||||
}
|
||||
@@ -60,7 +60,7 @@ pub struct ExecuteArgs {
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
|
||||
)]
|
||||
#[strum_discriminants(name(ExecuteRequestMethod), derive(Display))]
|
||||
#[strum_discriminants(name(ExecuteRequestVariant), derive(Display))]
|
||||
#[args(ExecuteArgs)]
|
||||
#[response(JsonString)]
|
||||
#[error(mogh_error::Error)]
|
||||
@@ -148,7 +148,6 @@ pub enum ExecuteRequest {
|
||||
|
||||
// ==== SWARM ====
|
||||
RemoveSwarmNodes(RemoveSwarmNodes),
|
||||
UpdateSwarmNode(UpdateSwarmNode),
|
||||
RemoveSwarmStacks(RemoveSwarmStacks),
|
||||
RemoveSwarmServices(RemoveSwarmServices),
|
||||
CreateSwarmConfig(CreateSwarmConfig),
|
||||
@@ -250,19 +249,11 @@ pub fn inner_handler(
|
||||
async move {
|
||||
let log = match handle.await {
|
||||
Ok(Err(e)) => {
|
||||
warn!(
|
||||
api = "Execute",
|
||||
task_id = task_id.to_string(),
|
||||
"/execute request task error: {e:#}",
|
||||
);
|
||||
warn!("/execute request {task_id} task error: {e:#}",);
|
||||
Log::error("Task Error", format_serror(&e.into()))
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
api = "Execute",
|
||||
task_id = task_id.to_string(),
|
||||
"/execute request spawn error: {e:?}",
|
||||
);
|
||||
warn!("/execute request {task_id} spawn error: {e:?}",);
|
||||
Log::error("Spawn Error", format!("{e:#?}"))
|
||||
}
|
||||
_ => return,
|
||||
@@ -277,7 +268,7 @@ pub fn inner_handler(
|
||||
find_one_by_id(&db_client().updates, &update_id)
|
||||
.await
|
||||
.context("Failed to query to db")?
|
||||
.context("No Update exists with given id")?;
|
||||
.context("No update exists with given id")?;
|
||||
update.logs.push(log);
|
||||
update.finalize();
|
||||
update_update(update).await
|
||||
@@ -286,10 +277,7 @@ pub fn inner_handler(
|
||||
|
||||
if let Err(e) = res {
|
||||
warn!(
|
||||
api = "Execute",
|
||||
task_id = task_id.to_string(),
|
||||
update_id,
|
||||
"Failed to modify Update with task error log | {e:#}"
|
||||
"Failed to update update with task error log | {e:#}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -305,44 +293,25 @@ async fn task(
|
||||
user: User,
|
||||
update: Update,
|
||||
) -> anyhow::Result<String> {
|
||||
let method: ExecuteRequestMethod = (&request).into();
|
||||
|
||||
let user_id = user.id.clone();
|
||||
let username = user.username.clone();
|
||||
let variant: ExecuteRequestVariant = (&request).into();
|
||||
|
||||
info!(
|
||||
api = "Execute",
|
||||
task_id = id.to_string(),
|
||||
method = method.to_string(),
|
||||
user_id,
|
||||
username,
|
||||
"EXECUTE REQUEST",
|
||||
"EXECUTE REQUEST {id} | METHOD: {variant} | USER: {} ({})",
|
||||
user.username, user.id
|
||||
);
|
||||
|
||||
let res = match request
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: id,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Err(e) => Err(e.error),
|
||||
Ok(JsonString::Err(e)) => Err(
|
||||
anyhow::Error::from(e).context("failed to serialize response"),
|
||||
),
|
||||
Ok(JsonString::Ok(res)) => Ok(res),
|
||||
};
|
||||
let res =
|
||||
match request.resolve(&ExecuteArgs { user, update, id }).await {
|
||||
Err(e) => Err(e.error),
|
||||
Ok(JsonString::Err(e)) => Err(
|
||||
anyhow::Error::from(e)
|
||||
.context("failed to serialize response"),
|
||||
),
|
||||
Ok(JsonString::Ok(res)) => Ok(res),
|
||||
};
|
||||
|
||||
if let Err(e) = &res {
|
||||
warn!(
|
||||
api = "Execute",
|
||||
task_id = id.to_string(),
|
||||
method = method.to_string(),
|
||||
user_id,
|
||||
username,
|
||||
"EXECUTE REQUEST | ERROR: {e:#}"
|
||||
);
|
||||
warn!("EXECUTE REQUEST {id} | METHOD: {variant} | ERROR: {e:#}");
|
||||
}
|
||||
|
||||
res
|
||||
|
||||
@@ -59,7 +59,7 @@ impl Resolve<ExecuteArgs> for RunProcedure {
|
||||
"RunProcedure",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
procedure = self.procedure,
|
||||
@@ -67,11 +67,7 @@ impl Resolve<ExecuteArgs> for RunProcedure {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
Ok(
|
||||
resolve_inner(self.procedure, user.clone(), update.clone())
|
||||
|
||||
@@ -55,14 +55,14 @@ impl Resolve<ExecuteArgs> for BatchCloneRepo {
|
||||
"BatchCloneRepo",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchCloneRepo>(&self.pattern, user)
|
||||
@@ -76,7 +76,7 @@ impl Resolve<ExecuteArgs> for CloneRepo {
|
||||
"CloneRepo",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
repo = self.repo,
|
||||
@@ -84,11 +84,7 @@ impl Resolve<ExecuteArgs> for CloneRepo {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
@@ -190,14 +186,14 @@ impl Resolve<ExecuteArgs> for BatchPullRepo {
|
||||
"BatchPullRepo",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchPullRepo>(&self.pattern, user)
|
||||
@@ -211,7 +207,7 @@ impl Resolve<ExecuteArgs> for PullRepo {
|
||||
"PullRepo",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
repo = self.repo,
|
||||
@@ -219,11 +215,7 @@ impl Resolve<ExecuteArgs> for PullRepo {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
@@ -371,14 +363,14 @@ impl Resolve<ExecuteArgs> for BatchBuildRepo {
|
||||
"BatchBuildRepo",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchBuildRepo>(&self.pattern, user)
|
||||
@@ -392,7 +384,7 @@ impl Resolve<ExecuteArgs> for BuildRepo {
|
||||
"BuildRepo",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
repo = self.repo,
|
||||
@@ -400,11 +392,7 @@ impl Resolve<ExecuteArgs> for BuildRepo {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
@@ -717,7 +705,7 @@ impl Resolve<ExecuteArgs> for CancelRepoBuild {
|
||||
"CancelRepoBuild",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
repo = self.repo,
|
||||
@@ -725,11 +713,7 @@ impl Resolve<ExecuteArgs> for CancelRepoBuild {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
|
||||
@@ -14,7 +14,7 @@ use periphery_client::api;
|
||||
|
||||
use crate::{
|
||||
helpers::{periphery_client, update::update_update},
|
||||
monitor::refresh_server_cache,
|
||||
monitor::update_cache_for_server,
|
||||
permission::get_check_permissions,
|
||||
state::action_states,
|
||||
};
|
||||
@@ -26,7 +26,7 @@ impl Resolve<ExecuteArgs> for StartContainer {
|
||||
"StartContainer",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -35,11 +35,7 @@ impl Resolve<ExecuteArgs> for StartContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -61,7 +57,7 @@ impl Resolve<ExecuteArgs> for StartContainer {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
@@ -80,7 +76,7 @@ impl Resolve<ExecuteArgs> for StartContainer {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -94,7 +90,7 @@ impl Resolve<ExecuteArgs> for RestartContainer {
|
||||
"RestartContainer",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -103,11 +99,7 @@ impl Resolve<ExecuteArgs> for RestartContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -129,7 +121,7 @@ impl Resolve<ExecuteArgs> for RestartContainer {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
@@ -150,7 +142,7 @@ impl Resolve<ExecuteArgs> for RestartContainer {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -164,7 +156,7 @@ impl Resolve<ExecuteArgs> for PauseContainer {
|
||||
"PauseContainer",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -173,11 +165,7 @@ impl Resolve<ExecuteArgs> for PauseContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -199,7 +187,7 @@ impl Resolve<ExecuteArgs> for PauseContainer {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
@@ -218,7 +206,7 @@ impl Resolve<ExecuteArgs> for PauseContainer {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -232,7 +220,7 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
|
||||
"UnpauseContainer",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -241,11 +229,7 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -267,7 +251,7 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
@@ -288,7 +272,7 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -302,7 +286,7 @@ impl Resolve<ExecuteArgs> for StopContainer {
|
||||
"StopContainer",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -313,11 +297,7 @@ impl Resolve<ExecuteArgs> for StopContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -339,7 +319,7 @@ impl Resolve<ExecuteArgs> for StopContainer {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
@@ -360,7 +340,7 @@ impl Resolve<ExecuteArgs> for StopContainer {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -374,7 +354,7 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
|
||||
"DestroyContainer",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -385,11 +365,7 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let DestroyContainer {
|
||||
server,
|
||||
@@ -417,7 +393,7 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
// Send update after setting action state, this way UI gets correct state.
|
||||
// Send update after setting action state, this way frontend gets correct state.
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
@@ -440,7 +416,7 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -454,7 +430,7 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
|
||||
"StartAllContainers",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -462,11 +438,7 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -505,7 +477,7 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
|
||||
);
|
||||
}
|
||||
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -518,7 +490,7 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
|
||||
"RestartAllContainers",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -526,11 +498,7 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -571,7 +539,7 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
|
||||
);
|
||||
}
|
||||
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -584,7 +552,7 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
|
||||
"PauseAllContainers",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -592,11 +560,7 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -635,7 +599,7 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
|
||||
);
|
||||
}
|
||||
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -648,7 +612,7 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
|
||||
"UnpauseAllContainers",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -656,11 +620,7 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -701,7 +661,7 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
|
||||
);
|
||||
}
|
||||
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -714,7 +674,7 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
|
||||
"StopAllContainers",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -722,11 +682,7 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -765,7 +721,7 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
|
||||
);
|
||||
}
|
||||
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -778,7 +734,7 @@ impl Resolve<ExecuteArgs> for PruneContainers {
|
||||
"PruneContainers",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -786,11 +742,7 @@ impl Resolve<ExecuteArgs> for PruneContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -833,7 +785,7 @@ impl Resolve<ExecuteArgs> for PruneContainers {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -847,7 +799,7 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
|
||||
"DeleteNetwork",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -856,11 +808,7 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -898,7 +846,7 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -912,7 +860,7 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
|
||||
"PruneNetworks",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -920,11 +868,7 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -965,7 +909,7 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -979,7 +923,7 @@ impl Resolve<ExecuteArgs> for DeleteImage {
|
||||
"DeleteImage",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -988,11 +932,7 @@ impl Resolve<ExecuteArgs> for DeleteImage {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -1027,7 +967,7 @@ impl Resolve<ExecuteArgs> for DeleteImage {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -1041,7 +981,7 @@ impl Resolve<ExecuteArgs> for PruneImages {
|
||||
"PruneImages",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -1049,11 +989,7 @@ impl Resolve<ExecuteArgs> for PruneImages {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -1092,7 +1028,7 @@ impl Resolve<ExecuteArgs> for PruneImages {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -1106,7 +1042,7 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
|
||||
"DeleteVolume",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -1115,11 +1051,7 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -1157,7 +1089,7 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -1171,7 +1103,7 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
|
||||
"PruneVolumes",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -1179,11 +1111,7 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -1222,7 +1150,7 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -1236,7 +1164,7 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
|
||||
"PruneDockerBuilders",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -1244,11 +1172,7 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -1287,7 +1211,7 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -1301,7 +1225,7 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
|
||||
"PruneBuildx",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -1309,11 +1233,7 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -1352,7 +1272,7 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -1366,7 +1286,7 @@ impl Resolve<ExecuteArgs> for PruneSystem {
|
||||
"PruneSystem",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
server = self.server,
|
||||
@@ -1374,11 +1294,7 @@ impl Resolve<ExecuteArgs> for PruneSystem {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -1416,7 +1332,7 @@ impl Resolve<ExecuteArgs> for PruneSystem {
|
||||
};
|
||||
|
||||
update.logs.push(log);
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -39,7 +39,7 @@ use crate::{
|
||||
add_update_without_send, init_execution_update, update_update,
|
||||
},
|
||||
},
|
||||
monitor::{refresh_server_cache, refresh_swarm_cache},
|
||||
monitor::{update_cache_for_server, update_cache_for_swarm},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
stack::{
|
||||
@@ -69,14 +69,14 @@ impl Resolve<ExecuteArgs> for BatchDeployStack {
|
||||
"BatchDeployStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDeployStack>(&self.pattern, user)
|
||||
@@ -90,7 +90,7 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
"DeployStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -100,11 +100,7 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (mut stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
@@ -317,10 +313,10 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
match swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
SwarmOrServer::Server(server) => {
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,14 +342,14 @@ impl Resolve<ExecuteArgs> for BatchDeployStackIfChanged {
|
||||
"BatchDeployStackIfChanged",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDeployStackIfChanged>(
|
||||
@@ -370,7 +366,7 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
|
||||
"DeployStackIfChanged",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -379,11 +375,7 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
@@ -441,7 +433,7 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.clone(),
|
||||
update,
|
||||
task_id: *task_id,
|
||||
id: *id,
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -580,7 +572,7 @@ async fn deploy_services(
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.clone(),
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -611,7 +603,7 @@ async fn restart_services(
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.clone(),
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -774,14 +766,14 @@ impl Resolve<ExecuteArgs> for BatchPullStack {
|
||||
"BatchPullStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchPullStack>(&self.pattern, user)
|
||||
@@ -886,7 +878,7 @@ pub async fn pull_stack_inner(
|
||||
.await?;
|
||||
|
||||
// Ensure cached stack state up to date by updating server cache
|
||||
refresh_server_cache(server, true).await;
|
||||
update_cache_for_server(server, true).await;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
@@ -896,7 +888,7 @@ impl Resolve<ExecuteArgs> for PullStack {
|
||||
"PullStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -905,11 +897,7 @@ impl Resolve<ExecuteArgs> for PullStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
@@ -971,7 +959,7 @@ impl Resolve<ExecuteArgs> for StartStack {
|
||||
"StartStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -980,11 +968,7 @@ impl Resolve<ExecuteArgs> for StartStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
execute_compose::<StartStack>(
|
||||
&self.stack,
|
||||
@@ -1004,7 +988,7 @@ impl Resolve<ExecuteArgs> for RestartStack {
|
||||
"RestartStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -1013,11 +997,7 @@ impl Resolve<ExecuteArgs> for RestartStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
execute_compose::<RestartStack>(
|
||||
&self.stack,
|
||||
@@ -1039,7 +1019,7 @@ impl Resolve<ExecuteArgs> for PauseStack {
|
||||
"PauseStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -1048,11 +1028,7 @@ impl Resolve<ExecuteArgs> for PauseStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
execute_compose::<PauseStack>(
|
||||
&self.stack,
|
||||
@@ -1072,7 +1048,7 @@ impl Resolve<ExecuteArgs> for UnpauseStack {
|
||||
"UnpauseStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -1081,11 +1057,7 @@ impl Resolve<ExecuteArgs> for UnpauseStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
execute_compose::<UnpauseStack>(
|
||||
&self.stack,
|
||||
@@ -1105,7 +1077,7 @@ impl Resolve<ExecuteArgs> for StopStack {
|
||||
"StopStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -1114,11 +1086,7 @@ impl Resolve<ExecuteArgs> for StopStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
execute_compose::<StopStack>(
|
||||
&self.stack,
|
||||
@@ -1150,14 +1118,14 @@ impl Resolve<ExecuteArgs> for BatchDestroyStack {
|
||||
"BatchDestroyStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
super::batch_execute::<BatchDestroyStack>(&self.pattern, user)
|
||||
.await
|
||||
@@ -1170,7 +1138,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
|
||||
"DestroyStack",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -1181,11 +1149,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
@@ -1218,7 +1182,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
|
||||
action_state.update(|state| state.destroying = true)?;
|
||||
|
||||
let mut update = update.clone();
|
||||
// Send update here for UI to recheck action state
|
||||
// Send update here for frontend to recheck action state
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
match swarm_request(
|
||||
@@ -1241,7 +1205,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
|
||||
}
|
||||
|
||||
// Ensure cached stack state up to date by updating swarm cache
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
@@ -1269,7 +1233,7 @@ impl Resolve<ExecuteArgs> for RunStackService {
|
||||
"RunStackService",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
stack = self.stack,
|
||||
@@ -1279,11 +1243,7 @@ impl Resolve<ExecuteArgs> for RunStackService {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let (mut stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use formatting::format_serror;
|
||||
use komodo_client::{
|
||||
api::execute::*,
|
||||
api::execute::{
|
||||
CreateSwarmConfig, CreateSwarmSecret, RemoveSwarmConfigs,
|
||||
RemoveSwarmNodes, RemoveSwarmSecrets, RemoveSwarmServices,
|
||||
RemoveSwarmStacks, RotateSwarmConfig, RotateSwarmSecret,
|
||||
},
|
||||
entities::{permission::PermissionLevel, swarm::Swarm},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
@@ -8,7 +12,7 @@ use mogh_resolver::Resolve;
|
||||
use crate::{
|
||||
api::execute::ExecuteArgs,
|
||||
helpers::{swarm::swarm_request, update::update_update},
|
||||
monitor::refresh_swarm_cache,
|
||||
monitor::update_cache_for_swarm,
|
||||
permission::get_check_permissions,
|
||||
};
|
||||
|
||||
@@ -17,7 +21,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
|
||||
"RemoveSwarmNodes",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -27,11 +31,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -55,7 +55,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Remove Swarm Nodes",
|
||||
@@ -72,78 +72,12 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for UpdateSwarmNode {
|
||||
#[instrument(
|
||||
"UpdateSwarmNode",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
node = self.node,
|
||||
availability = format!("{:?}", self.availability),
|
||||
label_add = format!("{:?}", self.label_add),
|
||||
label_rm = format!("{:?}", self.label_rm),
|
||||
role = format!("{:?}", self.role),
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
match swarm_request(
|
||||
&swarm.config.server_ids,
|
||||
periphery_client::api::swarm::UpdateSwarmNode {
|
||||
node: self.node,
|
||||
availability: self.availability,
|
||||
label_add: self.label_add,
|
||||
label_rm: self.label_rm,
|
||||
role: self.role,
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Update Swarm Node",
|
||||
format_serror(
|
||||
&e.context("Failed to update swarm node").into(),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
|
||||
#[instrument(
|
||||
"RemoveSwarmStacks",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -153,11 +87,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -181,7 +111,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Remove Swarm Stacks",
|
||||
@@ -203,7 +133,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmServices {
|
||||
"RemoveSwarmServices",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -212,11 +142,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmServices {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -239,7 +165,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmServices {
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Remove Swarm Services",
|
||||
@@ -261,7 +187,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmConfig {
|
||||
"CreateSwarmConfig",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -270,11 +196,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmConfig {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -300,7 +222,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmConfig {
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Create Swarm Config",
|
||||
@@ -322,7 +244,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmConfig {
|
||||
"RotateSwarmConfig",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -331,11 +253,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmConfig {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -359,7 +277,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmConfig {
|
||||
{
|
||||
Ok(logs) => {
|
||||
update.logs.extend(logs);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Rotate Swarm Config",
|
||||
@@ -381,7 +299,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmConfigs {
|
||||
"RemoveSwarmConfigs",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -390,11 +308,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmConfigs {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -417,7 +331,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmConfigs {
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Remove Swarm Configs",
|
||||
@@ -439,7 +353,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmSecret {
|
||||
"CreateSwarmSecret",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -448,11 +362,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmSecret {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -479,7 +389,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmSecret {
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Create Swarm Secret",
|
||||
@@ -501,7 +411,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmSecret {
|
||||
"RotateSwarmSecret",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -510,11 +420,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmSecret {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -538,7 +444,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmSecret {
|
||||
{
|
||||
Ok(logs) => {
|
||||
update.logs.extend(logs);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Rotate Swarm Secret",
|
||||
@@ -560,7 +466,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmSecrets {
|
||||
"RemoveSwarmSecrets",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -569,11 +475,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmSecrets {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -596,7 +498,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmSecrets {
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Remove Swarm Secrets",
|
||||
|
||||
@@ -21,7 +21,6 @@ use komodo_client::{
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
update::{Log, Update},
|
||||
user::sync_user,
|
||||
@@ -54,7 +53,7 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
"RunSync",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
id = id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
sync = self.sync,
|
||||
@@ -64,11 +63,7 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
let RunSync {
|
||||
sync,
|
||||
@@ -139,36 +134,56 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
let Some(resource_type) = match_resource_type else {
|
||||
return Some(name_or_id);
|
||||
};
|
||||
macro_rules! resolve_id_to_name {
|
||||
($(($Variant:ident, $field:ident)),* $(,)?) => {
|
||||
match ObjectId::from_str(&name_or_id) {
|
||||
Ok(_) => match resource_type {
|
||||
$(
|
||||
ResourceTargetVariant::$Variant => all_resources
|
||||
.$field
|
||||
.get(&name_or_id)
|
||||
.map(|r| r.name.clone()),
|
||||
)*
|
||||
ResourceTargetVariant::System => None,
|
||||
},
|
||||
Err(_) => Some(name_or_id),
|
||||
}
|
||||
};
|
||||
match ObjectId::from_str(&name_or_id) {
|
||||
Ok(_) => match resource_type {
|
||||
ResourceTargetVariant::Swarm => all_resources
|
||||
.swarms
|
||||
.get(&name_or_id)
|
||||
.map(|s| s.name.clone()),
|
||||
ResourceTargetVariant::Server => all_resources
|
||||
.servers
|
||||
.get(&name_or_id)
|
||||
.map(|s| s.name.clone()),
|
||||
ResourceTargetVariant::Stack => all_resources
|
||||
.stacks
|
||||
.get(&name_or_id)
|
||||
.map(|s| s.name.clone()),
|
||||
ResourceTargetVariant::Deployment => all_resources
|
||||
.deployments
|
||||
.get(&name_or_id)
|
||||
.map(|d| d.name.clone()),
|
||||
ResourceTargetVariant::Build => all_resources
|
||||
.builds
|
||||
.get(&name_or_id)
|
||||
.map(|b| b.name.clone()),
|
||||
ResourceTargetVariant::Repo => all_resources
|
||||
.repos
|
||||
.get(&name_or_id)
|
||||
.map(|r| r.name.clone()),
|
||||
ResourceTargetVariant::Procedure => all_resources
|
||||
.procedures
|
||||
.get(&name_or_id)
|
||||
.map(|p| p.name.clone()),
|
||||
ResourceTargetVariant::Action => all_resources
|
||||
.actions
|
||||
.get(&name_or_id)
|
||||
.map(|p| p.name.clone()),
|
||||
ResourceTargetVariant::ResourceSync => all_resources
|
||||
.syncs
|
||||
.get(&name_or_id)
|
||||
.map(|s| s.name.clone()),
|
||||
ResourceTargetVariant::Builder => all_resources
|
||||
.builders
|
||||
.get(&name_or_id)
|
||||
.map(|b| b.name.clone()),
|
||||
ResourceTargetVariant::Alerter => all_resources
|
||||
.alerters
|
||||
.get(&name_or_id)
|
||||
.map(|a| a.name.clone()),
|
||||
ResourceTargetVariant::System => None,
|
||||
},
|
||||
Err(_) => Some(name_or_id),
|
||||
}
|
||||
// New resource types need to be added here manually.
|
||||
resolve_id_to_name!(
|
||||
(Server, servers),
|
||||
(Swarm, swarms),
|
||||
(Stack, stacks),
|
||||
(Deployment, deployments),
|
||||
(Build, builds),
|
||||
(Repo, repos),
|
||||
(Procedure, procedures),
|
||||
(Action, actions),
|
||||
(ResourceSync, syncs),
|
||||
(Builder, builders),
|
||||
(Alerter, alerters),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
@@ -216,39 +231,136 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
|
||||
let delete = sync.config.managed || sync.config.delete;
|
||||
|
||||
macro_rules! get_deltas {
|
||||
($(($var:ident, $Type:ident, $field:ident)),* $(,)?) => {
|
||||
$(
|
||||
let $var = if sync.config.include_resources {
|
||||
get_updates_for_execution::<$Type>(
|
||||
resources.$field,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
)*
|
||||
};
|
||||
}
|
||||
// New resource types need to be added here manually.
|
||||
get_deltas!(
|
||||
(server_deltas, Server, servers),
|
||||
(swarm_deltas, Swarm, swarms),
|
||||
(stack_deltas, Stack, stacks),
|
||||
(deployment_deltas, Deployment, deployments),
|
||||
(build_deltas, Build, builds),
|
||||
(repo_deltas, Repo, repos),
|
||||
(procedure_deltas, Procedure, procedures),
|
||||
(action_deltas, Action, actions),
|
||||
(builder_deltas, Builder, builders),
|
||||
(alerter_deltas, Alerter, alerters),
|
||||
(resource_sync_deltas, ResourceSync, resource_syncs),
|
||||
);
|
||||
let server_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Server>(
|
||||
resources.servers,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let stack_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Stack>(
|
||||
resources.stacks,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let deployment_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Deployment>(
|
||||
resources.deployments,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let build_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Build>(
|
||||
resources.builds,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let repo_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Repo>(
|
||||
resources.repos,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let procedure_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Procedure>(
|
||||
resources.procedures,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let action_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Action>(
|
||||
resources.actions,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let builder_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Builder>(
|
||||
resources.builders,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let alerter_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<Alerter>(
|
||||
resources.alerters,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let resource_sync_deltas = if sync.config.include_resources {
|
||||
get_updates_for_execution::<entities::sync::ResourceSync>(
|
||||
resources.resource_syncs,
|
||||
delete,
|
||||
match_resource_type,
|
||||
match_resources.as_deref(),
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
let (
|
||||
variables_to_create,
|
||||
@@ -286,7 +398,6 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
if deploy_cache.is_empty()
|
||||
&& resource_sync_deltas.no_changes()
|
||||
&& server_deltas.no_changes()
|
||||
&& swarm_deltas.no_changes()
|
||||
&& deployment_deltas.no_changes()
|
||||
&& stack_deltas.no_changes()
|
||||
&& build_deltas.no_changes()
|
||||
@@ -314,11 +425,7 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
return Ok(update);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// The ordering these are executed does matter, since
|
||||
// latter resources may depend on prior synced resources
|
||||
// already being updated with the declared state.
|
||||
// =====================================================
|
||||
// =================
|
||||
|
||||
// No deps
|
||||
maybe_extend(
|
||||
@@ -357,10 +464,6 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
);
|
||||
|
||||
// Dependent on server
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Swarm::execute_sync_updates(swarm_deltas).await,
|
||||
);
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Builder::execute_sync_updates(builder_deltas).await,
|
||||
|
||||
@@ -79,7 +79,7 @@ pub async fn handle_build_webhook<B: super::ExtractBranch>(
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -121,7 +121,7 @@ impl RepoExecution for CloneRepo {
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -145,7 +145,7 @@ impl RepoExecution for PullRepo {
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -169,7 +169,7 @@ impl RepoExecution for BuildRepo {
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -272,7 +272,7 @@ impl StackExecution for DeployStack {
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -290,7 +290,7 @@ impl StackExecution for DeployStack {
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -403,7 +403,7 @@ impl SyncExecution for RunSync {
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -517,7 +517,7 @@ pub async fn handle_procedure_webhook<B: super::ExtractBranch>(
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
@@ -583,7 +583,7 @@ pub async fn handle_action_webhook<B: super::ExtractBranch>(
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
|
||||
@@ -222,31 +222,8 @@ where
|
||||
.status_code(StatusCode::BAD_REQUEST)?;
|
||||
P::verify_secret(headers, body, R::custom_secret(&resource))
|
||||
.status_code(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
info!(
|
||||
resource_type = R::resource_type().to_string(),
|
||||
resource_id = id,
|
||||
source_ip = ip.to_string(),
|
||||
"Successfully authenticated incoming webhook"
|
||||
);
|
||||
|
||||
debug!(
|
||||
resource_type = R::resource_type().to_string(),
|
||||
resource_id = id,
|
||||
source_ip = ip.to_string(),
|
||||
"Webhook body: {body}"
|
||||
);
|
||||
|
||||
mogh_error::Result::Ok(resource)
|
||||
}
|
||||
.with_failure_rate_limit_using_ip(&GENERAL_RATE_LIMITER, &ip)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
warn!(
|
||||
resource_id = id,
|
||||
source_ip = ip.to_string(),
|
||||
"Incoming webhook failed | ERROR: {:#}",
|
||||
e.error
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,10 +38,7 @@ pub fn app() -> Router {
|
||||
.nest("/ws", ws::router())
|
||||
.nest("/client", ts_client::router())
|
||||
.layer(memory_session_layer(config))
|
||||
.fallback_service(serve_static_ui(
|
||||
&config.ui_path,
|
||||
config.ui_index_force_no_cache,
|
||||
))
|
||||
.fallback_service(serve_static_ui(&config.frontend_path))
|
||||
.layer(cors_layer(config))
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ pub struct ReadArgs {
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
|
||||
)]
|
||||
#[strum_discriminants(name(ReadRequestMethod), derive(Display))]
|
||||
#[strum_discriminants(name(ReadRequestVariant), derive(Display))]
|
||||
#[args(ReadArgs)]
|
||||
#[response(Response)]
|
||||
#[error(mogh_error::Error)]
|
||||
@@ -291,28 +291,18 @@ async fn handler(
|
||||
Json(request): Json<ReadRequest>,
|
||||
) -> mogh_error::Result<axum::response::Response> {
|
||||
let req_id = Uuid::new_v4();
|
||||
let method: ReadRequestMethod = (&request).into();
|
||||
let variant: ReadRequestVariant = (&request).into();
|
||||
|
||||
let user_id = user.id.clone();
|
||||
let username = user.username.clone();
|
||||
|
||||
trace!(
|
||||
req_id = req_id.to_string(),
|
||||
method = method.to_string(),
|
||||
user_id,
|
||||
username,
|
||||
"READ REQUEST",
|
||||
debug!(
|
||||
"READ REQUEST {req_id} | METHOD: {variant} | USER: {} ({})",
|
||||
user.username, user.id
|
||||
);
|
||||
|
||||
let res = request.resolve(&ReadArgs { user }).await;
|
||||
|
||||
if let Err(e) = &res {
|
||||
trace!(
|
||||
req_id = req_id.to_string(),
|
||||
method = method.to_string(),
|
||||
user_id,
|
||||
username,
|
||||
"READ REQUEST | ERROR: {:#}",
|
||||
debug!(
|
||||
"READ REQUEST {req_id} | METHOD: {variant} | ERROR: {:#}",
|
||||
e.error
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ impl Resolve<ReadArgs> for GetResourceSyncsSummary {
|
||||
for resource_sync in resource_syncs {
|
||||
res.total += 1;
|
||||
|
||||
if !(resource_sync.info.pending_deploys.is_empty()
|
||||
if !(resource_sync.info.pending_deploy.to_deploy == 0
|
||||
&& resource_sync.info.resource_updates.is_empty()
|
||||
&& resource_sync.info.variable_updates.is_empty()
|
||||
&& resource_sync.info.user_group_updates.is_empty())
|
||||
|
||||
+339
-119
@@ -1,8 +1,5 @@
|
||||
use anyhow::Context;
|
||||
use database::{
|
||||
bson::doc,
|
||||
mungos::{find::find_collect, mongodb::options::FindOptions},
|
||||
};
|
||||
use database::mungos::find::find_collect;
|
||||
use komodo_client::{
|
||||
api::read::{
|
||||
ExportAllResourcesToToml, ExportAllResourcesToTomlResponse,
|
||||
@@ -41,45 +38,122 @@ async fn get_all_targets(
|
||||
user: &User,
|
||||
) -> anyhow::Result<Vec<ResourceTarget>> {
|
||||
let mut targets = Vec::<ResourceTarget>::new();
|
||||
|
||||
let all_tags = if tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
|
||||
macro_rules! extend_targets {
|
||||
($($Type:ident),* $(,)?) => {
|
||||
$(
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<$Type>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::$Type(resource.id)),
|
||||
);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
extend_targets!(
|
||||
Alerter,
|
||||
Builder,
|
||||
Server,
|
||||
Swarm,
|
||||
Stack,
|
||||
Deployment,
|
||||
Build,
|
||||
Repo,
|
||||
Procedure,
|
||||
Action,
|
||||
ResourceSync,
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Alerter>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Alerter(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Builder>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Builder(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Server>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Server(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Stack>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Stack(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Deployment>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Deployment(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Build>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Build(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Repo>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Repo(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Procedure>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Procedure(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<Action>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|resource| ResourceTarget::Action(resource.id)),
|
||||
);
|
||||
targets.extend(
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
ResourceQuery::builder().tags(tags).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
// These will already be filtered by [ExportResourcesToToml]
|
||||
.map(|resource| ResourceTarget::ResourceSync(resource.id)),
|
||||
);
|
||||
|
||||
Ok(targets)
|
||||
}
|
||||
|
||||
@@ -113,7 +187,6 @@ impl Resolve<ReadArgs> for ExportAllResourcesToToml {
|
||||
targets,
|
||||
user_groups,
|
||||
include_variables: self.include_variables,
|
||||
existing: self.existing,
|
||||
}
|
||||
.resolve(args)
|
||||
.await
|
||||
@@ -129,64 +202,131 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
targets,
|
||||
user_groups,
|
||||
include_variables,
|
||||
existing,
|
||||
} = self;
|
||||
let mut res = ResourcesToml::default();
|
||||
let id_to_tags = get_id_to_tags(None).await?;
|
||||
let ReadArgs { user } = args;
|
||||
macro_rules! convert_target {
|
||||
($id:expr, $Type:ident, $field:ident) => {{
|
||||
let mut resource = get_check_permissions::<$Type>(
|
||||
&$id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
$Type::replace_ids(&mut resource);
|
||||
let (deploy, after) = existing
|
||||
.as_ref()
|
||||
.and_then(|e| {
|
||||
e.$field.iter().find(|r| r.name == resource.name)
|
||||
})
|
||||
.map(|r| (r.deploy, r.after.clone()))
|
||||
.unwrap_or_default();
|
||||
res.$field.push(convert_resource::<$Type>(
|
||||
resource,
|
||||
deploy,
|
||||
after,
|
||||
&id_to_tags,
|
||||
));
|
||||
}};
|
||||
}
|
||||
for target in targets {
|
||||
match target {
|
||||
ResourceTarget::Server(id) => {
|
||||
convert_target!(id, Server, servers)
|
||||
}
|
||||
ResourceTarget::Swarm(id) => {
|
||||
convert_target!(id, Swarm, swarms)
|
||||
let mut swarm = get_check_permissions::<Swarm>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Swarm::replace_ids(&mut swarm);
|
||||
res.swarms.push(convert_resource::<Swarm>(
|
||||
swarm,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::Server(id) => {
|
||||
let mut server = get_check_permissions::<Server>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Server::replace_ids(&mut server);
|
||||
res.servers.push(convert_resource::<Server>(
|
||||
server,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::Stack(id) => {
|
||||
convert_target!(id, Stack, stacks)
|
||||
let mut stack = get_check_permissions::<Stack>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Stack::replace_ids(&mut stack);
|
||||
res.stacks.push(convert_resource::<Stack>(
|
||||
stack,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::Deployment(id) => {
|
||||
convert_target!(id, Deployment, deployments)
|
||||
let mut deployment = get_check_permissions::<Deployment>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Deployment::replace_ids(&mut deployment);
|
||||
res.deployments.push(convert_resource::<Deployment>(
|
||||
deployment,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::Build(id) => {
|
||||
convert_target!(id, Build, builds)
|
||||
let mut build = get_check_permissions::<Build>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Build::replace_ids(&mut build);
|
||||
res.builds.push(convert_resource::<Build>(
|
||||
build,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::Repo(id) => {
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Repo::replace_ids(&mut repo);
|
||||
res.repos.push(convert_resource::<Repo>(
|
||||
repo,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::Repo(id) => convert_target!(id, Repo, repos),
|
||||
ResourceTarget::Procedure(id) => {
|
||||
convert_target!(id, Procedure, procedures)
|
||||
let mut procedure = get_check_permissions::<Procedure>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Procedure::replace_ids(&mut procedure);
|
||||
res.procedures.push(convert_resource::<Procedure>(
|
||||
procedure,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
));
|
||||
}
|
||||
ResourceTarget::Action(id) => {
|
||||
convert_target!(id, Action, actions)
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
convert_target!(id, Builder, builders)
|
||||
}
|
||||
ResourceTarget::Alerter(id) => {
|
||||
convert_target!(id, Alerter, alerters)
|
||||
let mut action = get_check_permissions::<Action>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Action::replace_ids(&mut action);
|
||||
res.actions.push(convert_resource::<Action>(
|
||||
action,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
));
|
||||
}
|
||||
ResourceTarget::ResourceSync(id) => {
|
||||
let mut sync = get_check_permissions::<ResourceSync>(
|
||||
@@ -209,6 +349,36 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
))
|
||||
}
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
let mut builder = get_check_permissions::<Builder>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Builder::replace_ids(&mut builder);
|
||||
res.builders.push(convert_resource::<Builder>(
|
||||
builder,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::Alerter(id) => {
|
||||
let mut alerter = get_check_permissions::<Alerter>(
|
||||
&id,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
Alerter::replace_ids(&mut alerter);
|
||||
res.alerters.push(convert_resource::<Alerter>(
|
||||
alerter,
|
||||
false,
|
||||
vec![],
|
||||
&id_to_tags,
|
||||
))
|
||||
}
|
||||
ResourceTarget::System(_) => continue,
|
||||
};
|
||||
}
|
||||
@@ -218,21 +388,18 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
.context("failed to add user groups")?;
|
||||
|
||||
if include_variables {
|
||||
res.variables = find_collect(
|
||||
&db_client().variables,
|
||||
None,
|
||||
FindOptions::builder().sort(doc! { "name": 1 }).build(),
|
||||
)
|
||||
.await
|
||||
.context("failed to get variables from db")?
|
||||
.into_iter()
|
||||
.map(|mut variable| {
|
||||
if !user.admin && variable.is_secret {
|
||||
variable.value = "#".repeat(variable.value.len())
|
||||
}
|
||||
variable
|
||||
})
|
||||
.collect();
|
||||
res.variables =
|
||||
find_collect(&db_client().variables, None, None)
|
||||
.await
|
||||
.context("failed to get variables from db")?
|
||||
.into_iter()
|
||||
.map(|mut variable| {
|
||||
if !user.admin && variable.is_secret {
|
||||
variable.value = "#".repeat(variable.value.len())
|
||||
}
|
||||
variable
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
let toml = serialize_resources_toml(res)
|
||||
@@ -267,32 +434,85 @@ fn serialize_resources_toml(
|
||||
) -> anyhow::Result<String> {
|
||||
let mut toml = String::new();
|
||||
|
||||
macro_rules! serialize_resources {
|
||||
($(($Type:ident, $field:ident, $header:literal)),* $(,)?) => {
|
||||
$(
|
||||
for resource in resources.$field {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str(concat!("[[",$header,"]]\n"));
|
||||
$Type::push_to_toml_string(resource, &mut toml)?;
|
||||
}
|
||||
)*
|
||||
};
|
||||
for server in resources.servers {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[server]]\n");
|
||||
Server::push_to_toml_string(server, &mut toml)?;
|
||||
}
|
||||
|
||||
for stack in resources.stacks {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[stack]]\n");
|
||||
Stack::push_to_toml_string(stack, &mut toml)?;
|
||||
}
|
||||
|
||||
for deployment in resources.deployments {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[deployment]]\n");
|
||||
Deployment::push_to_toml_string(deployment, &mut toml)?;
|
||||
}
|
||||
|
||||
for build in resources.builds {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[build]]\n");
|
||||
Build::push_to_toml_string(build, &mut toml)?;
|
||||
}
|
||||
|
||||
for repo in resources.repos {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[repo]]\n");
|
||||
Repo::push_to_toml_string(repo, &mut toml)?;
|
||||
}
|
||||
|
||||
for procedure in resources.procedures {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[procedure]]\n");
|
||||
Procedure::push_to_toml_string(procedure, &mut toml)?;
|
||||
}
|
||||
|
||||
for action in resources.actions {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[action]]\n");
|
||||
Action::push_to_toml_string(action, &mut toml)?;
|
||||
}
|
||||
|
||||
for alerter in resources.alerters {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[alerter]]\n");
|
||||
Alerter::push_to_toml_string(alerter, &mut toml)?;
|
||||
}
|
||||
|
||||
for builder in resources.builders {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[builder]]\n");
|
||||
Builder::push_to_toml_string(builder, &mut toml)?;
|
||||
}
|
||||
|
||||
for resource_sync in resources.resource_syncs {
|
||||
if !toml.is_empty() {
|
||||
toml.push_str("\n\n##\n\n");
|
||||
}
|
||||
toml.push_str("[[resource_sync]]\n");
|
||||
ResourceSync::push_to_toml_string(resource_sync, &mut toml)?;
|
||||
}
|
||||
serialize_resources!(
|
||||
(Server, servers, "server"),
|
||||
(Swarm, swarms, "swarm"),
|
||||
(Stack, stacks, "stack"),
|
||||
(Deployment, deployments, "deployment"),
|
||||
(Build, builds, "build"),
|
||||
(Repo, repos, "repo"),
|
||||
(Procedure, procedures, "procedure"),
|
||||
(Action, actions, "action"),
|
||||
(Alerter, alerters, "alerter"),
|
||||
(Builder, builders, "builder"),
|
||||
(ResourceSync, resource_syncs, "resource_sync"),
|
||||
);
|
||||
|
||||
for variable in &resources.variables {
|
||||
if !toml.is_empty() {
|
||||
|
||||
@@ -71,20 +71,9 @@ impl Resolve<ReadArgs> for ListUsers {
|
||||
anyhow!("this route is only accessable by admins").into(),
|
||||
);
|
||||
}
|
||||
let filter = match self.service_users {
|
||||
komodo_client::api::read::ServiceUserQueryBehavior::Include => {
|
||||
None
|
||||
}
|
||||
komodo_client::api::read::ServiceUserQueryBehavior::Exclude => {
|
||||
Some(doc! { "config.type": { "$ne": "Service" } })
|
||||
}
|
||||
komodo_client::api::read::ServiceUserQueryBehavior::Only => {
|
||||
Some(doc! { "config.type": "Service" })
|
||||
}
|
||||
};
|
||||
let mut users = find_collect(
|
||||
&db_client().users,
|
||||
filter,
|
||||
None,
|
||||
FindOptions::builder().sort(doc! { "username": 1 }).build(),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -2,11 +2,10 @@ use std::sync::OnceLock;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use database::mungos::{by_id::update_one_by_id, mongodb::bson::doc};
|
||||
use futures_util::{StreamExt as _, stream::FuturesOrdered};
|
||||
use komodo_client::{
|
||||
api::{execute::Deploy, write::*},
|
||||
entities::{
|
||||
Operation, ResourceTarget, SwarmOrServer,
|
||||
NoData, Operation, ResourceTarget, SwarmOrServer,
|
||||
alert::{Alert, AlertData, SeverityLevel},
|
||||
deployment::{
|
||||
Deployment, DeploymentImage, DeploymentInfo, DeploymentState,
|
||||
@@ -27,18 +26,15 @@ use periphery_client::api::{self, container::InspectContainer};
|
||||
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
api::execute::{self, ExecuteRequest, ExecutionResult},
|
||||
api::execute::{self, ExecuteRequest},
|
||||
helpers::{
|
||||
periphery_client,
|
||||
query::{get_deployment_state, get_swarm_or_server},
|
||||
query::get_deployment_state,
|
||||
registry_token,
|
||||
update::{add_update, make_update, poll_update_until_complete},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource::{
|
||||
self, list_full_for_user_using_pattern,
|
||||
setup_deployment_execution,
|
||||
},
|
||||
resource::{self, setup_deployment_execution},
|
||||
state::{
|
||||
action_states, db_client, deployment_status_cache,
|
||||
image_digest_cache, server_status_cache,
|
||||
@@ -363,14 +359,12 @@ impl Resolve<WriteArgs> for CheckDeploymentForUpdate {
|
||||
fields(
|
||||
operator = user.id,
|
||||
deployment = self.deployment,
|
||||
skip_auto_update = self.skip_auto_update,
|
||||
wait_for_auto_update = self.wait_for_auto_update,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Self::Response> {
|
||||
) -> mogh_error::Result<NoData> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// deployment should be able to do this.
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
@@ -388,8 +382,9 @@ impl Resolve<WriteArgs> for CheckDeploymentForUpdate {
|
||||
self.skip_auto_update,
|
||||
self.wait_for_auto_update,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.await?;
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,8 +409,6 @@ fn deployment_alert_sent_cache() -> &'static SetCache<String> {
|
||||
skip_all,
|
||||
fields(
|
||||
deployment = deployment.id,
|
||||
skip_auto_update,
|
||||
wait_for_auto_update,
|
||||
)
|
||||
)]
|
||||
pub async fn check_deployment_for_update_inner(
|
||||
@@ -424,19 +417,16 @@ pub async fn check_deployment_for_update_inner(
|
||||
skip_auto_update: bool,
|
||||
// Otherwise spawns task to run in background
|
||||
wait_for_auto_update: bool,
|
||||
) -> anyhow::Result<CheckDeploymentForUpdateResponse> {
|
||||
) -> anyhow::Result<bool> {
|
||||
let alert_cache = deployment_alert_sent_cache();
|
||||
|
||||
let (image, account, token) = match &deployment.config.image {
|
||||
let (image, account, token) = match deployment.config.image {
|
||||
DeploymentImage::Image { image } => {
|
||||
if image.contains('@') {
|
||||
// Images with a hardcoded digest can't have update.
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: false,
|
||||
});
|
||||
return Ok(false)
|
||||
}
|
||||
let domain = extract_registry_domain(image)?;
|
||||
let domain = extract_registry_domain(&image)?;
|
||||
let account =
|
||||
optional_string(&deployment.config.image_registry_account);
|
||||
let token = if let Some(account) = &account {
|
||||
@@ -450,15 +440,12 @@ pub async fn check_deployment_for_update_inner(
|
||||
alert_cache.remove(&deployment.id).await;
|
||||
// This method not used for build based deployments
|
||||
// as deployed version vs built version can be inferred from Updates.
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: false,
|
||||
});
|
||||
return Ok(false);
|
||||
}
|
||||
};
|
||||
|
||||
let latest_digest = image_digest_cache()
|
||||
.get(swarm_or_server, image, account, token)
|
||||
.get(&swarm_or_server, &image, account, token)
|
||||
.await?;
|
||||
|
||||
resource::update_info::<Deployment>(
|
||||
@@ -469,50 +456,39 @@ pub async fn check_deployment_for_update_inner(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Some((state, Some(current_digests))) =
|
||||
deployment_status_cache()
|
||||
.get(&deployment.id)
|
||||
.await
|
||||
.map(|s| (s.curr.state, s.curr.image_digests.clone()))
|
||||
let Some((state, Some(current_digest))) = deployment_status_cache()
|
||||
.get(&deployment.id)
|
||||
.await
|
||||
.map(|s| (s.curr.state, s.curr.image_digest.clone()))
|
||||
else {
|
||||
alert_cache.remove(&deployment.id).await;
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: false,
|
||||
});
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
// If not running or latest digest matches current, early return
|
||||
if !matches!(state, DeploymentState::Running)
|
||||
|| !latest_digest.update_available(¤t_digests)
|
||||
|| !latest_digest.update_available(¤t_digest)
|
||||
{
|
||||
alert_cache.remove(&deployment.id).await;
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: false,
|
||||
});
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if !skip_auto_update && deployment.config.auto_update {
|
||||
// Trigger deploy + alert
|
||||
|
||||
// Conservatively remove from alert cache so 'skip_auto_update'
|
||||
// doesn't cause alerts not to be sent on subsequent calls.
|
||||
alert_cache.remove(&deployment.id).await;
|
||||
|
||||
let swarm_id = swarm_or_server.swarm_id().map(str::to_string);
|
||||
let swarm_name = swarm_or_server.swarm_name().map(str::to_string);
|
||||
let server_id = swarm_or_server.server_id().map(str::to_string);
|
||||
let server_name =
|
||||
swarm_or_server.server_name().map(str::to_string);
|
||||
let id = deployment.id.clone();
|
||||
let name = deployment.name.clone();
|
||||
let image = image.clone();
|
||||
|
||||
// Conservatively remove from alert cache so 'skip_auto_update'
|
||||
// doesn't cause alerts not to be sent on subsequent calls.
|
||||
alert_cache.remove(&deployment.id).await;
|
||||
|
||||
let run = async move {
|
||||
match execute::inner_handler(
|
||||
ExecuteRequest::Deploy(Deploy {
|
||||
deployment: name.clone(),
|
||||
deployment: deployment.name.clone(),
|
||||
stop_signal: None,
|
||||
stop_time: None,
|
||||
}),
|
||||
@@ -520,45 +496,38 @@ pub async fn check_deployment_for_update_inner(
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(res) => {
|
||||
let ExecutionResult::Single(update) = res else {
|
||||
unreachable!()
|
||||
Ok(_) => {
|
||||
let ts = komodo_timestamp();
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: true,
|
||||
resolved_ts: ts.into(),
|
||||
level: SeverityLevel::Ok,
|
||||
target: ResourceTarget::Deployment(deployment.id.clone()),
|
||||
data: AlertData::DeploymentAutoUpdated {
|
||||
id: deployment.id.clone(),
|
||||
name: deployment.name.clone(),
|
||||
swarm_id,
|
||||
swarm_name,
|
||||
server_id,
|
||||
server_name,
|
||||
image: image.clone(),
|
||||
},
|
||||
};
|
||||
let Ok(update) =
|
||||
poll_update_until_complete(&update.id).await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if update.success {
|
||||
let ts = komodo_timestamp();
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: true,
|
||||
resolved_ts: ts.into(),
|
||||
level: SeverityLevel::Ok,
|
||||
target: ResourceTarget::Deployment(id.clone()),
|
||||
data: AlertData::DeploymentAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
swarm_id,
|
||||
swarm_name,
|
||||
server_id,
|
||||
server_name,
|
||||
image,
|
||||
},
|
||||
};
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"Failed to record DeploymentAutoUpdated to db | {e:#}"
|
||||
);
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"Failed to record DeploymentAutoUpdated to db | {e:#}"
|
||||
);
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to auto update Deployment {name} | {e:#}",)
|
||||
warn!(
|
||||
"Failed to auto update Deployment {} | {e:#}",
|
||||
deployment.name
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -571,10 +540,7 @@ pub async fn check_deployment_for_update_inner(
|
||||
} else {
|
||||
// Avoid spamming alerts
|
||||
if alert_cache.contains(&deployment.id).await {
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: true,
|
||||
});
|
||||
return Ok(true);
|
||||
}
|
||||
alert_cache.insert(deployment.id.clone()).await;
|
||||
let ts = komodo_timestamp();
|
||||
@@ -606,69 +572,5 @@ pub async fn check_deployment_for_update_inner(
|
||||
send_alerts(&[alert]).await;
|
||||
}
|
||||
|
||||
Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: !deployment.config.auto_update,
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for BatchCheckDeploymentForUpdate {
|
||||
#[instrument(
|
||||
"BatchCheckDeploymentForUpdate",
|
||||
skip_all,
|
||||
fields(
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
skip_auto_update = self.skip_auto_update,
|
||||
wait_for_auto_update = self.wait_for_auto_update,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let deployments = list_full_for_user_using_pattern::<Deployment>(
|
||||
&self.pattern,
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let res = deployments
|
||||
.into_iter()
|
||||
.map(|deployment| async move {
|
||||
let swarm_or_server = get_swarm_or_server(
|
||||
&deployment.config.swarm_id,
|
||||
&deployment.config.server_id,
|
||||
)
|
||||
.await?;
|
||||
swarm_or_server.verify_has_target().map_err(|e| e.error)?;
|
||||
check_deployment_for_update_inner(
|
||||
deployment,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
self.wait_for_auto_update,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.collect::<FuturesOrdered<_>>()
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|res| {
|
||||
res
|
||||
.inspect_err(|e| {
|
||||
warn!(
|
||||
"Failed to check deployment for update in batch run | {e:#}"
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.collect();
|
||||
Ok(res)
|
||||
}
|
||||
Ok(!deployment.config.auto_update)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ pub struct WriteArgs {
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
|
||||
)]
|
||||
#[strum_discriminants(name(WriteRequestMethod), derive(Display))]
|
||||
#[strum_discriminants(name(WriteRequestVariant), derive(Display))]
|
||||
#[args(WriteArgs)]
|
||||
#[response(Response)]
|
||||
#[error(mogh_error::Error)]
|
||||
@@ -95,7 +95,6 @@ pub enum WriteRequest {
|
||||
WriteStackFileContents(WriteStackFileContents),
|
||||
RefreshStackCache(RefreshStackCache),
|
||||
CheckStackForUpdate(CheckStackForUpdate),
|
||||
BatchCheckStackForUpdate(BatchCheckStackForUpdate),
|
||||
|
||||
// ==== DEPLOYMENT ====
|
||||
CreateDeployment(CreateDeployment),
|
||||
@@ -105,7 +104,6 @@ pub enum WriteRequest {
|
||||
UpdateDeployment(UpdateDeployment),
|
||||
RenameDeployment(RenameDeployment),
|
||||
CheckDeploymentForUpdate(CheckDeploymentForUpdate),
|
||||
BatchCheckDeploymentForUpdate(BatchCheckDeploymentForUpdate),
|
||||
|
||||
// ==== BUILD ====
|
||||
CreateBuild(CreateBuild),
|
||||
@@ -255,35 +253,19 @@ async fn task(
|
||||
request: WriteRequest,
|
||||
user: User,
|
||||
) -> mogh_error::Result<axum::response::Response> {
|
||||
let task_id = Uuid::new_v4();
|
||||
let method: WriteRequestMethod = (&request).into();
|
||||
let req_id = Uuid::new_v4();
|
||||
let variant: WriteRequestVariant = (&request).into();
|
||||
|
||||
let user_id = user.id.clone();
|
||||
let username = user.username.clone();
|
||||
|
||||
if !matches!(
|
||||
request,
|
||||
WriteRequest::SetLastSeenUpdate(_)
|
||||
| WriteRequest::PushRecentlyViewed(_)
|
||||
) {
|
||||
info!(
|
||||
task_id = task_id.to_string(),
|
||||
method = method.to_string(),
|
||||
user_id,
|
||||
username,
|
||||
"WRITE REQUEST",
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"WRITE REQUEST {req_id} | METHOD: {variant} | USER: {} ({})",
|
||||
user.username, user.id
|
||||
);
|
||||
|
||||
let res = request.resolve(&WriteArgs { user }).await;
|
||||
|
||||
if let Err(e) = &res {
|
||||
warn!(
|
||||
task_id = task_id.to_string(),
|
||||
method = method.to_string(),
|
||||
user_id,
|
||||
username,
|
||||
"WRITE REQUEST | ERROR: {:#}",
|
||||
"WRITE REQUEST {req_id} | METHOD: {variant} | ERROR: {:#}",
|
||||
e.error
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ use mogh_pki::EncodedKeyPair;
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
|
||||
use crate::{
|
||||
api::write::WriteArgs, helpers::query::get_all_tags,
|
||||
state::db_client,
|
||||
};
|
||||
use crate::{api::write::WriteArgs, state::db_client};
|
||||
|
||||
//
|
||||
|
||||
@@ -56,21 +53,6 @@ impl Resolve<WriteArgs> for CreateOnboardingKey {
|
||||
)?
|
||||
.public
|
||||
.into_inner();
|
||||
let tags = if self.tags.is_empty() {
|
||||
self.tags
|
||||
} else {
|
||||
// fix_tags by ensuring existence, and force replace with tag name.
|
||||
let all_tags = get_all_tags(None).await?;
|
||||
self
|
||||
.tags
|
||||
.into_iter()
|
||||
.filter_map(|tag| {
|
||||
let tag =
|
||||
all_tags.iter().find(|t| t.id == tag || t.name == tag)?;
|
||||
Some(tag.name.clone())
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let onboarding_key = OnboardingKey {
|
||||
public_key,
|
||||
name: self.name,
|
||||
@@ -78,8 +60,8 @@ impl Resolve<WriteArgs> for CreateOnboardingKey {
|
||||
onboarded: Default::default(),
|
||||
created_at: komodo_timestamp(),
|
||||
expires: self.expires,
|
||||
tags,
|
||||
privileged: self.privileged,
|
||||
tags: self.tags,
|
||||
fix_existing_servers: self.fix_existing_servers,
|
||||
copy_server: self.copy_server,
|
||||
create_builder: self.create_builder,
|
||||
};
|
||||
@@ -157,26 +139,11 @@ impl Resolve<WriteArgs> for UpdateOnboardingKey {
|
||||
}
|
||||
|
||||
if let Some(tags) = self.tags {
|
||||
let tags = if tags.is_empty() {
|
||||
tags
|
||||
} else {
|
||||
// fix_tags by ensuring existence, and force replace with tag name.
|
||||
let all_tags = get_all_tags(None).await?;
|
||||
tags
|
||||
.into_iter()
|
||||
.filter_map(|tag| {
|
||||
let tag = all_tags
|
||||
.iter()
|
||||
.find(|t| t.id == tag || t.name == tag)?;
|
||||
Some(tag.name.clone())
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
update.insert("tags", tags);
|
||||
}
|
||||
|
||||
if let Some(privileged) = self.privileged {
|
||||
update.insert("privileged", privileged);
|
||||
if let Some(fix_existing_servers) = self.fix_existing_servers {
|
||||
update.insert("fix_existing_servers", fix_existing_servers);
|
||||
}
|
||||
|
||||
if let Some(copy_server) = self.copy_server {
|
||||
|
||||
+51
-155
@@ -9,7 +9,6 @@ use database::{
|
||||
},
|
||||
};
|
||||
use formatting::format_serror;
|
||||
use futures_util::{StreamExt as _, stream::FuturesOrdered};
|
||||
use komodo_client::{
|
||||
api::{execute::DeployStack, write::*},
|
||||
entities::{
|
||||
@@ -33,15 +32,15 @@ use periphery_client::api::compose::{
|
||||
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
api::execute::{self, ExecuteRequest, ExecutionResult},
|
||||
api::execute::{self, ExecuteRequest},
|
||||
config::core_config,
|
||||
helpers::{
|
||||
query::get_swarm_or_server,
|
||||
stack_git_token, swarm_or_server_request,
|
||||
update::{add_update, make_update, poll_update_until_complete},
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource::{self, list_full_for_user_using_pattern},
|
||||
resource,
|
||||
stack::{
|
||||
remote::{RemoteComposeContents, get_repo_compose_contents},
|
||||
services::{
|
||||
@@ -563,16 +562,7 @@ impl Resolve<WriteArgs> for RefreshStackCache {
|
||||
|
||||
(services, Some(contents), Some(errors), None, None)
|
||||
} else {
|
||||
// This path is reached if the swarm / server is not available.
|
||||
// It carries over the last successful poll.
|
||||
(
|
||||
stack.info.latest_services,
|
||||
stack.info.remote_contents,
|
||||
stack.info.remote_errors,
|
||||
// Files on host can set hash / message back to None.
|
||||
None,
|
||||
None,
|
||||
)
|
||||
(vec![], None, None, None, None)
|
||||
}
|
||||
} else if !repo_empty {
|
||||
// ================
|
||||
@@ -686,7 +676,7 @@ impl Resolve<WriteArgs> for CheckStackForUpdate {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Self::Response> {
|
||||
) -> mogh_error::Result<NoData> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// stack should be able to do this.
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
@@ -705,8 +695,9 @@ impl Resolve<WriteArgs> for CheckStackForUpdate {
|
||||
self.wait_for_auto_update,
|
||||
self.skip_cache_refresh,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.await?;
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -740,7 +731,7 @@ pub async fn check_stack_for_update_inner(
|
||||
// Otherwise spawns task to run in background
|
||||
wait_for_auto_update: bool,
|
||||
skip_cache_refresh: bool,
|
||||
) -> anyhow::Result<CheckStackForUpdateResponse> {
|
||||
) -> anyhow::Result<Vec<StackServiceWithUpdate>> {
|
||||
if !skip_cache_refresh {
|
||||
(RefreshStackCache {
|
||||
stack: stack.clone(),
|
||||
@@ -759,28 +750,17 @@ pub async fn check_stack_for_update_inner(
|
||||
let cache = image_digest_cache();
|
||||
|
||||
for service in &mut stack.info.latest_services {
|
||||
// Prefer the image coming from deployed services
|
||||
// so it will be after any interpolation.
|
||||
let image = stack
|
||||
.info
|
||||
.deployed_services
|
||||
.as_ref()
|
||||
.and_then(|services| {
|
||||
services.iter().find_map(|deployed| {
|
||||
(deployed.service_name == service.service_name)
|
||||
.then_some(&deployed.image)
|
||||
})
|
||||
})
|
||||
.unwrap_or(&service.image);
|
||||
|
||||
if image.is_empty() ||
|
||||
if service.image.is_empty() ||
|
||||
// Images with a hardcoded digest can't have update.
|
||||
image.contains('@')
|
||||
service.image.contains('@')
|
||||
{
|
||||
service.image_digest = None;
|
||||
continue;
|
||||
}
|
||||
match cache.get(swarm_or_server, image, None, None).await {
|
||||
match cache
|
||||
.get(&swarm_or_server, &service.image, None, None)
|
||||
.await
|
||||
{
|
||||
Ok(digest) => service.image_digest = Some(digest),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
@@ -810,8 +790,8 @@ pub async fn check_stack_for_update_inner(
|
||||
alert_cache
|
||||
.retain(|(stack_id, _)| stack_id != &stack.id)
|
||||
.await;
|
||||
return Ok(CheckStackForUpdateResponse {
|
||||
services: extract_services_from_stack(&stack)
|
||||
return Ok(
|
||||
extract_services_from_stack(&stack)
|
||||
.into_iter()
|
||||
.map(|service| StackServiceWithUpdate {
|
||||
service: service.service_name,
|
||||
@@ -819,17 +799,15 @@ pub async fn check_stack_for_update_inner(
|
||||
update_available: false,
|
||||
})
|
||||
.collect(),
|
||||
stack: stack.id,
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
let StackState::Running = status.curr.state else {
|
||||
alert_cache
|
||||
.retain(|(stack_id, _)| stack_id != &stack.id)
|
||||
.await;
|
||||
return Ok(CheckStackForUpdateResponse {
|
||||
stack: stack.id,
|
||||
services: status
|
||||
return Ok(
|
||||
status
|
||||
.curr
|
||||
.services
|
||||
.iter()
|
||||
@@ -839,7 +817,7 @@ pub async fn check_stack_for_update_inner(
|
||||
update_available: false,
|
||||
})
|
||||
.collect(),
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
let mut services = Vec::new();
|
||||
@@ -851,7 +829,7 @@ pub async fn check_stack_for_update_inner(
|
||||
update_available: false,
|
||||
};
|
||||
|
||||
let Some(current_digests) = &service.image_digests else {
|
||||
let Some(current_digest) = &service.image_digest else {
|
||||
services.push(service_with_update);
|
||||
continue;
|
||||
};
|
||||
@@ -870,7 +848,7 @@ pub async fn check_stack_for_update_inner(
|
||||
};
|
||||
|
||||
service_with_update.update_available =
|
||||
latest_digest.update_available(current_digests);
|
||||
latest_digest.update_available(¤t_digest.0);
|
||||
|
||||
if service_with_update.update_available
|
||||
&& (skip_auto_update || !stack.config.auto_update)
|
||||
@@ -927,10 +905,7 @@ pub async fn check_stack_for_update_inner(
|
||||
|| !stack.config.auto_update
|
||||
|| services_with_update.is_empty()
|
||||
{
|
||||
return Ok(CheckStackForUpdateResponse {
|
||||
stack: stack.id,
|
||||
services,
|
||||
});
|
||||
return Ok(services);
|
||||
}
|
||||
|
||||
// Conservatively remove from alert cache so 'skip_auto_update'
|
||||
@@ -939,10 +914,7 @@ pub async fn check_stack_for_update_inner(
|
||||
.retain(|(stack_id, _)| stack_id != &stack.id)
|
||||
.await;
|
||||
|
||||
let deploy_services = if stack.config.auto_update_all_services
|
||||
// Swarm stacks don't support individual service deploy
|
||||
|| !stack.config.swarm_id.is_empty()
|
||||
{
|
||||
let deploy_services = if stack.config.auto_update_all_services {
|
||||
Vec::new()
|
||||
} else {
|
||||
services_with_update
|
||||
@@ -956,8 +928,6 @@ pub async fn check_stack_for_update_inner(
|
||||
let server_id = swarm_or_server.server_id().map(str::to_string);
|
||||
let server_name = swarm_or_server.server_name().map(str::to_string);
|
||||
|
||||
let stack_id = stack.id.clone();
|
||||
|
||||
let run = async move {
|
||||
match execute::inner_handler(
|
||||
ExecuteRequest::DeployStack(DeployStack {
|
||||
@@ -969,42 +939,33 @@ pub async fn check_stack_for_update_inner(
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(res) => {
|
||||
let ExecutionResult::Single(update) = res else {
|
||||
unreachable!()
|
||||
Ok(_) => {
|
||||
let ts = komodo_timestamp();
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: true,
|
||||
resolved_ts: ts.into(),
|
||||
level: SeverityLevel::Ok,
|
||||
target: ResourceTarget::Stack(stack.id.clone()),
|
||||
data: AlertData::StackAutoUpdated {
|
||||
id: stack.id.clone(),
|
||||
name: stack.name.clone(),
|
||||
swarm_id,
|
||||
swarm_name,
|
||||
server_id,
|
||||
server_name,
|
||||
images: services_with_update
|
||||
.iter()
|
||||
.map(|service| service.image.clone())
|
||||
.collect(),
|
||||
},
|
||||
};
|
||||
let Ok(update) = poll_update_until_complete(&update.id).await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if update.success {
|
||||
let ts = komodo_timestamp();
|
||||
let alert = Alert {
|
||||
id: Default::default(),
|
||||
ts,
|
||||
resolved: true,
|
||||
resolved_ts: ts.into(),
|
||||
level: SeverityLevel::Ok,
|
||||
target: ResourceTarget::Stack(stack.id.clone()),
|
||||
data: AlertData::StackAutoUpdated {
|
||||
id: stack.id.clone(),
|
||||
name: stack.name.clone(),
|
||||
swarm_id,
|
||||
swarm_name,
|
||||
server_id,
|
||||
server_name,
|
||||
images: services_with_update
|
||||
.iter()
|
||||
.map(|service| service.image.clone())
|
||||
.collect(),
|
||||
},
|
||||
};
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!("Failed to record StackAutoUpdated to db | {e:#}");
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!("Failed to record StackAutoUpdated to db | {e:#}");
|
||||
}
|
||||
send_alerts(&[alert]).await;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to auto update Stack {} | {e:#}", stack.name)
|
||||
@@ -1018,70 +979,5 @@ pub async fn check_stack_for_update_inner(
|
||||
tokio::spawn(run);
|
||||
}
|
||||
|
||||
Ok(CheckStackForUpdateResponse {
|
||||
stack: stack_id,
|
||||
services,
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for BatchCheckStackForUpdate {
|
||||
#[instrument(
|
||||
"BatchCheckStackForUpdate",
|
||||
skip_all,
|
||||
fields(
|
||||
operator = user.id,
|
||||
pattern = self.pattern,
|
||||
skip_auto_update = self.skip_auto_update,
|
||||
wait_for_auto_update = self.wait_for_auto_update,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let stacks = list_full_for_user_using_pattern::<Stack>(
|
||||
&self.pattern,
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let res = stacks
|
||||
.into_iter()
|
||||
.map(|stack| async move {
|
||||
let swarm_or_server = get_swarm_or_server(
|
||||
&stack.config.swarm_id,
|
||||
&stack.config.server_id,
|
||||
)
|
||||
.await?;
|
||||
swarm_or_server.verify_has_target().map_err(|e| e.error)?;
|
||||
check_stack_for_update_inner(
|
||||
stack.id,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
self.wait_for_auto_update,
|
||||
self.skip_cache_refresh,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.collect::<FuturesOrdered<_>>()
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|res| {
|
||||
res
|
||||
.inspect_err(|e| {
|
||||
warn!(
|
||||
"Failed to check stack for update in batch run | {e:#}"
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.collect();
|
||||
Ok(res)
|
||||
}
|
||||
Ok(services)
|
||||
}
|
||||
|
||||
+105
-46
@@ -26,8 +26,7 @@ use komodo_client::{
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::{ResourceSync, ResourceSyncInfo},
|
||||
sync::{ResourceSync, ResourceSyncInfo, SyncDeployUpdate},
|
||||
to_path_compatible_name,
|
||||
update::{Log, Update},
|
||||
user::sync_user,
|
||||
@@ -516,20 +515,11 @@ impl Resolve<WriteArgs> for CommitSync {
|
||||
None
|
||||
};
|
||||
|
||||
// Get the latest existing resources to preserve any meta values
|
||||
let RemoteResources { resources, .. } =
|
||||
crate::sync::remote::get_remote_resources(&sync, repo.as_ref())
|
||||
.await
|
||||
.context("failed to get remote resources")?;
|
||||
|
||||
let res = ExportAllResourcesToToml {
|
||||
include_resources: sync.config.include_resources,
|
||||
tags: sync.config.match_tags.clone(),
|
||||
include_variables: sync.config.include_variables,
|
||||
include_user_groups: sync.config.include_user_groups,
|
||||
existing: resources
|
||||
.inspect_err(|e| warn!("Existing resource TOML is unavailable, resource meta will not be preserved | ERROR: {e:#}"))
|
||||
.ok(),
|
||||
}
|
||||
.resolve(&ReadArgs {
|
||||
user: sync_user().to_owned(),
|
||||
@@ -785,40 +775,110 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
|
||||
|
||||
let mut diffs = Vec::new();
|
||||
|
||||
macro_rules! push_updates {
|
||||
($(($Type:ident, $field:ident)),* $(,)?) => {
|
||||
$(
|
||||
push_updates_for_view::<$Type>(
|
||||
resources.$field,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
)*
|
||||
};
|
||||
}
|
||||
// New resource types need to be added here manually.
|
||||
push_updates!(
|
||||
(Server, servers),
|
||||
(Swarm, swarms),
|
||||
(Stack, stacks),
|
||||
(Deployment, deployments),
|
||||
(Build, builds),
|
||||
(Repo, repos),
|
||||
(Procedure, procedures),
|
||||
(Action, actions),
|
||||
(Builder, builders),
|
||||
(Alerter, alerters),
|
||||
(ResourceSync, resource_syncs),
|
||||
);
|
||||
push_updates_for_view::<Server>(
|
||||
resources.servers,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Stack>(
|
||||
resources.stacks,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Deployment>(
|
||||
resources.deployments,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Build>(
|
||||
resources.builds,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Repo>(
|
||||
resources.repos,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Procedure>(
|
||||
resources.procedures,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Action>(
|
||||
resources.actions,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Builder>(
|
||||
resources.builders,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<Alerter>(
|
||||
resources.alerters,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
push_updates_for_view::<ResourceSync>(
|
||||
resources.resource_syncs,
|
||||
delete,
|
||||
None,
|
||||
None,
|
||||
&id_to_tags,
|
||||
&sync.config.match_tags,
|
||||
&mut diffs,
|
||||
)
|
||||
.await?;
|
||||
|
||||
(diffs, deploy_updates)
|
||||
} else {
|
||||
Default::default()
|
||||
(Vec::new(), SyncDeployUpdate::default())
|
||||
};
|
||||
|
||||
let variable_updates = if sync.config.include_variables {
|
||||
@@ -852,7 +912,7 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
|
||||
|
||||
let (
|
||||
resource_updates,
|
||||
(pending_deploys, pending_deploy_error),
|
||||
deploy_updates,
|
||||
variable_updates,
|
||||
user_group_updates,
|
||||
pending_error,
|
||||
@@ -868,7 +928,7 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
|
||||
};
|
||||
|
||||
let has_updates = !resource_updates.is_empty()
|
||||
|| !pending_deploys.is_empty()
|
||||
|| !deploy_updates.to_deploy == 0
|
||||
|| !variable_updates.is_empty()
|
||||
|| !user_group_updates.is_empty();
|
||||
|
||||
@@ -880,8 +940,7 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
|
||||
remote_errors: sync.info.remote_errors,
|
||||
pending_hash: sync.info.pending_hash,
|
||||
pending_message: sync.info.pending_message,
|
||||
pending_deploys,
|
||||
pending_deploy_error,
|
||||
pending_deploy: deploy_updates,
|
||||
resource_updates,
|
||||
variable_updates,
|
||||
user_group_updates,
|
||||
|
||||
@@ -3,12 +3,8 @@ use futures_util::{StreamExt as _, stream::FuturesUnordered};
|
||||
use komodo_client::{
|
||||
api::write::*,
|
||||
entities::{
|
||||
NoData,
|
||||
deployment::Deployment,
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
terminal::{Terminal, TerminalTarget},
|
||||
NoData, deployment::Deployment, permission::PermissionLevel,
|
||||
server::Server, stack::Stack, terminal::TerminalTarget,
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
@@ -51,35 +47,30 @@ impl Resolve<WriteArgs> for CreateTerminal {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Terminal> {
|
||||
) -> mogh_error::Result<NoData> {
|
||||
match self.target.clone() {
|
||||
TerminalTarget::Server { server } => {
|
||||
let server = server
|
||||
.context("Must provide 'target.params.server'")
|
||||
.status_code(StatusCode::BAD_REQUEST)?;
|
||||
create_server_terminal(self, server, user)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
create_server_terminal(self, server, user).await?;
|
||||
}
|
||||
TerminalTarget::Container { server, container } => {
|
||||
create_container_terminal(self, server, container, user)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.await?;
|
||||
}
|
||||
TerminalTarget::Stack { stack, service } => {
|
||||
let service = service
|
||||
.context("Must provide 'target.params.service'")
|
||||
.status_code(StatusCode::BAD_REQUEST)?;
|
||||
create_stack_service_terminal(self, stack, service, user)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
.await?;
|
||||
}
|
||||
TerminalTarget::Deployment { deployment } => {
|
||||
create_deployment_terminal(self, deployment, user)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
create_deployment_terminal(self, deployment, user).await?;
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +84,7 @@ async fn create_server_terminal(
|
||||
}: CreateTerminal,
|
||||
server: String,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<()> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
@@ -103,7 +94,7 @@ async fn create_server_terminal(
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
|
||||
let mut terminal = periphery
|
||||
periphery
|
||||
.request(api::terminal::CreateServerTerminal {
|
||||
name,
|
||||
command,
|
||||
@@ -112,12 +103,7 @@ async fn create_server_terminal(
|
||||
.await
|
||||
.context("Failed to create Server Terminal on Periphery")?;
|
||||
|
||||
// Fix server terminal target with server id
|
||||
terminal.target = TerminalTarget::Server {
|
||||
server: Some(server.id),
|
||||
};
|
||||
|
||||
Ok(terminal)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_container_terminal(
|
||||
@@ -125,7 +111,7 @@ async fn create_container_terminal(
|
||||
server: String,
|
||||
container: String,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<()> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
@@ -141,7 +127,7 @@ async fn create_stack_service_terminal(
|
||||
stack: String,
|
||||
service: String,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<()> {
|
||||
let (_, periphery, container) =
|
||||
get_stack_service_periphery_container(&stack, &service, user)
|
||||
.await?;
|
||||
@@ -152,7 +138,7 @@ async fn create_deployment_terminal(
|
||||
req: CreateTerminal,
|
||||
deployment: String,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<()> {
|
||||
let (_, periphery, container) =
|
||||
get_deployment_periphery_container(&deployment, user).await?;
|
||||
create_container_terminal_inner(req, &periphery, container).await
|
||||
|
||||
@@ -36,15 +36,6 @@ use super::WriteArgs;
|
||||
const RECENTLY_VIEWED_MAX: usize = 10;
|
||||
|
||||
impl Resolve<WriteArgs> for PushRecentlyViewed {
|
||||
#[instrument(
|
||||
"PushRecentlyViewed",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(
|
||||
user_id = user.id,
|
||||
resource = format!("{:?}", self.resource)
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user, .. }: &WriteArgs,
|
||||
@@ -88,12 +79,6 @@ impl Resolve<WriteArgs> for PushRecentlyViewed {
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for SetLastSeenUpdate {
|
||||
#[instrument(
|
||||
"SetLastSeenUpdate",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(user_id = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user, .. }: &WriteArgs,
|
||||
|
||||
@@ -5,7 +5,6 @@ use axum::{
|
||||
response::IntoResponse,
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use colored::Colorize;
|
||||
use futures_util::{SinkExt, StreamExt as _};
|
||||
use komodo_client::{
|
||||
api::terminal::ConnectTerminalQuery, entities::user::User,
|
||||
@@ -129,26 +128,14 @@ async fn forward_ws_channel(
|
||||
};
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
let message = format!("{}: {e:#}", "ERROR".red().bold());
|
||||
if message.contains("pty exited") {
|
||||
let _ = client_send
|
||||
.send(ws::Message::text(format!(
|
||||
"\n{} {}",
|
||||
"pty".bold(),
|
||||
"exited".red().bold()
|
||||
)))
|
||||
.await;
|
||||
} else {
|
||||
let _ = client_send
|
||||
.send(ws::Message::text(message.replace('\n', "\r\n")))
|
||||
.await;
|
||||
}
|
||||
let _ = client_send
|
||||
.send(ws::Message::text(format!("{e:#}")))
|
||||
.await;
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
let _ = client_send
|
||||
.send(ws::Message::text("\r\n\nSTREAM EOF"))
|
||||
.await;
|
||||
let _ =
|
||||
client_send.send(ws::Message::text("STREAM EOF")).await;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,12 +143,14 @@ impl AuthImpl for KomodoAuthImpl {
|
||||
}
|
||||
|
||||
fn host(&self) -> &str {
|
||||
&core_config().host
|
||||
static AUTH_HOST: LazyLock<String> =
|
||||
LazyLock::new(|| format!("{}/auth", core_config().host));
|
||||
&AUTH_HOST
|
||||
}
|
||||
|
||||
fn post_link_redirect(&self) -> &str {
|
||||
static POST_LINK_REDIRECT: LazyLock<String> =
|
||||
LazyLock::new(|| format!("{}/profile", core_config().host));
|
||||
LazyLock::new(|| format!("{}/settings", core_config().host));
|
||||
&POST_LINK_REDIRECT
|
||||
}
|
||||
|
||||
|
||||
+12
-19
@@ -293,6 +293,9 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
first_server_name: env
|
||||
.komodo_first_server_name
|
||||
.or(config.first_server_name),
|
||||
frontend_path: env
|
||||
.komodo_frontend_path
|
||||
.unwrap_or(config.frontend_path),
|
||||
jwt_ttl: env.komodo_jwt_ttl.unwrap_or(config.jwt_ttl),
|
||||
auth_rate_limit_disabled: env
|
||||
.komodo_auth_rate_limit_disabled
|
||||
@@ -309,9 +312,15 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
cors_allow_credentials: env
|
||||
.komodo_cors_allow_credentials
|
||||
.unwrap_or(config.cors_allow_credentials),
|
||||
session_allow_cross_site: env
|
||||
.komodo_session_allow_cross_site
|
||||
.unwrap_or(config.session_allow_cross_site),
|
||||
sync_directory: env
|
||||
.komodo_sync_directory
|
||||
.unwrap_or(config.sync_directory),
|
||||
repo_directory: env
|
||||
.komodo_repo_directory
|
||||
.unwrap_or(config.repo_directory),
|
||||
action_directory: env
|
||||
.komodo_action_directory
|
||||
.unwrap_or(config.action_directory),
|
||||
resource_poll_interval: env
|
||||
.komodo_resource_poll_interval
|
||||
.unwrap_or(config.resource_poll_interval),
|
||||
@@ -375,9 +384,6 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
.komodo_logging_location
|
||||
.unwrap_or(config.logging.location),
|
||||
ansi: env.komodo_logging_ansi.unwrap_or(config.logging.ansi),
|
||||
timestamps: env
|
||||
.komodo_logging_timestamps
|
||||
.unwrap_or(config.logging.timestamps),
|
||||
otlp_endpoint: env
|
||||
.komodo_logging_otlp_endpoint
|
||||
.unwrap_or(config.logging.otlp_endpoint),
|
||||
@@ -406,19 +412,6 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
ssl_cert_file: env
|
||||
.komodo_ssl_cert_file
|
||||
.unwrap_or(config.ssl_cert_file),
|
||||
ui_path: env.komodo_ui_path.unwrap_or(config.ui_path),
|
||||
ui_index_force_no_cache: env
|
||||
.komodo_ui_index_force_no_cache
|
||||
.unwrap_or(config.ui_index_force_no_cache),
|
||||
sync_directory: env
|
||||
.komodo_sync_directory
|
||||
.unwrap_or(config.sync_directory),
|
||||
repo_directory: env
|
||||
.komodo_repo_directory
|
||||
.unwrap_or(config.repo_directory),
|
||||
action_directory: env
|
||||
.komodo_action_directory
|
||||
.unwrap_or(config.action_directory),
|
||||
|
||||
// These can't be overridden on env
|
||||
secrets: config.secrets,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use komodo_client::entities::server::Server;
|
||||
use periphery_client::{
|
||||
CONNECTION_RETRY_SECONDS, transport::LoginMessage,
|
||||
};
|
||||
@@ -19,7 +18,6 @@ use transport::{
|
||||
|
||||
use crate::{
|
||||
config::{core_config, core_connection_query},
|
||||
monitor::refresh_server_cache,
|
||||
periphery::PeripheryClient,
|
||||
state::periphery_connections,
|
||||
};
|
||||
@@ -48,7 +46,6 @@ impl PeripheryConnectionArgs<'_> {
|
||||
|
||||
let responses = connection.responses.clone();
|
||||
|
||||
let _id = id.clone();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let ws = tokio::select! {
|
||||
@@ -73,14 +70,6 @@ impl PeripheryConnectionArgs<'_> {
|
||||
}
|
||||
};
|
||||
|
||||
debug!(
|
||||
host = identifiers.host(),
|
||||
query = core_connection_query(),
|
||||
sec_websocket_accept = accept.to_str().unwrap_or_default(),
|
||||
resource_id = connection.args.id,
|
||||
"[PERIPHERY AUTH] Zero trust identifiers"
|
||||
);
|
||||
|
||||
let identifiers = identifiers.build(
|
||||
accept.as_bytes(),
|
||||
core_connection_query().as_bytes(),
|
||||
@@ -97,18 +86,6 @@ impl PeripheryConnectionArgs<'_> {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Waits until after connection is handled then
|
||||
// force refreshes the server cache.
|
||||
let id = _id.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
let Ok(server) = crate::resource::get::<Server>(&id).await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
refresh_server_cache(&server, true).await;
|
||||
});
|
||||
|
||||
connection.handle_socket(socket, &mut receiver).await
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use axum::{
|
||||
@@ -42,7 +42,6 @@ use crate::{
|
||||
api::write::WriteArgs,
|
||||
config::core_keys,
|
||||
helpers::query::id_or_name_filter,
|
||||
monitor::refresh_server_cache,
|
||||
resource::KomodoResource,
|
||||
state::{db_client, periphery_connections},
|
||||
};
|
||||
@@ -130,14 +129,6 @@ async fn existing_server_handler(
|
||||
return;
|
||||
};
|
||||
|
||||
debug!(
|
||||
host = identifiers.host.to_str().unwrap_or_default(),
|
||||
query,
|
||||
sec_websocket_accept = identifiers.accept,
|
||||
resource_id = &server.id,
|
||||
"[PERIPHERY AUTH] Zero trust identifiers"
|
||||
);
|
||||
|
||||
let span = info_span!(
|
||||
"PeripheryLogin",
|
||||
server_id = server.id,
|
||||
@@ -164,7 +155,7 @@ async fn existing_server_handler(
|
||||
socket: &mut socket,
|
||||
identifiers: identifiers.build(query.as_bytes()),
|
||||
private_key: core_keys().load().private.as_str(),
|
||||
public_key_validator: OnboardingKeyValidator { privileged_required: true },
|
||||
public_key_validator: OnboardingKeyValidator { fix_existing_servers_required: true },
|
||||
should_close: true
|
||||
})
|
||||
.await
|
||||
@@ -202,13 +193,6 @@ async fn existing_server_handler(
|
||||
return;
|
||||
}
|
||||
|
||||
// Waits until after connection is handled then
|
||||
// force refreshes the server cache.
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
refresh_server_cache(&server, true).await;
|
||||
});
|
||||
|
||||
connection.handle_socket(socket, &mut receiver).await
|
||||
}))
|
||||
}
|
||||
@@ -235,7 +219,7 @@ async fn fix_existing_server_handler(
|
||||
socket: &mut socket,
|
||||
identifiers: identifiers.build(query.as_bytes()),
|
||||
private_key: core_keys().load().private.as_str(),
|
||||
public_key_validator: OnboardingKeyValidator { privileged_required: true },
|
||||
public_key_validator: OnboardingKeyValidator { fix_existing_servers_required: true },
|
||||
should_close: true,
|
||||
})
|
||||
.await
|
||||
@@ -307,13 +291,6 @@ async fn fix_server(
|
||||
} else {
|
||||
None
|
||||
},
|
||||
// Move address to external_address if not set.
|
||||
// This helps preserve container port link behavior.
|
||||
external_address: if !server.config.external_address.is_empty() {
|
||||
Some(server.config.address)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
if !config.is_none() {
|
||||
@@ -356,7 +333,7 @@ async fn onboard_new_server_handler(
|
||||
socket: &mut socket,
|
||||
identifiers: identifiers.build(query.as_bytes()),
|
||||
private_key: core_keys().load().private.as_str(),
|
||||
public_key_validator: OnboardingKeyValidator { privileged_required: false },
|
||||
public_key_validator: OnboardingKeyValidator { fix_existing_servers_required: false },
|
||||
should_close: true
|
||||
})
|
||||
.await
|
||||
@@ -520,7 +497,7 @@ async fn create_server_maybe_builder(
|
||||
}
|
||||
|
||||
struct OnboardingKeyValidator {
|
||||
privileged_required: bool,
|
||||
fix_existing_servers_required: bool,
|
||||
}
|
||||
|
||||
impl PublicKeyValidator for OnboardingKeyValidator {
|
||||
@@ -542,7 +519,9 @@ impl PublicKeyValidator for OnboardingKeyValidator {
|
||||
{
|
||||
return Err(anyhow!("Onboarding key is invalid"));
|
||||
}
|
||||
if self.privileged_required && !onboarding_key.privileged {
|
||||
if self.fix_existing_servers_required
|
||||
&& !onboarding_key.fix_existing_servers
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"Onboarding key not able to fix existing servers"
|
||||
));
|
||||
|
||||
@@ -39,34 +39,34 @@ impl ImageDigestCache {
|
||||
account: Option<String>,
|
||||
token: Option<String>,
|
||||
) -> anyhow::Result<ImageDigest> {
|
||||
if let Some((digest, valid_until)) = self.0.get(image).await
|
||||
if let Some((digest, valid_until)) = self.0.get(&image).await
|
||||
// Ensure the query time was within last 10 mins to use cache.
|
||||
&& valid_until > komodo_timestamp()
|
||||
{
|
||||
return Ok(digest);
|
||||
}
|
||||
|
||||
let digest = swarm_or_server_request(
|
||||
swarm_or_server,
|
||||
GetLatestImageDigest {
|
||||
name: image.clone(),
|
||||
account,
|
||||
token,
|
||||
},
|
||||
)
|
||||
.await?
|
||||
.digest;
|
||||
|
||||
let digest = ImageDigest::new(image, &digest);
|
||||
|
||||
self
|
||||
.0
|
||||
.insert(
|
||||
image,
|
||||
(digest.clone(), komodo_timestamp() + 10 * 60 * 1_000),
|
||||
} else {
|
||||
let digest = swarm_or_server_request(
|
||||
&swarm_or_server,
|
||||
GetLatestImageDigest {
|
||||
name: image.clone(),
|
||||
account,
|
||||
token,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
.await?
|
||||
.digest;
|
||||
|
||||
Ok(digest)
|
||||
let digest = ImageDigest::new(image, &digest);
|
||||
|
||||
self
|
||||
.0
|
||||
.insert(
|
||||
image,
|
||||
(digest.clone(), komodo_timestamp() + 10 * 60 * 1_000),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(digest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1157
-352
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ use komodo_client::{
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
terminal::{ContainerTerminalMode, Terminal, TerminalTarget},
|
||||
terminal::{ContainerTerminalMode, TerminalTarget},
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
@@ -84,7 +84,7 @@ async fn setup_server_target_for_user(
|
||||
if let Some(init) = init {
|
||||
periphery
|
||||
.request(api::terminal::CreateServerTerminal {
|
||||
name: Some(terminal.clone()),
|
||||
name: terminal.clone(),
|
||||
command: init.command,
|
||||
recreate: init.recreate,
|
||||
})
|
||||
@@ -131,7 +131,7 @@ async fn setup_container_target_for_user(
|
||||
if let Some(init) = init {
|
||||
create_container_terminal_inner(
|
||||
CreateTerminal {
|
||||
name: Some(terminal.clone()),
|
||||
name: terminal.clone(),
|
||||
target: target.clone(),
|
||||
command: init.command,
|
||||
mode: init.mode,
|
||||
@@ -166,7 +166,7 @@ async fn setup_stack_service_target_for_user(
|
||||
if let Some(init) = init {
|
||||
create_container_terminal_inner(
|
||||
CreateTerminal {
|
||||
name: Some(terminal.clone()),
|
||||
name: terminal.clone(),
|
||||
target: target.clone(),
|
||||
command: init.command,
|
||||
mode: init.mode,
|
||||
@@ -199,7 +199,7 @@ async fn setup_deployment_target_for_user(
|
||||
if let Some(init) = init {
|
||||
create_container_terminal_inner(
|
||||
CreateTerminal {
|
||||
name: Some(terminal.clone()),
|
||||
name: terminal.clone(),
|
||||
target: target.clone(),
|
||||
command: init.command,
|
||||
mode: init.mode,
|
||||
@@ -241,7 +241,7 @@ pub async fn create_container_terminal_inner(
|
||||
}: CreateTerminal,
|
||||
periphery: &PeripheryClient,
|
||||
container: String,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<()> {
|
||||
match mode.unwrap_or_default() {
|
||||
ContainerTerminalMode::Exec => periphery
|
||||
.request(periphery_client::api::terminal::CreateContainerExecTerminal {
|
||||
@@ -254,7 +254,7 @@ pub async fn create_container_terminal_inner(
|
||||
.await
|
||||
.context(
|
||||
"Failed to create Container Exec Terminal on Periphery",
|
||||
),
|
||||
)?,
|
||||
ContainerTerminalMode::Attach => periphery
|
||||
.request(periphery_client::api::terminal::CreateContainerAttachTerminal {
|
||||
name,
|
||||
@@ -265,8 +265,9 @@ pub async fn create_container_terminal_inner(
|
||||
.await
|
||||
.context(
|
||||
"Failed to create Container Attach Terminal on Periphery",
|
||||
),
|
||||
}
|
||||
)?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_stack_service_periphery_container(
|
||||
|
||||
+458
-161
@@ -1,5 +1,3 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context;
|
||||
use database::mungos::{
|
||||
by_id::{find_one_by_id, update_one_by_id},
|
||||
@@ -18,7 +16,7 @@ use komodo_client::entities::{
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
update::{Update, UpdateListItem, UpdateStatus},
|
||||
update::{Update, UpdateListItem},
|
||||
user::User,
|
||||
};
|
||||
|
||||
@@ -123,151 +121,464 @@ pub async fn init_execution_update(
|
||||
request: &ExecuteRequest,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Update> {
|
||||
macro_rules! init_execution_match {
|
||||
(
|
||||
resource: [$(($Variant:ident, $ResType:ident, $field:ident)),* $(,)?],
|
||||
batch: [$($BatchVariant:ident),* $(,)?],
|
||||
stack_service: [$(($StackVariant:ident, $ServiceOp:ident)),* $(,)?],
|
||||
system: [$($SysVariant:ident),* $(,)?],
|
||||
) => {
|
||||
match &request {
|
||||
$(
|
||||
ExecuteRequest::$Variant(data) => (
|
||||
Operation::$Variant,
|
||||
ResourceTarget::$ResType(
|
||||
resource::get::<$ResType>(&data.$field).await?.id,
|
||||
),
|
||||
),
|
||||
)*
|
||||
$(
|
||||
ExecuteRequest::$BatchVariant(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
)*
|
||||
$(
|
||||
ExecuteRequest::$StackVariant(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::$ServiceOp
|
||||
} else {
|
||||
Operation::$StackVariant
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
)*
|
||||
// DeployStackIfChanged doesn't have a service variant
|
||||
ExecuteRequest::DeployStackIfChanged(data) => (
|
||||
Operation::DeployStack,
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
$(
|
||||
ExecuteRequest::$SysVariant(_data) => {
|
||||
(Operation::$SysVariant, ResourceTarget::system())
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
let (operation, target) = match &request {
|
||||
// Swarm
|
||||
ExecuteRequest::RemoveSwarmNodes(data) => (
|
||||
Operation::RemoveSwarmNodes,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmStacks(data) => (
|
||||
Operation::RemoveSwarmStacks,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmServices(data) => (
|
||||
Operation::RemoveSwarmServices,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::CreateSwarmConfig(data) => (
|
||||
Operation::CreateSwarmConfig,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RotateSwarmConfig(data) => (
|
||||
Operation::RotateSwarmConfig,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmConfigs(data) => (
|
||||
Operation::RemoveSwarmConfigs,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::CreateSwarmSecret(data) => (
|
||||
Operation::CreateSwarmSecret,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RotateSwarmSecret(data) => (
|
||||
Operation::RotateSwarmSecret,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmSecrets(data) => (
|
||||
Operation::RemoveSwarmSecrets,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
let (operation, target) = init_execution_match!(
|
||||
resource: [
|
||||
// Swarm
|
||||
(RemoveSwarmNodes, Swarm, swarm),
|
||||
(UpdateSwarmNode, Swarm, swarm),
|
||||
(RemoveSwarmStacks, Swarm, swarm),
|
||||
(RemoveSwarmServices, Swarm, swarm),
|
||||
(CreateSwarmConfig, Swarm, swarm),
|
||||
(RotateSwarmConfig, Swarm, swarm),
|
||||
(RemoveSwarmConfigs, Swarm, swarm),
|
||||
(CreateSwarmSecret, Swarm, swarm),
|
||||
(RotateSwarmSecret, Swarm, swarm),
|
||||
(RemoveSwarmSecrets, Swarm, swarm),
|
||||
// Server
|
||||
(StartContainer, Server, server),
|
||||
(RestartContainer, Server, server),
|
||||
(PauseContainer, Server, server),
|
||||
(UnpauseContainer, Server, server),
|
||||
(StopContainer, Server, server),
|
||||
(DestroyContainer, Server, server),
|
||||
(StartAllContainers, Server, server),
|
||||
(RestartAllContainers, Server, server),
|
||||
(PauseAllContainers, Server, server),
|
||||
(UnpauseAllContainers, Server, server),
|
||||
(StopAllContainers, Server, server),
|
||||
(PruneContainers, Server, server),
|
||||
(DeleteNetwork, Server, server),
|
||||
(PruneNetworks, Server, server),
|
||||
(DeleteImage, Server, server),
|
||||
(PruneImages, Server, server),
|
||||
(DeleteVolume, Server, server),
|
||||
(PruneVolumes, Server, server),
|
||||
(PruneDockerBuilders, Server, server),
|
||||
(PruneBuildx, Server, server),
|
||||
(PruneSystem, Server, server),
|
||||
// Deployment
|
||||
(Deploy, Deployment, deployment),
|
||||
(PullDeployment, Deployment, deployment),
|
||||
(StartDeployment, Deployment, deployment),
|
||||
(RestartDeployment, Deployment, deployment),
|
||||
(PauseDeployment, Deployment, deployment),
|
||||
(UnpauseDeployment, Deployment, deployment),
|
||||
(StopDeployment, Deployment, deployment),
|
||||
(DestroyDeployment, Deployment, deployment),
|
||||
// Build
|
||||
(RunBuild, Build, build),
|
||||
(CancelBuild, Build, build),
|
||||
// Repo
|
||||
(CloneRepo, Repo, repo),
|
||||
(PullRepo, Repo, repo),
|
||||
(BuildRepo, Repo, repo),
|
||||
(CancelRepoBuild, Repo, repo),
|
||||
// Procedure
|
||||
(RunProcedure, Procedure, procedure),
|
||||
// Action
|
||||
(RunAction, Action, action),
|
||||
// Resource Sync
|
||||
(RunSync, ResourceSync, sync),
|
||||
// Stack (simple)
|
||||
(RunStackService, Stack, stack),
|
||||
// Alerter
|
||||
(TestAlerter, Alerter, alerter),
|
||||
],
|
||||
batch: [
|
||||
BatchDeploy,
|
||||
BatchDestroyDeployment,
|
||||
BatchRunBuild,
|
||||
BatchCloneRepo,
|
||||
BatchPullRepo,
|
||||
BatchBuildRepo,
|
||||
BatchRunProcedure,
|
||||
BatchRunAction,
|
||||
BatchDeployStack,
|
||||
BatchDeployStackIfChanged,
|
||||
BatchPullStack,
|
||||
BatchDestroyStack,
|
||||
],
|
||||
stack_service: [
|
||||
(DeployStack, DeployStackService),
|
||||
(PullStack, PullStackService),
|
||||
(StartStack, StartStackService),
|
||||
(RestartStack, RestartStackService),
|
||||
(PauseStack, PauseStackService),
|
||||
(UnpauseStack, UnpauseStackService),
|
||||
(StopStack, StopStackService),
|
||||
(DestroyStack, DestroyStackService),
|
||||
],
|
||||
system: [
|
||||
SendAlert,
|
||||
ClearRepoCache,
|
||||
BackupCoreDatabase,
|
||||
GlobalAutoUpdate,
|
||||
RotateAllServerKeys,
|
||||
RotateCoreKeys,
|
||||
],
|
||||
);
|
||||
// Server
|
||||
ExecuteRequest::StartContainer(data) => (
|
||||
Operation::StartContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RestartContainer(data) => (
|
||||
Operation::RestartContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseContainer(data) => (
|
||||
Operation::PauseContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseContainer(data) => (
|
||||
Operation::UnpauseContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopContainer(data) => (
|
||||
Operation::StopContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DestroyContainer(data) => (
|
||||
Operation::DestroyContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StartAllContainers(data) => (
|
||||
Operation::StartAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RestartAllContainers(data) => (
|
||||
Operation::RestartAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseAllContainers(data) => (
|
||||
Operation::PauseAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseAllContainers(data) => (
|
||||
Operation::UnpauseAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopAllContainers(data) => (
|
||||
Operation::StopAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneContainers(data) => (
|
||||
Operation::PruneContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DeleteNetwork(data) => (
|
||||
Operation::DeleteNetwork,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneNetworks(data) => (
|
||||
Operation::PruneNetworks,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DeleteImage(data) => (
|
||||
Operation::DeleteImage,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneImages(data) => (
|
||||
Operation::PruneImages,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DeleteVolume(data) => (
|
||||
Operation::DeleteVolume,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneVolumes(data) => (
|
||||
Operation::PruneVolumes,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneDockerBuilders(data) => (
|
||||
Operation::PruneDockerBuilders,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneBuildx(data) => (
|
||||
Operation::PruneBuildx,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneSystem(data) => (
|
||||
Operation::PruneSystem,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Deployment
|
||||
ExecuteRequest::Deploy(data) => (
|
||||
Operation::Deploy,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDeploy(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::PullDeployment(data) => (
|
||||
Operation::PullDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StartDeployment(data) => (
|
||||
Operation::StartDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RestartDeployment(data) => (
|
||||
Operation::RestartDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseDeployment(data) => (
|
||||
Operation::PauseDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseDeployment(data) => (
|
||||
Operation::UnpauseDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopDeployment(data) => (
|
||||
Operation::StopDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DestroyDeployment(data) => (
|
||||
Operation::DestroyDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDestroyDeployment(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
// Build
|
||||
ExecuteRequest::RunBuild(data) => (
|
||||
Operation::RunBuild,
|
||||
ResourceTarget::Build(
|
||||
resource::get::<Build>(&data.build).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchRunBuild(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::CancelBuild(data) => (
|
||||
Operation::CancelBuild,
|
||||
ResourceTarget::Build(
|
||||
resource::get::<Build>(&data.build).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Repo
|
||||
ExecuteRequest::CloneRepo(data) => (
|
||||
Operation::CloneRepo,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchCloneRepo(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::PullRepo(data) => (
|
||||
Operation::PullRepo,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchPullRepo(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::BuildRepo(data) => (
|
||||
Operation::BuildRepo,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchBuildRepo(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::CancelRepoBuild(data) => (
|
||||
Operation::CancelRepoBuild,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Procedure
|
||||
ExecuteRequest::RunProcedure(data) => (
|
||||
Operation::RunProcedure,
|
||||
ResourceTarget::Procedure(
|
||||
resource::get::<Procedure>(&data.procedure).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchRunProcedure(_) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
// Action
|
||||
ExecuteRequest::RunAction(data) => (
|
||||
Operation::RunAction,
|
||||
ResourceTarget::Action(
|
||||
resource::get::<Action>(&data.action).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchRunAction(_) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
// Resource Sync
|
||||
ExecuteRequest::RunSync(data) => (
|
||||
Operation::RunSync,
|
||||
ResourceTarget::ResourceSync(
|
||||
resource::get::<ResourceSync>(&data.sync).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Stack
|
||||
ExecuteRequest::DeployStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::DeployStackService
|
||||
} else {
|
||||
Operation::DeployStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDeployStack(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::DeployStackIfChanged(data) => (
|
||||
Operation::DeployStack,
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDeployStackIfChanged(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::StartStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::StartStackService
|
||||
} else {
|
||||
Operation::StartStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PullStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::PullStackService
|
||||
} else {
|
||||
Operation::PullStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchPullStack(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::RestartStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::RestartStackService
|
||||
} else {
|
||||
Operation::RestartStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::PauseStackService
|
||||
} else {
|
||||
Operation::PauseStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::UnpauseStackService
|
||||
} else {
|
||||
Operation::UnpauseStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::StopStackService
|
||||
} else {
|
||||
Operation::StopStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DestroyStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::DestroyStackService
|
||||
} else {
|
||||
Operation::DestroyStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDestroyStack(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
ExecuteRequest::RunStackService(data) => (
|
||||
Operation::RunStackService,
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Alerter
|
||||
ExecuteRequest::TestAlerter(data) => (
|
||||
Operation::TestAlerter,
|
||||
ResourceTarget::Alerter(
|
||||
resource::get::<Alerter>(&data.alerter).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::SendAlert(_) => {
|
||||
(Operation::SendAlert, ResourceTarget::system())
|
||||
}
|
||||
|
||||
// Maintenance
|
||||
ExecuteRequest::ClearRepoCache(_data) => {
|
||||
(Operation::ClearRepoCache, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::BackupCoreDatabase(_data) => {
|
||||
(Operation::BackupCoreDatabase, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::GlobalAutoUpdate(_data) => {
|
||||
(Operation::GlobalAutoUpdate, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::RotateAllServerKeys(_data) => {
|
||||
(Operation::RotateAllServerKeys, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::RotateCoreKeys(_data) => {
|
||||
(Operation::RotateCoreKeys, ResourceTarget::system())
|
||||
}
|
||||
};
|
||||
|
||||
let mut update = make_update(target, operation, user);
|
||||
update.in_progress();
|
||||
@@ -280,17 +591,3 @@ pub async fn init_execution_update(
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
|
||||
pub async fn poll_update_until_complete(
|
||||
update_id: &str,
|
||||
) -> anyhow::Result<Update> {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
let update = find_one_by_id(&db_client().updates, update_id)
|
||||
.await?
|
||||
.context("No update found at given ID")?;
|
||||
if matches!(update.status, UpdateStatus::Complete) {
|
||||
return Ok(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@ use std::collections::HashMap;
|
||||
use komodo_client::entities::{
|
||||
ResourceTarget,
|
||||
alert::{Alert, AlertData, SeverityLevel},
|
||||
deployment::{Deployment, DeploymentState},
|
||||
optional_string,
|
||||
deployment::{Deployment, DeploymentState}, optional_string,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -2,9 +2,7 @@ use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use anyhow::Context;
|
||||
use komodo_client::entities::{
|
||||
alert::AlertDataVariant, permission::PermissionLevel,
|
||||
resource::ResourceQuery, server::Server, swarm::Swarm,
|
||||
user::system_user,
|
||||
alert::AlertDataVariant, permission::PermissionLevel, resource::ResourceQuery, server::Server, swarm::Swarm, user::system_user
|
||||
};
|
||||
|
||||
use crate::resource;
|
||||
|
||||
@@ -51,7 +51,7 @@ pub async fn alert_servers(
|
||||
|
||||
let mut alerts_to_open = Vec::<(Alert, SendAlerts)>::new();
|
||||
let mut alerts_to_update = Vec::<(Alert, SendAlerts)>::new();
|
||||
let mut alerts_to_close = Vec::<(Alert, SendAlerts)>::new();
|
||||
let mut alert_ids_to_close = Vec::<(Alert, SendAlerts)>::new();
|
||||
|
||||
let buffer = alert_buffer();
|
||||
|
||||
@@ -66,21 +66,15 @@ pub async fn alert_servers(
|
||||
let in_maintenance =
|
||||
is_in_maintenance(&server.config.maintenance_windows, ts);
|
||||
|
||||
// ===============
|
||||
// SERVER HEALTH
|
||||
// ===============
|
||||
// ===================
|
||||
// SERVER HEALTH
|
||||
// ===================
|
||||
let health_alert = server_alerts.as_ref().and_then(|alerts| {
|
||||
alerts.get(&AlertDataVariant::ServerUnreachable)
|
||||
});
|
||||
match (
|
||||
server_status.state,
|
||||
health_alert,
|
||||
server.config.send_unreachable_alerts,
|
||||
) {
|
||||
// Skip alerting if alerts disabled
|
||||
(ServerState::NotOk, None, false) => {}
|
||||
(ServerState::NotOk, None, true) => {
|
||||
// Only open unreachable alert if unreachable alerts enabled and not in maintenance and buffer is ready
|
||||
match (server_status.state, health_alert) {
|
||||
(ServerState::NotOk, None) => {
|
||||
// Only open unreachable alert if not in maintenance and buffer is ready
|
||||
if !in_maintenance
|
||||
&& buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
@@ -105,14 +99,19 @@ pub async fn alert_servers(
|
||||
.push((alert, server.config.send_unreachable_alerts))
|
||||
}
|
||||
}
|
||||
(ServerState::NotOk, Some(alert), true) => {
|
||||
(ServerState::NotOk, Some(alert)) => {
|
||||
// update alert err
|
||||
let mut alert = alert.clone();
|
||||
let (id, name, region) = match alert.data {
|
||||
AlertData::ServerUnreachable {
|
||||
id, name, region, ..
|
||||
} => (id, name, region),
|
||||
_ => unreachable!(),
|
||||
data => {
|
||||
error!(
|
||||
"got incorrect alert data in ServerStatus handler. got {data:?}"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
alert.data = AlertData::ServerUnreachable {
|
||||
id,
|
||||
@@ -125,30 +124,23 @@ pub async fn alert_servers(
|
||||
alerts_to_update.push((alert, false));
|
||||
}
|
||||
|
||||
(ServerState::NotOk, Some(alert), false) => {
|
||||
alerts_to_close.push((
|
||||
alert.clone(),
|
||||
server.config.send_unreachable_alerts,
|
||||
));
|
||||
}
|
||||
|
||||
// Close an open alert
|
||||
(ServerState::Ok | ServerState::Disabled, Some(alert), _) => {
|
||||
alerts_to_close.push((
|
||||
(ServerState::Ok | ServerState::Disabled, Some(alert)) => {
|
||||
alert_ids_to_close.push((
|
||||
alert.clone(),
|
||||
server.config.send_unreachable_alerts,
|
||||
));
|
||||
}
|
||||
(ServerState::Ok | ServerState::Disabled, None, _) => buffer
|
||||
(ServerState::Ok | ServerState::Disabled, None) => buffer
|
||||
.reset(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerUnreachable,
|
||||
),
|
||||
}
|
||||
|
||||
// ========================
|
||||
// ===================
|
||||
// SERVER VERSION MISMATCH
|
||||
// ========================
|
||||
// ===================
|
||||
let core_version = env!("CARGO_PKG_VERSION");
|
||||
let mismatched_server_version =
|
||||
if server_status.state != ServerState::Ok {
|
||||
@@ -166,14 +158,9 @@ pub async fn alert_servers(
|
||||
alerts.get(&AlertDataVariant::ServerVersionMismatch)
|
||||
});
|
||||
|
||||
match (
|
||||
mismatched_server_version,
|
||||
version_alert,
|
||||
server.config.send_version_mismatch_alerts,
|
||||
) {
|
||||
(Some(_), None, false) => {}
|
||||
(Some(version), None, true) => {
|
||||
// Only open version mismatch alert if version mismatch alerts enabled and not in maintenance and buffer is ready
|
||||
match (mismatched_server_version, version_alert) {
|
||||
(Some(version), None) => {
|
||||
// Only open version mismatch alert if not in maintenance and buffer is ready
|
||||
if !in_maintenance
|
||||
&& buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
@@ -200,47 +187,27 @@ pub async fn alert_servers(
|
||||
.push((alert, server.config.send_version_mismatch_alerts))
|
||||
}
|
||||
}
|
||||
(Some(version), Some(alert), true) => {
|
||||
// Update existing alert with current version info if changed
|
||||
let (alert_server_version, alert_core_version) =
|
||||
match &alert.data {
|
||||
AlertData::ServerVersionMismatch {
|
||||
server_version,
|
||||
core_version,
|
||||
..
|
||||
} => (server_version, core_version),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if alert_server_version != version
|
||||
|| alert_core_version != core_version
|
||||
{
|
||||
let mut alert = alert.clone();
|
||||
alert.data = AlertData::ServerVersionMismatch {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
server_version: version.clone(),
|
||||
core_version: core_version.to_string(),
|
||||
};
|
||||
// Don't send notification for updates
|
||||
alerts_to_update.push((alert, false));
|
||||
}
|
||||
(Some(version), Some(alert)) => {
|
||||
// Update existing alert with current version info
|
||||
let mut alert = alert.clone();
|
||||
alert.data = AlertData::ServerVersionMismatch {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
region: optional_string(&server.config.region),
|
||||
server_version: version.clone(),
|
||||
core_version: core_version.to_string(),
|
||||
};
|
||||
// Don't send notification for updates
|
||||
alerts_to_update.push((alert, false));
|
||||
}
|
||||
(Some(_), Some(alert), false) => {
|
||||
alerts_to_close.push((
|
||||
alert.clone(),
|
||||
server.config.send_version_mismatch_alerts,
|
||||
));
|
||||
}
|
||||
(None, Some(alert), _) => {
|
||||
(None, Some(alert)) => {
|
||||
// Version is now correct, close the alert
|
||||
alerts_to_close.push((
|
||||
alert_ids_to_close.push((
|
||||
alert.clone(),
|
||||
server.config.send_version_mismatch_alerts,
|
||||
));
|
||||
}
|
||||
(None, None, _) => {
|
||||
(None, None) => {
|
||||
// Reset buffer state when no mismatch and no alert
|
||||
buffer.reset(
|
||||
server_status.id.clone(),
|
||||
@@ -260,25 +227,10 @@ pub async fn alert_servers(
|
||||
.as_ref()
|
||||
.and_then(|alerts| alerts.get(&AlertDataVariant::ServerCpu))
|
||||
.cloned();
|
||||
match (
|
||||
health.cpu.level,
|
||||
cpu_alert,
|
||||
health.cpu.should_close_alert,
|
||||
server.config.send_cpu_alerts,
|
||||
) {
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
None,
|
||||
_,
|
||||
false,
|
||||
) => {}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
None,
|
||||
_,
|
||||
true,
|
||||
) => {
|
||||
// Only open CPU alert if cpu alerts enabled and not in maintenance and buffer is ready
|
||||
match (health.cpu.level, cpu_alert, health.cpu.should_close_alert)
|
||||
{
|
||||
(SeverityLevel::Warning | SeverityLevel::Critical, None, _) => {
|
||||
// Only open CPU alert if not in maintenance and buffer is ready
|
||||
if !in_maintenance
|
||||
&& buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
@@ -310,7 +262,6 @@ pub async fn alert_servers(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
Some(mut alert),
|
||||
_,
|
||||
true,
|
||||
) => {
|
||||
// modify alert level only if it has increased and not in maintenance
|
||||
if !in_maintenance && alert.level < health.cpu.level {
|
||||
@@ -329,15 +280,8 @@ pub async fn alert_servers(
|
||||
.push((alert, server.config.send_cpu_alerts));
|
||||
}
|
||||
}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
Some(alert),
|
||||
_,
|
||||
false,
|
||||
) => {
|
||||
alerts_to_close.push((alert, server.config.send_cpu_alerts));
|
||||
}
|
||||
(SeverityLevel::Ok, Some(mut alert), true, _) => {
|
||||
(SeverityLevel::Ok, Some(alert), true) => {
|
||||
let mut alert = alert.clone();
|
||||
alert.data = AlertData::ServerCpu {
|
||||
id: server_status.id.clone(),
|
||||
name: server.name.clone(),
|
||||
@@ -348,9 +292,10 @@ pub async fn alert_servers(
|
||||
.map(|s| s.cpu_perc as f64)
|
||||
.unwrap_or(0.0),
|
||||
};
|
||||
alerts_to_close.push((alert, server.config.send_cpu_alerts))
|
||||
alert_ids_to_close
|
||||
.push((alert, server.config.send_cpu_alerts))
|
||||
}
|
||||
(SeverityLevel::Ok, _, _, _) => buffer
|
||||
(SeverityLevel::Ok, _, _) => buffer
|
||||
.reset(server_status.id.clone(), AlertDataVariant::ServerCpu),
|
||||
}
|
||||
|
||||
@@ -361,25 +306,10 @@ pub async fn alert_servers(
|
||||
.as_ref()
|
||||
.and_then(|alerts| alerts.get(&AlertDataVariant::ServerMem))
|
||||
.cloned();
|
||||
match (
|
||||
health.mem.level,
|
||||
mem_alert,
|
||||
health.mem.should_close_alert,
|
||||
server.config.send_mem_alerts,
|
||||
) {
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
None,
|
||||
_,
|
||||
false,
|
||||
) => {}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
None,
|
||||
_,
|
||||
true,
|
||||
) => {
|
||||
// Only open memory alert if memory alerts enabled and not in maintenance and buffer is ready
|
||||
match (health.mem.level, mem_alert, health.mem.should_close_alert)
|
||||
{
|
||||
(SeverityLevel::Warning | SeverityLevel::Critical, None, _) => {
|
||||
// Only open memory alert if not in maintenance and buffer is ready
|
||||
if !in_maintenance
|
||||
&& buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
@@ -416,7 +346,6 @@ pub async fn alert_servers(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
Some(mut alert),
|
||||
_,
|
||||
true,
|
||||
) => {
|
||||
// modify alert level only if it has increased and not in maintenance
|
||||
if !in_maintenance && alert.level < health.mem.level {
|
||||
@@ -440,16 +369,7 @@ pub async fn alert_servers(
|
||||
.push((alert, server.config.send_mem_alerts));
|
||||
}
|
||||
}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
Some(alert),
|
||||
_,
|
||||
false,
|
||||
) => {
|
||||
alerts_to_close
|
||||
.push((alert.clone(), server.config.send_mem_alerts));
|
||||
}
|
||||
(SeverityLevel::Ok, Some(alert), true, _) => {
|
||||
(SeverityLevel::Ok, Some(alert), true) => {
|
||||
let mut alert = alert.clone();
|
||||
alert.data = AlertData::ServerMem {
|
||||
id: server_status.id.clone(),
|
||||
@@ -466,9 +386,10 @@ pub async fn alert_servers(
|
||||
.map(|s| s.mem_used_gb)
|
||||
.unwrap_or(0.0),
|
||||
};
|
||||
alerts_to_close.push((alert, server.config.send_mem_alerts))
|
||||
alert_ids_to_close
|
||||
.push((alert, server.config.send_mem_alerts))
|
||||
}
|
||||
(SeverityLevel::Ok, _, _, _) => buffer
|
||||
(SeverityLevel::Ok, _, _) => buffer
|
||||
.reset(server_status.id.clone(), AlertDataVariant::ServerMem),
|
||||
}
|
||||
|
||||
@@ -484,25 +405,13 @@ pub async fn alert_servers(
|
||||
.as_ref()
|
||||
.and_then(|alerts| alerts.get(path))
|
||||
.cloned();
|
||||
match (
|
||||
health.level,
|
||||
disk_alert,
|
||||
health.should_close_alert,
|
||||
server.config.send_disk_alerts,
|
||||
) {
|
||||
match (health.level, disk_alert, health.should_close_alert) {
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
None,
|
||||
_,
|
||||
false,
|
||||
) => {}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
None,
|
||||
_,
|
||||
true,
|
||||
) => {
|
||||
// Only open disk alert if disk alerts enabled and not in maintenance and buffer is ready
|
||||
// Only open disk alert if not in maintenance and buffer is ready
|
||||
if !in_maintenance
|
||||
&& buffer.ready_to_open(
|
||||
server_status.id.clone(),
|
||||
@@ -541,7 +450,6 @@ pub async fn alert_servers(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
Some(mut alert),
|
||||
_,
|
||||
true,
|
||||
) => {
|
||||
// modify alert level only if it has increased and not in maintenance
|
||||
if !in_maintenance && health.level < alert.level {
|
||||
@@ -562,16 +470,8 @@ pub async fn alert_servers(
|
||||
.push((alert, server.config.send_disk_alerts));
|
||||
}
|
||||
}
|
||||
(
|
||||
SeverityLevel::Warning | SeverityLevel::Critical,
|
||||
Some(alert),
|
||||
_,
|
||||
false,
|
||||
) => {
|
||||
alerts_to_close
|
||||
.push((alert, server.config.send_disk_alerts));
|
||||
}
|
||||
(SeverityLevel::Ok, Some(mut alert), true, _) => {
|
||||
(SeverityLevel::Ok, Some(alert), true) => {
|
||||
let mut alert = alert.clone();
|
||||
let disk =
|
||||
server_status.system_stats.as_ref().and_then(|stats| {
|
||||
stats.disks.iter().find(|disk| disk.mount == *path)
|
||||
@@ -585,10 +485,10 @@ pub async fn alert_servers(
|
||||
total_gb: disk.map(|d| d.total_gb).unwrap_or_default(),
|
||||
used_gb: disk.map(|d| d.used_gb).unwrap_or_default(),
|
||||
};
|
||||
alerts_to_close
|
||||
alert_ids_to_close
|
||||
.push((alert, server.config.send_disk_alerts))
|
||||
}
|
||||
(SeverityLevel::Ok, _, _, _) => buffer.reset(
|
||||
(SeverityLevel::Ok, _, _) => buffer.reset(
|
||||
server_status.id.clone(),
|
||||
AlertDataVariant::ServerDisk,
|
||||
),
|
||||
@@ -601,7 +501,7 @@ pub async fn alert_servers(
|
||||
if !health.disks.contains_key(path) {
|
||||
let mut alert = alert.clone();
|
||||
alert.level = SeverityLevel::Ok;
|
||||
alerts_to_close
|
||||
alert_ids_to_close
|
||||
.push((alert, server.config.send_disk_alerts));
|
||||
}
|
||||
}
|
||||
@@ -611,7 +511,7 @@ pub async fn alert_servers(
|
||||
tokio::join!(
|
||||
open_new_alerts(&alerts_to_open),
|
||||
update_alerts(&alerts_to_update),
|
||||
resolve_alerts(&alerts_to_close),
|
||||
resolve_alerts(&alert_ids_to_close),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,9 @@ pub async fn alert_stacks(
|
||||
let data = AlertData::StackStateChange {
|
||||
id: status.curr.id.clone(),
|
||||
name: stack.name,
|
||||
swarm_name: swarm_names.get(&stack.config.swarm_id).cloned(),
|
||||
swarm_name: swarm_names
|
||||
.get(&stack.config.swarm_id)
|
||||
.cloned(),
|
||||
swarm_id: optional_string(stack.config.swarm_id),
|
||||
server_name: server_names
|
||||
.get(&stack.config.server_id)
|
||||
|
||||
@@ -47,7 +47,7 @@ pub async fn alert_swarms(
|
||||
|
||||
let mut alerts_to_open = Vec::<(Alert, SendAlerts)>::new();
|
||||
let mut alerts_to_update = Vec::<(Alert, SendAlerts)>::new();
|
||||
let mut alerts_to_close = Vec::<(Alert, SendAlerts)>::new();
|
||||
let mut alert_ids_to_close = Vec::<(Alert, SendAlerts)>::new();
|
||||
|
||||
let buffer = alert_buffer();
|
||||
|
||||
@@ -68,26 +68,14 @@ pub async fn alert_swarms(
|
||||
let health_alert = swarm_alerts.as_ref().and_then(|alerts| {
|
||||
alerts.get(&AlertDataVariant::SwarmUnhealthy)
|
||||
});
|
||||
match (
|
||||
swarm_status.state,
|
||||
health_alert,
|
||||
swarm.config.send_unhealthy_alerts,
|
||||
) {
|
||||
match (swarm_status.state, health_alert) {
|
||||
(
|
||||
SwarmState::Unhealthy
|
||||
| SwarmState::Down
|
||||
| SwarmState::Unknown,
|
||||
None,
|
||||
false,
|
||||
) => {}
|
||||
(
|
||||
SwarmState::Unhealthy
|
||||
| SwarmState::Down
|
||||
| SwarmState::Unknown,
|
||||
None,
|
||||
true,
|
||||
) => {
|
||||
// Only open unhealthy alert if unhealthy alerts enabled and not in maintenance and buffer is ready
|
||||
// Only open unreachable alert if not in maintenance and buffer is ready
|
||||
if !in_maintenance
|
||||
&& buffer.ready_to_open(
|
||||
swarm_status.id.clone(),
|
||||
@@ -116,7 +104,6 @@ pub async fn alert_swarms(
|
||||
| SwarmState::Down
|
||||
| SwarmState::Unknown,
|
||||
Some(alert),
|
||||
true,
|
||||
) => {
|
||||
// update alert err
|
||||
let mut alert = alert.clone();
|
||||
@@ -139,23 +126,12 @@ pub async fn alert_swarms(
|
||||
alerts_to_update.push((alert, false));
|
||||
}
|
||||
|
||||
(
|
||||
SwarmState::Unhealthy
|
||||
| SwarmState::Down
|
||||
| SwarmState::Unknown,
|
||||
Some(alert),
|
||||
false,
|
||||
) => {
|
||||
alerts_to_close
|
||||
.push((alert.clone(), swarm.config.send_unhealthy_alerts));
|
||||
}
|
||||
|
||||
// Close an open alert
|
||||
(SwarmState::Healthy, Some(alert), _) => {
|
||||
alerts_to_close
|
||||
(SwarmState::Healthy, Some(alert)) => {
|
||||
alert_ids_to_close
|
||||
.push((alert.clone(), swarm.config.send_unhealthy_alerts));
|
||||
}
|
||||
(SwarmState::Healthy, None, _) => buffer.reset(
|
||||
(SwarmState::Healthy, None) => buffer.reset(
|
||||
swarm_status.id.clone(),
|
||||
AlertDataVariant::SwarmUnhealthy,
|
||||
),
|
||||
@@ -165,7 +141,7 @@ pub async fn alert_swarms(
|
||||
tokio::join!(
|
||||
open_new_alerts(&alerts_to_open),
|
||||
update_alerts(&alerts_to_update),
|
||||
resolve_alerts(&alerts_to_close),
|
||||
resolve_alerts(&alert_ids_to_close),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ pub async fn insert_deployments_status_unknown(
|
||||
state: DeploymentState::Unknown,
|
||||
container: None,
|
||||
service: None,
|
||||
image_digests: None,
|
||||
image_digest: None,
|
||||
},
|
||||
prev,
|
||||
}
|
||||
|
||||
+168
-180
@@ -41,7 +41,7 @@ mod record;
|
||||
mod resources;
|
||||
mod swarm;
|
||||
|
||||
pub use swarm::refresh_swarm_cache;
|
||||
pub use swarm::update_cache_for_swarm;
|
||||
|
||||
const ADDITIONAL_MS: u128 = 500;
|
||||
|
||||
@@ -52,17 +52,17 @@ pub fn spawn_monitoring_loops() {
|
||||
|
||||
fn spawn_server_monitoring_loop() {
|
||||
tokio::spawn(async move {
|
||||
refresh_all_server_cache(komodo_timestamp()).await;
|
||||
refresh_server_cache(komodo_timestamp()).await;
|
||||
let interval = monitoring_interval();
|
||||
loop {
|
||||
let ts = (wait_until_timelength(interval, ADDITIONAL_MS).await
|
||||
- ADDITIONAL_MS) as i64;
|
||||
refresh_all_server_cache(ts).await;
|
||||
refresh_server_cache(ts).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn refresh_all_server_cache(ts: i64) {
|
||||
async fn refresh_server_cache(ts: i64) {
|
||||
let servers =
|
||||
match find_collect(&db_client().servers, None, None).await {
|
||||
Ok(servers) => servers,
|
||||
@@ -74,7 +74,7 @@ async fn refresh_all_server_cache(ts: i64) {
|
||||
}
|
||||
};
|
||||
let futures = servers.into_iter().map(|server| async move {
|
||||
refresh_server_cache(&server, false).await;
|
||||
update_cache_for_server(&server, false).await;
|
||||
});
|
||||
join_all(futures).await;
|
||||
tokio::join!(check_alerts(ts), record_server_stats(ts));
|
||||
@@ -82,7 +82,7 @@ async fn refresh_all_server_cache(ts: i64) {
|
||||
|
||||
/// Makes sure cache for server doesn't update too frequently / simultaneously.
|
||||
/// If forced, will still block against simultaneous update.
|
||||
fn refresh_server_cache_controller()
|
||||
fn update_cache_for_server_controller()
|
||||
-> &'static CloneCache<String, Arc<Mutex<i64>>> {
|
||||
static CACHE: OnceLock<CloneCache<String, Arc<Mutex<i64>>>> =
|
||||
OnceLock::new();
|
||||
@@ -93,176 +93,188 @@ fn refresh_server_cache_controller()
|
||||
/// which exits early if the lock is busy or it was completed too recently.
|
||||
/// If force is true, it will wait on simultaneous calls, and will
|
||||
/// ignore the restriction on being completed too recently.
|
||||
///
|
||||
/// Returns impl Future for explicit Send bound, as this function
|
||||
/// calls `periphery_client` which in some paths calls back this function `refresh_server_cache`.
|
||||
/// While infinite recursion is prevented, the compiler needs some help to know the Send bound.
|
||||
///
|
||||
/// Clippy is not aware of this requirement, so the `manual_async_fn` lint is allowed here.
|
||||
#[allow(clippy::manual_async_fn)]
|
||||
pub fn refresh_server_cache(
|
||||
server: &Server,
|
||||
force: bool,
|
||||
) -> impl Future<Output = ()> + Send + '_ {
|
||||
async move {
|
||||
// Concurrency controller to ensure it isn't done too often
|
||||
// when it happens in other contexts.
|
||||
let controller = refresh_server_cache_controller()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
let mut lock = match controller.try_lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(_) if force => controller.lock().await,
|
||||
Err(_) => return,
|
||||
};
|
||||
pub async fn update_cache_for_server(server: &Server, force: bool) {
|
||||
// Concurrency controller to ensure it isn't done too often
|
||||
// when it happens in other contexts.
|
||||
let controller = update_cache_for_server_controller()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
let mut lock = match controller.try_lock() {
|
||||
Ok(lock) => lock,
|
||||
Err(_) if force => controller.lock().await,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let now = komodo_timestamp();
|
||||
let now = komodo_timestamp();
|
||||
|
||||
// early return if called again sooner than 1s.
|
||||
if !force && *lock > now - 1_000 {
|
||||
return;
|
||||
}
|
||||
// early return if called again sooner than 1s.
|
||||
if !force && *lock > now - 1_000 {
|
||||
return;
|
||||
}
|
||||
|
||||
*lock = now;
|
||||
*lock = now;
|
||||
|
||||
let resources = RefreshCacheResources::load_server(server).await;
|
||||
|
||||
// Handle server disabled
|
||||
if !server.config.enabled {
|
||||
resources.insert_status_unknown().await;
|
||||
insert_server_status(
|
||||
server,
|
||||
ServerState::Disabled,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
periphery_connections().remove(&server.id).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let periphery = match periphery_client(server).await {
|
||||
Ok(periphery) => periphery,
|
||||
Err(e) => {
|
||||
resources.insert_status_unknown().await;
|
||||
insert_server_status(
|
||||
server,
|
||||
ServerState::NotOk,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Serror::from(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let PollStatusResponse {
|
||||
periphery_info,
|
||||
system_info,
|
||||
system_stats,
|
||||
mut docker,
|
||||
} = match periphery
|
||||
.request(api::poll::PollStatus {
|
||||
include_stats: server.config.stats_monitoring,
|
||||
include_docker: true,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(info) => info,
|
||||
Err(e) => {
|
||||
resources.insert_status_unknown().await;
|
||||
insert_server_status(
|
||||
server,
|
||||
ServerState::NotOk,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Serror::from(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(docker) = &mut docker {
|
||||
docker.containers.iter_mut().for_each(|container| {
|
||||
container.server_id = Some(server.id.clone())
|
||||
});
|
||||
}
|
||||
|
||||
let containers = docker
|
||||
.as_ref()
|
||||
.map(|docker| docker.containers.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
let images = docker
|
||||
.as_ref()
|
||||
.map(|docker| docker.images.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
|
||||
tokio::join!(
|
||||
resources::update_server_stack_cache(
|
||||
resources.stacks,
|
||||
containers,
|
||||
images
|
||||
),
|
||||
resources::update_server_deployment_cache(
|
||||
resources.deployments,
|
||||
containers,
|
||||
images,
|
||||
),
|
||||
);
|
||||
let resources = UpdateCacheResources::load_server(server).await;
|
||||
|
||||
// Handle server disabled
|
||||
if !server.config.enabled {
|
||||
resources.insert_status_unknown().await;
|
||||
insert_server_status(
|
||||
server,
|
||||
ServerState::Ok,
|
||||
Some(periphery_info),
|
||||
Some(system_info),
|
||||
system_stats.map(|stats| filter_volumes(server, stats)),
|
||||
docker,
|
||||
ServerState::Disabled,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
periphery_connections().remove(&server.id).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let status_cache = repo_status_cache();
|
||||
for repo in resources.repos {
|
||||
let (latest_hash, latest_message) = periphery
|
||||
.request(GetLatestCommit {
|
||||
name: repo.name.clone(),
|
||||
path: optional_string(&repo.config.path),
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|c| (c.hash, c.message))
|
||||
.unzip();
|
||||
status_cache
|
||||
.insert(
|
||||
repo.id,
|
||||
CachedRepoStatus {
|
||||
latest_hash,
|
||||
latest_message,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
let periphery = match periphery_client(server).await {
|
||||
Ok(periphery) => periphery,
|
||||
Err(e) => {
|
||||
resources.insert_status_unknown().await;
|
||||
insert_server_status(
|
||||
server,
|
||||
ServerState::NotOk,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Serror::from(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let PollStatusResponse {
|
||||
periphery_info,
|
||||
system_info,
|
||||
system_stats,
|
||||
mut docker,
|
||||
} = match periphery
|
||||
.request(api::poll::PollStatus {
|
||||
include_stats: server.config.stats_monitoring,
|
||||
include_docker: true,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(info) => info,
|
||||
Err(e) => {
|
||||
resources.insert_status_unknown().await;
|
||||
insert_server_status(
|
||||
server,
|
||||
ServerState::NotOk,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Serror::from(&e),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(docker) = &mut docker {
|
||||
docker.containers.iter_mut().for_each(|container| {
|
||||
container.server_id = Some(server.id.clone())
|
||||
});
|
||||
}
|
||||
|
||||
let containers = docker
|
||||
.as_ref()
|
||||
.map(|docker| docker.containers.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
let images = docker
|
||||
.as_ref()
|
||||
.map(|docker| docker.images.as_slice())
|
||||
.unwrap_or(&[]);
|
||||
|
||||
tokio::join!(
|
||||
resources::update_server_stack_cache(
|
||||
resources.stacks,
|
||||
containers,
|
||||
images
|
||||
),
|
||||
resources::update_server_deployment_cache(
|
||||
resources.deployments,
|
||||
containers,
|
||||
images,
|
||||
),
|
||||
);
|
||||
|
||||
insert_server_status(
|
||||
server,
|
||||
ServerState::Ok,
|
||||
Some(periphery_info),
|
||||
Some(system_info),
|
||||
system_stats.map(|stats| filter_volumes(server, stats)),
|
||||
docker,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
let status_cache = repo_status_cache();
|
||||
for repo in resources.repos {
|
||||
let (latest_hash, latest_message) = periphery
|
||||
.request(GetLatestCommit {
|
||||
name: repo.name.clone(),
|
||||
path: optional_string(&repo.config.path),
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|c| (c.hash, c.message))
|
||||
.unzip();
|
||||
status_cache
|
||||
.insert(
|
||||
repo.id,
|
||||
CachedRepoStatus {
|
||||
latest_hash,
|
||||
latest_message,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
struct RefreshCacheResources {
|
||||
struct UpdateCacheResources {
|
||||
stacks: Vec<Stack>,
|
||||
deployments: Vec<Deployment>,
|
||||
repos: Vec<Repo>,
|
||||
}
|
||||
|
||||
impl RefreshCacheResources {
|
||||
impl UpdateCacheResources {
|
||||
pub async fn load_swarm(swarm: &Swarm) -> Self {
|
||||
let (stacks, deployments) = tokio::join!(
|
||||
find_collect(
|
||||
&db_client().stacks,
|
||||
doc! { "config.swarm_id": &swarm.id },
|
||||
None,
|
||||
),
|
||||
find_collect(
|
||||
&db_client().deployments,
|
||||
doc! { "config.swarm_id": &swarm.id },
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
let stacks = stacks.inspect_err(|e| error!("Failed to get stacks list from db (update swarm status cache) | swarm: {} | {e:#}", swarm.name)).unwrap_or_default();
|
||||
let deployments = deployments.inspect_err(|e| error!("Failed to get deployments list from db (update swarm status cache) | swarm : {} | {e:#}", swarm.name)).unwrap_or_default();
|
||||
|
||||
Self {
|
||||
stacks,
|
||||
deployments,
|
||||
repos: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn load_server(server: &Server) -> Self {
|
||||
let (stacks, deployments, repos) = tokio::join!(
|
||||
find_collect(
|
||||
@@ -293,30 +305,6 @@ impl RefreshCacheResources {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn load_swarm(swarm: &Swarm) -> Self {
|
||||
let (stacks, deployments) = tokio::join!(
|
||||
find_collect(
|
||||
&db_client().stacks,
|
||||
doc! { "config.swarm_id": &swarm.id },
|
||||
None,
|
||||
),
|
||||
find_collect(
|
||||
&db_client().deployments,
|
||||
doc! { "config.swarm_id": &swarm.id },
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
let stacks = stacks.inspect_err(|e| error!("Failed to get stacks list from db (update swarm status cache) | swarm: {} | {e:#}", swarm.name)).unwrap_or_default();
|
||||
let deployments = deployments.inspect_err(|e| error!("Failed to get deployments list from db (update swarm status cache) | swarm : {} | {e:#}", swarm.name)).unwrap_or_default();
|
||||
|
||||
Self {
|
||||
stacks,
|
||||
deployments,
|
||||
repos: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn insert_status_unknown(self) {
|
||||
insert_stacks_status_unknown(self.stacks).await;
|
||||
insert_deployments_status_unknown(self.deployments).await;
|
||||
|
||||
@@ -6,6 +6,7 @@ use komodo_client::entities::{
|
||||
container::ContainerListItem, image::ImageListItem,
|
||||
service::SwarmServiceListItem, stack::SwarmStackListItem,
|
||||
},
|
||||
optional_string,
|
||||
stack::{Stack, StackService, StackServiceNames, StackState},
|
||||
swarm::SwarmState,
|
||||
};
|
||||
@@ -79,7 +80,7 @@ pub async fn update_swarm_stack_cache(
|
||||
image: image.clone(),
|
||||
container: None,
|
||||
swarm_service,
|
||||
image_digests: Default::default(),
|
||||
image_digest: None,
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -130,7 +131,7 @@ pub async fn update_server_stack_cache(
|
||||
}.is_match(&container.name)
|
||||
}).cloned();
|
||||
|
||||
let (image, image_digests) = container
|
||||
let (image, image_digest) = container
|
||||
.as_ref()
|
||||
.and_then(|container| container.image_id.as_ref())
|
||||
.and_then(|image_id| {
|
||||
@@ -140,7 +141,7 @@ pub async fn update_server_stack_cache(
|
||||
})
|
||||
.map(|image| (
|
||||
image.name.clone(),
|
||||
Some(ImageDigest::vec(&image.digests)),
|
||||
optional_string(&image.digest).map(ImageDigest)
|
||||
))
|
||||
.unwrap_or((
|
||||
if image.contains(':') {
|
||||
@@ -156,7 +157,7 @@ pub async fn update_server_stack_cache(
|
||||
image: image.clone(),
|
||||
container,
|
||||
swarm_service: None,
|
||||
image_digests,
|
||||
image_digest,
|
||||
}
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
@@ -229,7 +230,7 @@ pub async fn update_swarm_deployment_cache(
|
||||
state: current_state,
|
||||
service,
|
||||
container: None,
|
||||
image_digests: None,
|
||||
image_digest: None,
|
||||
},
|
||||
prev: prev_state,
|
||||
}
|
||||
@@ -251,13 +252,13 @@ pub async fn update_server_deployment_cache(
|
||||
.iter()
|
||||
.find(|container| container.name == deployment.name)
|
||||
.cloned();
|
||||
let image_digests = container
|
||||
let image_digest = container
|
||||
.as_ref()
|
||||
.and_then(|container| container.image_id.as_ref())
|
||||
.and_then(|image_id| {
|
||||
images.iter().find_map(|image| {
|
||||
if &image.id == image_id {
|
||||
Some(ImageDigest::vec(&image.digests))
|
||||
optional_string(&image.digest)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -281,7 +282,7 @@ pub async fn update_server_deployment_cache(
|
||||
state: current_state,
|
||||
container,
|
||||
service: None,
|
||||
image_digests,
|
||||
image_digest,
|
||||
},
|
||||
prev: prev_state,
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::{
|
||||
use anyhow::anyhow;
|
||||
use async_timing_util::wait_until_timelength;
|
||||
use database::mungos::find::find_collect;
|
||||
use formatting::format_serror;
|
||||
use futures_util::future::join_all;
|
||||
use komodo_client::entities::{
|
||||
docker::node::NodeState,
|
||||
@@ -22,7 +23,7 @@ use crate::{
|
||||
config::monitoring_interval,
|
||||
helpers::swarm::swarm_request_custom_timeout,
|
||||
monitor::{
|
||||
RefreshCacheResources,
|
||||
UpdateCacheResources,
|
||||
resources::{
|
||||
update_swarm_deployment_cache, update_swarm_stack_cache,
|
||||
},
|
||||
@@ -34,16 +35,16 @@ const ADDITIONAL_MS: u128 = 1000;
|
||||
|
||||
pub fn spawn_swarm_monitoring_loop() {
|
||||
tokio::spawn(async move {
|
||||
refresh_all_swarm_cache().await;
|
||||
refresh_swarm_cache().await;
|
||||
let interval = monitoring_interval();
|
||||
loop {
|
||||
wait_until_timelength(interval, ADDITIONAL_MS).await;
|
||||
refresh_all_swarm_cache().await;
|
||||
refresh_swarm_cache().await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async fn refresh_all_swarm_cache() {
|
||||
async fn refresh_swarm_cache() {
|
||||
let swarms =
|
||||
match find_collect(&db_client().swarms, None, None).await {
|
||||
Ok(swarms) => swarms,
|
||||
@@ -55,7 +56,7 @@ async fn refresh_all_swarm_cache() {
|
||||
}
|
||||
};
|
||||
let futures = swarms.into_iter().map(|swarm| async move {
|
||||
refresh_swarm_cache(&swarm, false).await;
|
||||
update_cache_for_swarm(&swarm, false).await;
|
||||
});
|
||||
join_all(futures).await;
|
||||
// tokio::join!(check_alerts(ts), record_swarm_stats(ts));
|
||||
@@ -63,7 +64,7 @@ async fn refresh_all_swarm_cache() {
|
||||
|
||||
/// Makes sure cache for swarm doesn't update too frequently / simultaneously.
|
||||
/// If forced, will still block against simultaneous update.
|
||||
fn refresh_swarm_cache_controller()
|
||||
fn update_cache_for_swarm_controller()
|
||||
-> &'static CloneCache<String, Arc<Mutex<i64>>> {
|
||||
static CACHE: OnceLock<CloneCache<String, Arc<Mutex<i64>>>> =
|
||||
OnceLock::new();
|
||||
@@ -74,10 +75,10 @@ fn refresh_swarm_cache_controller()
|
||||
/// which exits early if the lock is busy or it was completed too recently.
|
||||
/// If force is true, it will wait on simultaneous calls, and will
|
||||
/// ignore the restriction on being completed too recently.
|
||||
pub async fn refresh_swarm_cache(swarm: &Swarm, force: bool) {
|
||||
pub async fn update_cache_for_swarm(swarm: &Swarm, force: bool) {
|
||||
// Concurrency controller to ensure it isn't done too often
|
||||
// when it happens in other contexts.
|
||||
let controller = refresh_swarm_cache_controller()
|
||||
let controller = update_cache_for_swarm_controller()
|
||||
.get_or_insert_default(&swarm.id)
|
||||
.await;
|
||||
let mut lock = match controller.try_lock() {
|
||||
@@ -95,7 +96,7 @@ pub async fn refresh_swarm_cache(swarm: &Swarm, force: bool) {
|
||||
|
||||
*lock = now;
|
||||
|
||||
let resources = RefreshCacheResources::load_swarm(swarm).await;
|
||||
let resources = UpdateCacheResources::load_swarm(swarm).await;
|
||||
|
||||
if swarm.config.server_ids.is_empty() {
|
||||
resources.insert_status_unknown().await;
|
||||
@@ -107,9 +108,9 @@ pub async fn refresh_swarm_cache(swarm: &Swarm, force: bool) {
|
||||
state: SwarmState::Unknown,
|
||||
inspect: None,
|
||||
lists: None,
|
||||
err: Some(
|
||||
anyhow!("No servers configured as manager nodes").into(),
|
||||
),
|
||||
err: Some(format_serror(
|
||||
&anyhow!("No Servers configured as manager nodes").into(),
|
||||
)),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
@@ -136,7 +137,7 @@ pub async fn refresh_swarm_cache(swarm: &Swarm, force: bool) {
|
||||
state: SwarmState::Unknown,
|
||||
inspect: None,
|
||||
lists: None,
|
||||
err: Some(e.into()),
|
||||
err: Some(format_serror(&e.into())),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ use crate::{
|
||||
query::{get_deployment_state, get_swarm_or_server},
|
||||
swarm::swarm_request,
|
||||
},
|
||||
monitor::{refresh_server_cache, refresh_swarm_cache},
|
||||
monitor::{update_cache_for_server, update_cache_for_swarm},
|
||||
state::{action_states, db_client, deployment_status_cache},
|
||||
};
|
||||
|
||||
@@ -72,13 +72,7 @@ impl super::KomodoResource for Deployment {
|
||||
fn inherit_specific_permissions_from(
|
||||
_self: &Resource<Self::Config, Self::Info>,
|
||||
) -> Option<ResourceTarget> {
|
||||
if !_self.config.swarm_id.is_empty() {
|
||||
Some(ResourceTarget::Swarm(_self.config.swarm_id.clone()))
|
||||
} else if !_self.config.server_id.is_empty() {
|
||||
Some(ResourceTarget::Server(_self.config.server_id.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ResourceTarget::Server(_self.config.server_id.clone()).into()
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
@@ -124,7 +118,7 @@ impl super::KomodoResource for Deployment {
|
||||
}
|
||||
DeploymentImage::Image { image } => (image, None),
|
||||
};
|
||||
let (image, current_digests) = status
|
||||
let (image, digest) = status
|
||||
.as_ref()
|
||||
.map(|s| {
|
||||
(
|
||||
@@ -144,17 +138,14 @@ impl super::KomodoResource for Deployment {
|
||||
.unwrap_or_else(|| String::from("Unknown"))
|
||||
})
|
||||
}),
|
||||
s.curr.image_digests.as_ref(),
|
||||
s.curr.image_digest.as_ref(),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let image = image.unwrap_or(build_image);
|
||||
let update_available = current_digests
|
||||
.map(|current_digests| {
|
||||
deployment
|
||||
.info
|
||||
.latest_image_digest
|
||||
.update_available(current_digests)
|
||||
let update_available = digest
|
||||
.map(|digest| {
|
||||
deployment.info.latest_image_digest.update_available(digest)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
DeploymentListItem {
|
||||
@@ -227,10 +218,10 @@ impl super::KomodoResource for Deployment {
|
||||
};
|
||||
match swarm_or_server {
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
SwarmOrServer::Server(server) => {
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
}
|
||||
SwarmOrServer::None => {}
|
||||
}
|
||||
|
||||
+684
-172
@@ -183,183 +183,695 @@ async fn validate_config(
|
||||
};
|
||||
for stage in stages {
|
||||
for exec in &mut stage.executions {
|
||||
macro_rules! check_execution_perms {
|
||||
(
|
||||
execute: [$(($Variant:ident, $Type:ident, $field:ident)),* $(,)?],
|
||||
batch_admin: [$($BatchVariant:ident),* $(,)?],
|
||||
admin_only: [$(($AdminVariant:ident, $msg:literal)),* $(,)?],
|
||||
) => {
|
||||
match &mut exec.execution {
|
||||
$(
|
||||
Execution::$Variant(params) => {
|
||||
let resource = super::get_check_permissions::<$Type>(
|
||||
¶ms.$field,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.$field = resource.id;
|
||||
}
|
||||
)*
|
||||
$(
|
||||
Execution::$BatchVariant(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
)*
|
||||
$(
|
||||
Execution::$AdminVariant(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!($msg));
|
||||
}
|
||||
}
|
||||
)*
|
||||
// Special: self-referential procedure check
|
||||
Execution::RunProcedure(params) => {
|
||||
let procedure = super::get_check_permissions::<Procedure>(
|
||||
¶ms.procedure,
|
||||
match &mut exec.execution {
|
||||
Execution::None(_) => {}
|
||||
Execution::RunProcedure(params) => {
|
||||
let procedure = super::get_check_permissions::<Procedure>(
|
||||
¶ms.procedure,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
match id {
|
||||
Some(id) if procedure.id == id => {
|
||||
return Err(anyhow!(
|
||||
"Cannot have self-referential procedure"
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
params.procedure = procedure.id;
|
||||
}
|
||||
Execution::BatchRunProcedure(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::RunAction(params) => {
|
||||
let action = super::get_check_permissions::<Action>(
|
||||
¶ms.action,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.action = action.id;
|
||||
}
|
||||
Execution::BatchRunAction(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::RunBuild(params) => {
|
||||
let build = super::get_check_permissions::<Build>(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
}
|
||||
Execution::BatchRunBuild(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::CancelBuild(params) => {
|
||||
let build = super::get_check_permissions::<Build>(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
}
|
||||
Execution::Deploy(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::BatchDeploy(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::PullDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StartDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::RestartDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::PauseDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::UnpauseDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StopDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::DestroyDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::BatchDestroyDeployment(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::CloneRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::BatchCloneRepo(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::PullRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::BatchPullRepo(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::BuildRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::BatchBuildRepo(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::CancelRepoBuild(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::StartContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RestartContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PauseContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::UnpauseContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::StopContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DestroyContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::StartAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RestartAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PauseAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::UnpauseAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::StopAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DeleteNetwork(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneNetworks(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DeleteImage(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneImages(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DeleteVolume(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneVolumes(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneDockerBuilders(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneBuildx(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneSystem(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RunSync(params) => {
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
}
|
||||
Execution::CommitSync(params) => {
|
||||
// This one is actually a write operation.
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
}
|
||||
Execution::DeployStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchDeployStack(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::DeployStackIfChanged(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::PullStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchPullStack(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::StartStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::RestartStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::PauseStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::UnpauseStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::StopStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::DestroyStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::RunStackService(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchDestroyStack(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::TestAlerter(params) => {
|
||||
let alerter = super::get_check_permissions::<Alerter>(
|
||||
¶ms.alerter,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.alerter = alerter.id;
|
||||
}
|
||||
Execution::SendAlert(params) => {
|
||||
params.alerters = params
|
||||
.alerters
|
||||
.iter()
|
||||
.map(async |alerter| {
|
||||
let id = super::get_check_permissions::<Alerter>(
|
||||
alerter,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
match id {
|
||||
Some(id) if procedure.id == id => {
|
||||
return Err(anyhow!(
|
||||
"Cannot have self-referential procedure"
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
params.procedure = procedure.id;
|
||||
}
|
||||
// Special: CommitSync uses Write permission
|
||||
Execution::CommitSync(params) => {
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
}
|
||||
// Special: SendAlert checks a Vec of alerters
|
||||
Execution::SendAlert(params) => {
|
||||
params.alerters = params
|
||||
.alerters
|
||||
.iter()
|
||||
.map(async |alerter| {
|
||||
let id = super::get_check_permissions::<Alerter>(
|
||||
alerter,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?
|
||||
.id;
|
||||
anyhow::Ok(id)
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
}
|
||||
Execution::None(_) | Execution::Sleep(_) => {}
|
||||
.await?
|
||||
.id;
|
||||
anyhow::Ok(id)
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
}
|
||||
Execution::RemoveSwarmNodes(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmStacks(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmServices(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::CreateSwarmConfig(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RotateSwarmConfig(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::CreateSwarmSecret(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RotateSwarmSecret(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::ClearRepoCache(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot clear repo cache"
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
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::RotateAllServerKeys(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger rotate all server keys"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::RotateCoreKeys(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger rotate core keys"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::Sleep(_) => {}
|
||||
}
|
||||
check_execution_perms!(
|
||||
execute: [
|
||||
// Action
|
||||
(RunAction, Action, action),
|
||||
// Build
|
||||
(RunBuild, Build, build),
|
||||
(CancelBuild, Build, build),
|
||||
// Deployment
|
||||
(Deploy, Deployment, deployment),
|
||||
(PullDeployment, Deployment, deployment),
|
||||
(StartDeployment, Deployment, deployment),
|
||||
(RestartDeployment, Deployment, deployment),
|
||||
(PauseDeployment, Deployment, deployment),
|
||||
(UnpauseDeployment, Deployment, deployment),
|
||||
(StopDeployment, Deployment, deployment),
|
||||
(DestroyDeployment, Deployment, deployment),
|
||||
// Repo
|
||||
(CloneRepo, Repo, repo),
|
||||
(PullRepo, Repo, repo),
|
||||
(BuildRepo, Repo, repo),
|
||||
(CancelRepoBuild, Repo, repo),
|
||||
// Server
|
||||
(StartContainer, Server, server),
|
||||
(RestartContainer, Server, server),
|
||||
(PauseContainer, Server, server),
|
||||
(UnpauseContainer, Server, server),
|
||||
(StopContainer, Server, server),
|
||||
(DestroyContainer, Server, server),
|
||||
(StartAllContainers, Server, server),
|
||||
(RestartAllContainers, Server, server),
|
||||
(PauseAllContainers, Server, server),
|
||||
(UnpauseAllContainers, Server, server),
|
||||
(StopAllContainers, Server, server),
|
||||
(PruneContainers, Server, server),
|
||||
(DeleteNetwork, Server, server),
|
||||
(PruneNetworks, Server, server),
|
||||
(DeleteImage, Server, server),
|
||||
(PruneImages, Server, server),
|
||||
(DeleteVolume, Server, server),
|
||||
(PruneVolumes, Server, server),
|
||||
(PruneDockerBuilders, Server, server),
|
||||
(PruneBuildx, Server, server),
|
||||
(PruneSystem, Server, server),
|
||||
// Resource Sync
|
||||
(RunSync, ResourceSync, sync),
|
||||
// Stack
|
||||
(DeployStack, Stack, stack),
|
||||
(DeployStackIfChanged, Stack, stack),
|
||||
(PullStack, Stack, stack),
|
||||
(StartStack, Stack, stack),
|
||||
(RestartStack, Stack, stack),
|
||||
(PauseStack, Stack, stack),
|
||||
(UnpauseStack, Stack, stack),
|
||||
(StopStack, Stack, stack),
|
||||
(DestroyStack, Stack, stack),
|
||||
(RunStackService, Stack, stack),
|
||||
// Alerter
|
||||
(TestAlerter, Alerter, alerter),
|
||||
// Swarm
|
||||
(RemoveSwarmNodes, Swarm, swarm),
|
||||
(UpdateSwarmNode, Swarm, swarm),
|
||||
(RemoveSwarmStacks, Swarm, swarm),
|
||||
(RemoveSwarmServices, Swarm, swarm),
|
||||
(CreateSwarmConfig, Swarm, swarm),
|
||||
(RotateSwarmConfig, Swarm, swarm),
|
||||
(RemoveSwarmConfigs, Swarm, swarm),
|
||||
(CreateSwarmSecret, Swarm, swarm),
|
||||
(RotateSwarmSecret, Swarm, swarm),
|
||||
(RemoveSwarmSecrets, Swarm, swarm),
|
||||
],
|
||||
batch_admin: [
|
||||
BatchRunProcedure,
|
||||
BatchRunAction,
|
||||
BatchRunBuild,
|
||||
BatchDeploy,
|
||||
BatchDestroyDeployment,
|
||||
BatchCloneRepo,
|
||||
BatchPullRepo,
|
||||
BatchBuildRepo,
|
||||
BatchDeployStack,
|
||||
BatchDeployStackIfChanged,
|
||||
BatchPullStack,
|
||||
BatchDestroyStack,
|
||||
],
|
||||
admin_only: [
|
||||
(ClearRepoCache, "Non admin user cannot clear repo cache"),
|
||||
(BackupCoreDatabase, "Non admin user cannot trigger core database backup"),
|
||||
(GlobalAutoUpdate, "Non admin user cannot trigger global auto update"),
|
||||
(RotateAllServerKeys, "Non admin user cannot trigger rotate all server keys"),
|
||||
(RotateCoreKeys, "Non admin user cannot trigger rotate core keys"),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use periphery_client::api;
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::periphery_client,
|
||||
monitor::refresh_server_cache,
|
||||
monitor::update_cache_for_server,
|
||||
state::{
|
||||
action_states, db_client, periphery_connections,
|
||||
server_status_cache,
|
||||
@@ -93,7 +93,6 @@ impl super::KomodoResource for Server {
|
||||
resource_type: ResourceTargetVariant::Server,
|
||||
info: ServerListItemInfo {
|
||||
state: status.as_ref().map(|s| s.state).unwrap_or_default(),
|
||||
err: status.as_ref().and_then(|s| s.err.clone()),
|
||||
region: server.config.region,
|
||||
address: optional_string(server.config.address),
|
||||
external_address: optional_string(
|
||||
@@ -152,7 +151,7 @@ impl super::KomodoResource for Server {
|
||||
created: &Resource<Self::Config, Self::Info>,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
refresh_server_cache(created, true).await;
|
||||
update_cache_for_server(created, true).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -174,7 +173,7 @@ impl super::KomodoResource for Server {
|
||||
updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
refresh_server_cache(updated, true).await;
|
||||
update_cache_for_server(updated, true).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use crate::{
|
||||
repo_link,
|
||||
swarm::swarm_request,
|
||||
},
|
||||
monitor::{refresh_server_cache, refresh_swarm_cache},
|
||||
monitor::{update_cache_for_server, update_cache_for_swarm},
|
||||
state::{
|
||||
action_states, all_resources_cache, db_client,
|
||||
server_status_cache, stack_status_cache,
|
||||
@@ -77,13 +77,7 @@ impl super::KomodoResource for Stack {
|
||||
fn inherit_specific_permissions_from(
|
||||
_self: &Resource<Self::Config, Self::Info>,
|
||||
) -> Option<ResourceTarget> {
|
||||
if !_self.config.swarm_id.is_empty() {
|
||||
Some(ResourceTarget::Swarm(_self.config.swarm_id.clone()))
|
||||
} else if !_self.config.server_id.is_empty() {
|
||||
Some(ResourceTarget::Server(_self.config.server_id.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ResourceTarget::Server(_self.config.server_id.clone()).into()
|
||||
}
|
||||
|
||||
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
|
||||
@@ -118,9 +112,9 @@ impl super::KomodoResource for Stack {
|
||||
.iter()
|
||||
.map(|current_service| {
|
||||
let update_available = current_service
|
||||
.image_digests
|
||||
.image_digest
|
||||
.as_ref()
|
||||
.map(|current_digests| {
|
||||
.map(|current_digest| {
|
||||
stack
|
||||
.info
|
||||
.latest_services
|
||||
@@ -132,7 +126,7 @@ impl super::KomodoResource for Stack {
|
||||
latest_service
|
||||
.image_digest
|
||||
.as_ref()?
|
||||
.update_available(current_digests)
|
||||
.update_available(¤t_digest.0)
|
||||
.into()
|
||||
} else {
|
||||
None
|
||||
@@ -300,10 +294,10 @@ impl super::KomodoResource for Stack {
|
||||
};
|
||||
match swarm_or_server {
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
update_cache_for_swarm(&swarm, true).await;
|
||||
}
|
||||
SwarmOrServer::Server(server) => {
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
}
|
||||
SwarmOrServer::None => {}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use komodo_client::entities::{
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
monitor::refresh_swarm_cache,
|
||||
monitor::update_cache_for_swarm,
|
||||
state::{db_client, swarm_status_cache},
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ impl super::KomodoResource for Swarm {
|
||||
created: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
refresh_swarm_cache(created, true).await;
|
||||
update_cache_for_swarm(created, true).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ impl super::KomodoResource for Swarm {
|
||||
updated: &Self,
|
||||
_update: &mut Update,
|
||||
) -> anyhow::Result<()> {
|
||||
refresh_swarm_cache(updated, true).await;
|
||||
update_cache_for_swarm(updated, true).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,12 @@ use komodo_client::{
|
||||
permission::PermissionLevel,
|
||||
repo::Repo,
|
||||
resource::Resource,
|
||||
sync::*,
|
||||
sync::{
|
||||
PartialResourceSyncConfig, ResourceSync, ResourceSyncConfig,
|
||||
ResourceSyncConfigDiff, ResourceSyncInfo, ResourceSyncListItem,
|
||||
ResourceSyncListItemInfo, ResourceSyncQuerySpecifics,
|
||||
ResourceSyncState,
|
||||
},
|
||||
update::Update,
|
||||
user::{User, sync_user},
|
||||
},
|
||||
@@ -255,15 +260,12 @@ async fn get_resource_sync_state(
|
||||
{
|
||||
return state;
|
||||
}
|
||||
if data.pending_error.is_some()
|
||||
|| data.pending_deploy_error.is_some()
|
||||
|| !data.remote_errors.is_empty()
|
||||
{
|
||||
if data.pending_error.is_some() || !data.remote_errors.is_empty() {
|
||||
ResourceSyncState::Failed
|
||||
} else if !data.resource_updates.is_empty()
|
||||
|| !data.variable_updates.is_empty()
|
||||
|| !data.user_group_updates.is_empty()
|
||||
|| !data.pending_deploys.is_empty()
|
||||
|| data.pending_deploy.to_deploy > 0
|
||||
{
|
||||
ResourceSyncState::Pending
|
||||
} else {
|
||||
|
||||
@@ -109,7 +109,7 @@ pub fn spawn_schedule_executor() {
|
||||
.resolve(&ExecuteArgs {
|
||||
user: action_user().to_owned(),
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
@@ -180,7 +180,7 @@ pub fn spawn_schedule_executor() {
|
||||
.resolve(&ExecuteArgs {
|
||||
user: procedure_user().to_owned(),
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ use periphery_client::api::compose::*;
|
||||
|
||||
use crate::{
|
||||
helpers::{periphery_client, update::update_update},
|
||||
monitor::refresh_server_cache,
|
||||
monitor::update_cache_for_server,
|
||||
periphery::PeripheryClient,
|
||||
state::action_states,
|
||||
};
|
||||
@@ -82,7 +82,7 @@ pub async fn execute_compose_with_stack_and_server<
|
||||
// The returned guard will set the action state back to default when dropped.
|
||||
let _action_guard = action_state.update(set_in_progress)?;
|
||||
|
||||
// Send update here for UI to recheck action state
|
||||
// Send update here for frontend to recheck action state
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let periphery = periphery_client(&server).await?;
|
||||
@@ -102,7 +102,7 @@ pub async fn execute_compose_with_stack_and_server<
|
||||
.push(T::execute(periphery, stack, services, extras).await?);
|
||||
|
||||
// Ensure cached stack state up to date by updating server cache
|
||||
refresh_server_cache(&server, true).await;
|
||||
update_cache_for_server(&server, true).await;
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
+20
-20
@@ -1,6 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Context;
|
||||
use colored::Colorize;
|
||||
use database::mungos::{
|
||||
find::find_collect,
|
||||
mongodb::bson::{
|
||||
@@ -32,7 +33,6 @@ use komodo_client::{
|
||||
user::{action_user, system_user},
|
||||
},
|
||||
};
|
||||
use mogh_auth_server::api::login::local::sign_up_local_user;
|
||||
use mogh_resolver::Resolve;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -41,7 +41,6 @@ use crate::{
|
||||
execute::{ExecuteArgs, ExecuteRequest},
|
||||
write::WriteArgs,
|
||||
},
|
||||
auth::KomodoAuthImpl,
|
||||
config::core_config,
|
||||
helpers::update::init_execution_update,
|
||||
network, resource,
|
||||
@@ -92,7 +91,7 @@ pub async fn run_startup_actions() {
|
||||
.resolve(&ExecuteArgs {
|
||||
user: action_user().to_owned(),
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
{
|
||||
@@ -302,7 +301,7 @@ async fn ensure_init_user_and_resources() {
|
||||
|
||||
// Assumes if there are any existing users, procedures, or tags,
|
||||
// the default procedures do not need to be set up.
|
||||
let Ok((None, procedures, tags)) = tokio::try_join!(
|
||||
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()),
|
||||
@@ -315,16 +314,20 @@ async fn ensure_init_user_and_resources() {
|
||||
// Init admin user if set in config.
|
||||
if let Some(username) = &config.init_admin_username {
|
||||
info!("Creating init admin user...");
|
||||
if let Err(e) = sign_up_local_user(
|
||||
&KomodoAuthImpl,
|
||||
username.to_string(),
|
||||
&config.init_admin_password,
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("Failed to create init admin user | {:#}", e.error);
|
||||
return;
|
||||
}
|
||||
// if let Err(e) = (SignUpLocalUser {
|
||||
// username: username.clone(),
|
||||
// password: config.init_admin_password.clone(),
|
||||
// })
|
||||
// .resolve(&AuthArgs {
|
||||
// headers: Default::default(),
|
||||
// ip: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
// session: None,
|
||||
// })
|
||||
// .await
|
||||
// {
|
||||
// error!("Failed to create init admin user | {:#}", e.error);
|
||||
// return;
|
||||
// }
|
||||
match db
|
||||
.users
|
||||
.find_one(doc! { "username": username })
|
||||
@@ -344,15 +347,12 @@ async fn ensure_init_user_and_resources() {
|
||||
}
|
||||
}
|
||||
|
||||
if config.disable_init_resources
|
||||
|| procedures.is_some()
|
||||
|| tags.is_some()
|
||||
{
|
||||
info!("Skipping initial system resource creation");
|
||||
if config.disable_init_resources {
|
||||
info!("System resources init {}", "DISABLED".red());
|
||||
return;
|
||||
}
|
||||
|
||||
info!("Creating initial system resources...");
|
||||
info!("Creating init system resources...");
|
||||
|
||||
let write_args = WriteArgs {
|
||||
user: system_user().to_owned(),
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::sync::{Arc, OnceLock};
|
||||
use anyhow::{Context, anyhow};
|
||||
use arc_swap::ArcSwap;
|
||||
use komodo_client::entities::{
|
||||
ImageDigest,
|
||||
action::ActionState,
|
||||
build::BuildState,
|
||||
deployment::DeploymentState,
|
||||
@@ -87,7 +86,7 @@ pub struct CachedSwarmStatus {
|
||||
pub inspect: Option<SwarmInspectInfo>,
|
||||
pub lists: Option<SwarmLists>,
|
||||
/// Store the error in communicating with Swarm
|
||||
pub err: Option<mogh_error::Serror>,
|
||||
pub err: Option<String>,
|
||||
}
|
||||
|
||||
pub type SwarmStatusCache =
|
||||
@@ -147,7 +146,7 @@ pub struct CachedDeploymentStatus {
|
||||
pub state: DeploymentState,
|
||||
pub container: Option<ContainerListItem>,
|
||||
pub service: Option<SwarmServiceListItem>,
|
||||
pub image_digests: Option<Vec<ImageDigest>>,
|
||||
pub image_digest: Option<String>,
|
||||
}
|
||||
|
||||
/// Cache of ids to status
|
||||
@@ -220,5 +219,5 @@ pub fn all_resources_cache() -> &'static ArcSwap<AllResourcesById> {
|
||||
pub fn image_digest_cache() -> &'static ImageDigestCache {
|
||||
static IMAGE_DIGEST_CACHE: OnceLock<Arc<ImageDigestCache>> =
|
||||
OnceLock::new();
|
||||
IMAGE_DIGEST_CACHE.get_or_init(ImageDigestCache::new)
|
||||
IMAGE_DIGEST_CACHE.get_or_init(|| ImageDigestCache::new())
|
||||
}
|
||||
|
||||
+54
-27
@@ -18,7 +18,7 @@ use komodo_client::{
|
||||
PartialStackConfig, Stack, StackConfig,
|
||||
StackRemoteFileContents, StackState,
|
||||
},
|
||||
sync::SyncDeployTarget,
|
||||
sync::SyncDeployUpdate,
|
||||
toml::ResourceToml,
|
||||
update::Log,
|
||||
user::sync_user,
|
||||
@@ -41,7 +41,8 @@ use super::ResourceSyncTrait;
|
||||
/// All entries in here are due to be deployed,
|
||||
/// after the given dependencies,
|
||||
/// with the given reason.
|
||||
pub type ToDeployCache = Vec<SyncDeployTarget>;
|
||||
pub type ToDeployCache =
|
||||
Vec<(ResourceTarget, String, Vec<ResourceTarget>)>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct SyncDeployParams<'a> {
|
||||
@@ -71,16 +72,14 @@ pub async fn deploy_from_cache(
|
||||
// Collect all waiting deployments without waiting dependencies.
|
||||
let good_to_deploy = to_deploy
|
||||
.iter()
|
||||
.filter(|SyncDeployTarget { after, .. }| {
|
||||
to_deploy.iter().all(|SyncDeployTarget { target, .. }| {
|
||||
!after.contains(target)
|
||||
})
|
||||
.filter(|(_, _, after)| {
|
||||
to_deploy
|
||||
.iter()
|
||||
.all(|(target, _, _)| !after.contains(target))
|
||||
})
|
||||
// The target / reason need the be cloned out to to_deploy is not borrowed from.
|
||||
// to_deploy will be mutably accessed later.
|
||||
.map(|SyncDeployTarget { target, reason, .. }| {
|
||||
(target.clone(), reason.clone())
|
||||
})
|
||||
.map(|(target, reason, _)| (target.clone(), reason.clone()))
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
// Deploy the ones ready for deployment
|
||||
@@ -104,7 +103,7 @@ pub async fn deploy_from_cache(
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.to_owned(),
|
||||
update,
|
||||
task_id: id,
|
||||
id,
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -123,7 +122,7 @@ pub async fn deploy_from_cache(
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.to_owned(),
|
||||
update,
|
||||
task_id: id,
|
||||
id,
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -173,9 +172,8 @@ pub async fn deploy_from_cache(
|
||||
}
|
||||
|
||||
// Remove the deployed ones from 'to_deploy'
|
||||
to_deploy.retain(|SyncDeployTarget { target, .. }| {
|
||||
!good_to_deploy.contains_key(target)
|
||||
});
|
||||
to_deploy
|
||||
.retain(|(target, _, _)| !good_to_deploy.contains_key(target));
|
||||
|
||||
// If there must be another round, these are dependent on the first round.
|
||||
// Sleep for 1s to allow for first round to startup
|
||||
@@ -198,10 +196,46 @@ pub async fn deploy_from_cache(
|
||||
|
||||
pub async fn get_updates_for_view(
|
||||
params: SyncDeployParams<'_>,
|
||||
) -> (Vec<SyncDeployTarget>, Option<String>) {
|
||||
match build_deploy_cache(params).await {
|
||||
Ok(targets) => (targets, None),
|
||||
Err(e) => (Vec::new(), Some(format_serror(&e.into()))),
|
||||
) -> SyncDeployUpdate {
|
||||
let inner = async {
|
||||
let mut update = SyncDeployUpdate {
|
||||
to_deploy: 0,
|
||||
log: String::from("Deploy Updates\n-------------------\n"),
|
||||
};
|
||||
let mut lines = Vec::<String>::new();
|
||||
for (target, reason, after) in build_deploy_cache(params).await? {
|
||||
update.to_deploy += 1;
|
||||
let mut line = format!(
|
||||
"{}: {}. reason: {reason}",
|
||||
colored("Deploy", Color::Green),
|
||||
bold(format!("{target:?}")),
|
||||
);
|
||||
if !after.is_empty() {
|
||||
line.push_str(&format!(
|
||||
"\n{}: {}",
|
||||
colored("After", Color::Blue),
|
||||
after
|
||||
.iter()
|
||||
.map(|target| format!("{target:?}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
))
|
||||
}
|
||||
lines.push(line);
|
||||
}
|
||||
|
||||
update.log.push_str(&lines.join("\n-------------------\n"));
|
||||
|
||||
anyhow::Ok(update)
|
||||
};
|
||||
match inner.await {
|
||||
Ok(res) => res,
|
||||
Err(e) => SyncDeployUpdate {
|
||||
to_deploy: 0,
|
||||
log: format_serror(
|
||||
&e.context("failed to get deploy updates for view").into(),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,11 +294,7 @@ pub async fn build_deploy_cache(
|
||||
.map(|(target, (reason, mut after))| {
|
||||
// Only keep targets which are deploying.
|
||||
after.retain(|target| clone.contains_key(target));
|
||||
SyncDeployTarget {
|
||||
target,
|
||||
reason,
|
||||
after,
|
||||
}
|
||||
(target, reason, after)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
@@ -590,12 +620,9 @@ fn build_cache_for_stack<'a>(
|
||||
|| diff.extra_args.is_some()
|
||||
|| diff.environment.is_some()
|
||||
|| diff.env_file_path.is_some()
|
||||
|| diff.additional_env_files.is_some()
|
||||
|| diff.linked_repo.is_some()
|
||||
|| diff.repo.is_some()
|
||||
|| diff.branch.is_some()
|
||||
|| diff.commit.is_some()
|
||||
|| diff.compose_cmd_wrapper.is_some();
|
||||
|| diff.commit.is_some();
|
||||
if changed {
|
||||
cache.insert(
|
||||
target,
|
||||
|
||||
+30
-21
@@ -243,27 +243,36 @@ pub fn extend_resources(
|
||||
more: ResourcesToml,
|
||||
match_tags: &[String],
|
||||
) {
|
||||
macro_rules! extend_filtered {
|
||||
($($field:ident),* $(,)?) => {
|
||||
$(
|
||||
resources.$field.extend(filter_by_tag(more.$field, match_tags));
|
||||
)*
|
||||
};
|
||||
}
|
||||
// New resource types need to be added here manually.
|
||||
extend_filtered!(
|
||||
servers,
|
||||
swarms,
|
||||
stacks,
|
||||
deployments,
|
||||
builds,
|
||||
repos,
|
||||
procedures,
|
||||
actions,
|
||||
alerters,
|
||||
builders,
|
||||
resource_syncs,
|
||||
);
|
||||
resources
|
||||
.servers
|
||||
.extend(filter_by_tag(more.servers, match_tags));
|
||||
resources
|
||||
.stacks
|
||||
.extend(filter_by_tag(more.stacks, match_tags));
|
||||
resources
|
||||
.deployments
|
||||
.extend(filter_by_tag(more.deployments, match_tags));
|
||||
resources
|
||||
.builds
|
||||
.extend(filter_by_tag(more.builds, match_tags));
|
||||
resources
|
||||
.repos
|
||||
.extend(filter_by_tag(more.repos, match_tags));
|
||||
resources
|
||||
.procedures
|
||||
.extend(filter_by_tag(more.procedures, match_tags));
|
||||
resources
|
||||
.actions
|
||||
.extend(filter_by_tag(more.actions, match_tags));
|
||||
resources
|
||||
.alerters
|
||||
.extend(filter_by_tag(more.alerters, match_tags));
|
||||
resources
|
||||
.builders
|
||||
.extend(filter_by_tag(more.builders, match_tags));
|
||||
resources
|
||||
.resource_syncs
|
||||
.extend(filter_by_tag(more.resource_syncs, match_tags));
|
||||
resources.user_groups.extend(more.user_groups);
|
||||
resources.variables.extend(more.variables);
|
||||
}
|
||||
|
||||
+500
-57
@@ -1,27 +1,29 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use formatting::{Color, bold, colored, muted};
|
||||
use komodo_client::entities::{
|
||||
ResourceTargetVariant,
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
update::Log,
|
||||
user::sync_user,
|
||||
use komodo_client::{
|
||||
api::execute::Execution,
|
||||
entities::{
|
||||
ResourceTargetVariant,
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
update::Log,
|
||||
user::sync_user,
|
||||
},
|
||||
};
|
||||
use partial_derive2::{MaybeNone, PartialDiff};
|
||||
|
||||
use crate::{
|
||||
helpers::procedure::replace_procedure_stage_ids_with_names,
|
||||
resource::{KomodoResource, ResourceMetaUpdate},
|
||||
state::all_resources_cache,
|
||||
sync::{ToUpdateItem, execute::run_update_meta},
|
||||
@@ -33,6 +35,17 @@ use super::{
|
||||
include_resource_by_tags,
|
||||
};
|
||||
|
||||
impl ResourceSyncTrait for Swarm {
|
||||
fn get_diff(
|
||||
original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
Ok(original.partial_diff(update))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecuteResourceSync for Swarm {}
|
||||
|
||||
impl ResourceSyncTrait for Server {
|
||||
fn get_diff(
|
||||
original: Self::Config,
|
||||
@@ -44,41 +57,20 @@ impl ResourceSyncTrait for Server {
|
||||
|
||||
impl ExecuteResourceSync for Server {}
|
||||
|
||||
impl ResourceSyncTrait for Swarm {
|
||||
fn get_diff(
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
let all = all_resources_cache().load();
|
||||
|
||||
original.server_ids.iter_mut().for_each(|server_id| {
|
||||
*server_id = all
|
||||
.servers
|
||||
.get(server_id)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
});
|
||||
|
||||
Ok(original.partial_diff(update))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecuteResourceSync for Swarm {}
|
||||
|
||||
impl ResourceSyncTrait for Deployment {
|
||||
fn get_diff(
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
let all = all_resources_cache().load();
|
||||
let resources = all_resources_cache().load();
|
||||
|
||||
original.swarm_id = all
|
||||
original.swarm_id = resources
|
||||
.swarms
|
||||
.get(&original.swarm_id)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
original.server_id = all
|
||||
original.server_id = resources
|
||||
.servers
|
||||
.get(&original.server_id)
|
||||
.map(|s| s.name.clone())
|
||||
@@ -88,7 +80,7 @@ impl ResourceSyncTrait for Deployment {
|
||||
&original.image
|
||||
{
|
||||
original.image = DeploymentImage::Build {
|
||||
build_id: all
|
||||
build_id: resources
|
||||
.builds
|
||||
.get(build_id)
|
||||
.map(|b| b.name.clone())
|
||||
@@ -108,21 +100,21 @@ impl ResourceSyncTrait for Stack {
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
let all = all_resources_cache().load();
|
||||
let resources = all_resources_cache().load();
|
||||
|
||||
original.swarm_id = all
|
||||
original.swarm_id = resources
|
||||
.swarms
|
||||
.get(&original.swarm_id)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
original.server_id = all
|
||||
original.server_id = resources
|
||||
.servers
|
||||
.get(&original.server_id)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
original.linked_repo = all
|
||||
original.linked_repo = resources
|
||||
.repos
|
||||
.get(&original.linked_repo)
|
||||
.map(|r| r.name.clone())
|
||||
@@ -139,13 +131,13 @@ impl ResourceSyncTrait for Build {
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
let all = all_resources_cache().load();
|
||||
original.builder_id = all
|
||||
let resources = all_resources_cache().load();
|
||||
original.builder_id = resources
|
||||
.builders
|
||||
.get(&original.builder_id)
|
||||
.map(|b| b.name.clone())
|
||||
.unwrap_or_default();
|
||||
original.linked_repo = all
|
||||
original.linked_repo = resources
|
||||
.repos
|
||||
.get(&original.linked_repo)
|
||||
.map(|r| r.name.clone())
|
||||
@@ -173,16 +165,16 @@ impl ResourceSyncTrait for Repo {
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
let all = all_resources_cache().load();
|
||||
let resources = all_resources_cache().load();
|
||||
// Need to replace server id with name
|
||||
original.server_id = all
|
||||
original.server_id = resources
|
||||
.servers
|
||||
.get(&original.server_id)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
// Need to replace builder id with name
|
||||
original.builder_id = all
|
||||
original.builder_id = resources
|
||||
.builders
|
||||
.get(&original.builder_id)
|
||||
.map(|s| s.name.clone())
|
||||
@@ -212,8 +204,8 @@ impl ResourceSyncTrait for Builder {
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
// need to replace server builder id with name
|
||||
if let BuilderConfig::Server(config) = &mut original {
|
||||
let all = all_resources_cache().load();
|
||||
config.server_id = all
|
||||
let resources = all_resources_cache().load();
|
||||
config.server_id = resources
|
||||
.servers
|
||||
.get(&config.server_id)
|
||||
.map(|s| s.name.clone())
|
||||
@@ -321,8 +313,8 @@ impl ResourceSyncTrait for ResourceSync {
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
let all = all_resources_cache().load();
|
||||
original.linked_repo = all
|
||||
let resources = all_resources_cache().load();
|
||||
original.linked_repo = resources
|
||||
.repos
|
||||
.get(&original.linked_repo)
|
||||
.map(|r| r.name.clone())
|
||||
@@ -339,7 +331,458 @@ impl ResourceSyncTrait for Procedure {
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
replace_procedure_stage_ids_with_names(&mut original.stages);
|
||||
let resources = all_resources_cache().load();
|
||||
for stage in &mut original.stages {
|
||||
for execution in &mut stage.executions {
|
||||
match &mut execution.execution {
|
||||
Execution::None(_) => {}
|
||||
Execution::RunProcedure(config) => {
|
||||
config.procedure = resources
|
||||
.procedures
|
||||
.get(&config.procedure)
|
||||
.map(|p| p.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchRunProcedure(_config) => {}
|
||||
Execution::RunAction(config) => {
|
||||
config.action = resources
|
||||
.actions
|
||||
.get(&config.action)
|
||||
.map(|p| p.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchRunAction(_config) => {}
|
||||
Execution::RunBuild(config) => {
|
||||
config.build = resources
|
||||
.builds
|
||||
.get(&config.build)
|
||||
.map(|b| b.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchRunBuild(_config) => {}
|
||||
Execution::CancelBuild(config) => {
|
||||
config.build = resources
|
||||
.builds
|
||||
.get(&config.build)
|
||||
.map(|b| b.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::Deploy(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDeploy(_config) => {}
|
||||
Execution::PullDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StartDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DestroyDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDestroyDeployment(_config) => {}
|
||||
Execution::CloneRepo(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchCloneRepo(_config) => {}
|
||||
Execution::PullRepo(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchPullRepo(_config) => {}
|
||||
Execution::BuildRepo(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchBuildRepo(_config) => {}
|
||||
Execution::CancelRepoBuild(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StartContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DestroyContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StartAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeleteNetwork(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneNetworks(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeleteImage(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneImages(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeleteVolume(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneVolumes(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneDockerBuilders(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneBuildx(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneSystem(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RunSync(config) => {
|
||||
config.sync = resources
|
||||
.syncs
|
||||
.get(&config.sync)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::CommitSync(config) => {
|
||||
config.sync = resources
|
||||
.syncs
|
||||
.get(&config.sync)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeployStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDeployStack(_config) => {}
|
||||
Execution::DeployStackIfChanged(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(_config) => {}
|
||||
Execution::PullStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchPullStack(_config) => {}
|
||||
Execution::StartStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DestroyStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RunStackService(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDestroyStack(_config) => {}
|
||||
Execution::TestAlerter(config) => {
|
||||
config.alerter = resources
|
||||
.alerters
|
||||
.get(&config.alerter)
|
||||
.map(|a| a.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::SendAlert(config) => {
|
||||
config.alerters = config
|
||||
.alerters
|
||||
.iter()
|
||||
.map(|alerter| {
|
||||
resources
|
||||
.alerters
|
||||
.get(alerter)
|
||||
.map(|a| a.name.clone())
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
Execution::RemoveSwarmNodes(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmStacks(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmServices(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::CreateSwarmConfig(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RotateSwarmConfig(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::CreateSwarmSecret(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RotateSwarmSecret(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::ClearRepoCache(_) => {}
|
||||
Execution::BackupCoreDatabase(_) => {}
|
||||
Execution::GlobalAutoUpdate(_) => {}
|
||||
Execution::RotateAllServerKeys(_) => {}
|
||||
Execution::RotateCoreKeys(_) => {}
|
||||
Execution::Sleep(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(original.partial_diff(update))
|
||||
}
|
||||
}
|
||||
|
||||
+519
-30
@@ -2,28 +2,28 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexMap;
|
||||
use komodo_client::entities::{
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig, PartialBuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
toml::ResourceToml,
|
||||
use komodo_client::{
|
||||
api::execute::Execution,
|
||||
entities::{
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig, PartialBuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
toml::ResourceToml,
|
||||
},
|
||||
};
|
||||
use partial_derive2::{MaybeNone, PartialDiff};
|
||||
|
||||
use crate::{
|
||||
helpers::procedure::replace_procedure_stage_ids_with_names,
|
||||
resource::KomodoResource, state::all_resources_cache,
|
||||
};
|
||||
use crate::{resource::KomodoResource, state::all_resources_cache};
|
||||
|
||||
pub const TOML_PRETTY_OPTIONS: toml_pretty::Options =
|
||||
toml_pretty::Options {
|
||||
@@ -178,14 +178,6 @@ impl ToToml for ResourceSync {
|
||||
impl ToToml for Swarm {
|
||||
fn replace_ids(resource: &mut Resource<Self::Config, Self::Info>) {
|
||||
let all = all_resources_cache().load();
|
||||
|
||||
resource.config.server_ids.iter_mut().for_each(|server_id| {
|
||||
*server_id = all
|
||||
.servers
|
||||
.get(server_id)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
});
|
||||
let mut res =
|
||||
Vec::with_capacity(resource.config.server_ids.capacity());
|
||||
for server_id in &resource.config.server_ids {
|
||||
@@ -462,9 +454,506 @@ impl ToToml for Builder {
|
||||
|
||||
impl ToToml for Procedure {
|
||||
fn replace_ids(resource: &mut Resource<Self::Config, Self::Info>) {
|
||||
replace_procedure_stage_ids_with_names(
|
||||
&mut resource.config.stages,
|
||||
);
|
||||
let all = all_resources_cache().load();
|
||||
for stage in &mut resource.config.stages {
|
||||
for execution in &mut stage.executions {
|
||||
match &mut execution.execution {
|
||||
Execution::RunProcedure(exec) => exec.procedure.clone_from(
|
||||
all
|
||||
.procedures
|
||||
.get(&exec.procedure)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchRunProcedure(_exec) => {}
|
||||
Execution::RunAction(exec) => exec.action.clone_from(
|
||||
all
|
||||
.actions
|
||||
.get(&exec.action)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchRunAction(_exec) => {}
|
||||
Execution::RunBuild(exec) => exec.build.clone_from(
|
||||
all
|
||||
.builds
|
||||
.get(&exec.build)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchRunBuild(_exec) => {}
|
||||
Execution::CancelBuild(exec) => exec.build.clone_from(
|
||||
all
|
||||
.builds
|
||||
.get(&exec.build)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::Deploy(exec) => exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchDeploy(_exec) => {}
|
||||
Execution::PullDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StartDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RestartDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PauseDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::UnpauseDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StopDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::DestroyDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::BatchDestroyDeployment(_exec) => {}
|
||||
Execution::CloneRepo(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchCloneRepo(_exec) => {}
|
||||
Execution::PullRepo(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchPullRepo(_exec) => {}
|
||||
Execution::BuildRepo(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchBuildRepo(_exec) => {}
|
||||
Execution::CancelRepoBuild(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::StartContainer(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RestartContainer(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PauseContainer(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::UnpauseContainer(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StopContainer(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DestroyContainer(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StartAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RestartAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PauseAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::UnpauseAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StopAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PruneContainers(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeleteNetwork(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneNetworks(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeleteImage(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneImages(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeleteVolume(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneVolumes(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneDockerBuilders(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PruneBuildx(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneSystem(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RunSync(exec) => exec.sync.clone_from(
|
||||
all
|
||||
.syncs
|
||||
.get(&exec.sync)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::CommitSync(exec) => exec.sync.clone_from(
|
||||
all
|
||||
.syncs
|
||||
.get(&exec.sync)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeployStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchDeployStack(_exec) => {}
|
||||
Execution::DeployStackIfChanged(exec) => {
|
||||
exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(_exec) => {}
|
||||
Execution::PullStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchPullStack(_exec) => {}
|
||||
Execution::StartStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RestartStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RunStackService(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PauseStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::UnpauseStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::StopStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DestroyStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchDestroyStack(_exec) => {}
|
||||
Execution::TestAlerter(exec) => exec.alerter.clone_from(
|
||||
all
|
||||
.alerters
|
||||
.get(&exec.alerter)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::SendAlert(exec) => {
|
||||
exec.alerters.iter_mut().for_each(|a| {
|
||||
a.clone_from(
|
||||
all
|
||||
.alerters
|
||||
.get(a)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
})
|
||||
}
|
||||
Execution::RemoveSwarmNodes(exec) => exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RemoveSwarmStacks(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RemoveSwarmServices(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::CreateSwarmConfig(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RotateSwarmConfig(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::CreateSwarmSecret(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RotateSwarmSecret(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::None(_)
|
||||
| Execution::Sleep(_)
|
||||
| Execution::ClearRepoCache(_)
|
||||
| Execution::BackupCoreDatabase(_)
|
||||
| Execution::GlobalAutoUpdate(_)
|
||||
| Execution::RotateAllServerKeys(_)
|
||||
| Execution::RotateCoreKeys(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_to_toml_string(
|
||||
|
||||
@@ -45,7 +45,7 @@ async fn serve_client_file(
|
||||
|
||||
let contents = fs::read_to_string(format!(
|
||||
"{}/client/{path}",
|
||||
core_config().ui_path
|
||||
core_config().frontend_path
|
||||
))
|
||||
.await
|
||||
.with_context(|| format!("Failed to read file: {path}"))?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
FROM rust:1.94.1-trixie AS builder
|
||||
FROM rust:1.91.1-trixie AS builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Sets up the necessary runtime container dependencies for Komodo Periphery.
|
||||
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
ARG X86_64_BINARIES=${BINARIES_IMAGE}-x86_64
|
||||
ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Assumes the latest binaries for the required arch are already built (by binaries.Dockerfile).
|
||||
## Sets up the necessary runtime container dependencies for Komodo Periphery.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
|
||||
|
||||
# This is required to work with COPY --from
|
||||
FROM ${BINARIES_IMAGE} AS binaries
|
||||
|
||||
@@ -307,8 +307,8 @@ impl Resolve<crate::api::Args> for ComposePull {
|
||||
};
|
||||
|
||||
// Canonicalize the path to ensure it exists, and is the cleanest path to the run directory.
|
||||
let run_directory = run_directory.canonicalize().with_context(||
|
||||
format!("Failed to validate run directory on host after stack write (canonicalize error), path={}", run_directory.to_string_lossy()),
|
||||
let run_directory = run_directory.canonicalize().context(
|
||||
"Failed to validate run directory on host after stack write (canonicalize error)",
|
||||
)?;
|
||||
|
||||
let file_paths = stack
|
||||
@@ -352,56 +352,16 @@ impl Resolve<crate::api::Args> for ComposePull {
|
||||
|
||||
let project_name = stack.project_name(false);
|
||||
|
||||
// Parse wrapper configuration
|
||||
let compose_cmd_wrapper =
|
||||
parse_multiline_command(&stack.config.compose_cmd_wrapper);
|
||||
// If wrapper_include is empty but wrapper is set, use default ["up"] for backward compatibility
|
||||
let default_include = vec![String::from("up")];
|
||||
let wrapper_include =
|
||||
if stack.config.compose_cmd_wrapper_include.is_empty()
|
||||
&& !compose_cmd_wrapper.is_empty()
|
||||
{
|
||||
&default_include
|
||||
} else {
|
||||
&stack.config.compose_cmd_wrapper_include
|
||||
};
|
||||
|
||||
let pull_command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull{service_args}",
|
||||
);
|
||||
let (pull_command, wrapped) = match maybe_wrap_command(
|
||||
pull_command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"pull",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
|
||||
let span = info_span!("RunComposePull");
|
||||
let mode = if wrapped {
|
||||
KomodoCommandMode::Shell
|
||||
} else {
|
||||
KomodoCommandMode::Standard
|
||||
};
|
||||
let Some(log) = run_komodo_command_with_sanitization(
|
||||
let log = run_komodo_standard_command(
|
||||
"Compose Pull",
|
||||
run_directory.as_path(),
|
||||
pull_command,
|
||||
mode,
|
||||
&replacers,
|
||||
run_directory.as_ref(),
|
||||
format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull{service_args}",
|
||||
),
|
||||
)
|
||||
.instrument(span)
|
||||
.await
|
||||
else {
|
||||
// Only reachable if command is empty,
|
||||
// not the case since it is provided above.
|
||||
unreachable!()
|
||||
};
|
||||
.await;
|
||||
|
||||
res.logs.push(log);
|
||||
|
||||
@@ -467,8 +427,8 @@ impl Resolve<crate::api::Args> for ComposeUp {
|
||||
};
|
||||
|
||||
// Canonicalize the path to ensure it exists, and is the cleanest path to the run directory.
|
||||
let run_directory = run_directory.canonicalize().with_context(||
|
||||
format!("Failed to validate run directory on host after stack write (canonicalize error), path={}", run_directory.to_string_lossy()),
|
||||
let run_directory = run_directory.canonicalize().context(
|
||||
"Failed to validate run directory on host after stack write (canonicalize error)",
|
||||
)?;
|
||||
|
||||
validate_files(&stack, &run_directory, &mut res).await;
|
||||
@@ -523,54 +483,25 @@ impl Resolve<crate::api::Args> for ComposeUp {
|
||||
&stack.config.additional_env_files,
|
||||
)?;
|
||||
|
||||
// Parse wrapper configuration once for reuse
|
||||
let compose_cmd_wrapper =
|
||||
parse_multiline_command(&stack.config.compose_cmd_wrapper);
|
||||
// If wrapper_include is empty but wrapper is set, use default ["up"] for backward compatibility
|
||||
let default_include = vec![String::from("up")];
|
||||
let wrapper_include =
|
||||
if stack.config.compose_cmd_wrapper_include.is_empty()
|
||||
&& !compose_cmd_wrapper.is_empty()
|
||||
{
|
||||
&default_include
|
||||
} else {
|
||||
&stack.config.compose_cmd_wrapper_include
|
||||
};
|
||||
|
||||
// Uses 'docker compose config' command to extract services (including image)
|
||||
// after performing interpolation
|
||||
{
|
||||
let command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} config",
|
||||
);
|
||||
let (command, wrapped) = match maybe_wrap_command(
|
||||
command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"config",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
let mode = if wrapped {
|
||||
KomodoCommandMode::Shell
|
||||
} else {
|
||||
KomodoCommandMode::Standard
|
||||
};
|
||||
let span = info_span!("GetComposeConfig", command);
|
||||
let Some(config_log) = run_komodo_command_with_sanitization(
|
||||
"Compose Config",
|
||||
run_directory.as_path(),
|
||||
command,
|
||||
mode,
|
||||
KomodoCommandMode::Standard,
|
||||
&replacers,
|
||||
)
|
||||
.instrument(span)
|
||||
.await
|
||||
else {
|
||||
// Only reachable if command is empty,
|
||||
// not the case since it is provided above.
|
||||
unreachable!()
|
||||
};
|
||||
if !config_log.success {
|
||||
@@ -627,29 +558,12 @@ impl Resolve<crate::api::Args> for ComposeUp {
|
||||
let command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} build{build_extra_args}{service_args}",
|
||||
);
|
||||
let (command, wrapped) = match maybe_wrap_command(
|
||||
command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"build",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
let mode = if wrapped {
|
||||
KomodoCommandMode::Shell
|
||||
} else {
|
||||
KomodoCommandMode::Standard
|
||||
};
|
||||
let span = info_span!("ExecuteComposeBuild");
|
||||
let Some(log) = run_komodo_command_with_sanitization(
|
||||
"Compose Build",
|
||||
run_directory.as_path(),
|
||||
command,
|
||||
mode,
|
||||
KomodoCommandMode::Shell,
|
||||
&replacers,
|
||||
)
|
||||
.instrument(span)
|
||||
@@ -670,36 +584,14 @@ impl Resolve<crate::api::Args> for ComposeUp {
|
||||
let command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull{service_args}",
|
||||
);
|
||||
let (command, wrapped) = match maybe_wrap_command(
|
||||
command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"pull",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
let mode = if wrapped {
|
||||
KomodoCommandMode::Shell
|
||||
} else {
|
||||
KomodoCommandMode::Standard
|
||||
};
|
||||
let span = info_span!("RunComposePull");
|
||||
let Some(log) = run_komodo_command_with_sanitization(
|
||||
let log = run_komodo_standard_command(
|
||||
"Compose Pull",
|
||||
run_directory.as_path(),
|
||||
run_directory.as_ref(),
|
||||
command,
|
||||
mode,
|
||||
&replacers,
|
||||
)
|
||||
.instrument(span)
|
||||
.await
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
.await;
|
||||
res.logs.push(log);
|
||||
if !all_logs_success(&res.logs) {
|
||||
return Ok(res);
|
||||
@@ -719,21 +611,24 @@ impl Resolve<crate::api::Args> for ComposeUp {
|
||||
|
||||
// Run compose up
|
||||
let extra_args = format_extra_args(&stack.config.extra_args);
|
||||
let command = format!(
|
||||
let mut command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} up -d{extra_args}{service_args}",
|
||||
);
|
||||
let (command, _) = match maybe_wrap_command(
|
||||
command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"up",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
|
||||
// Apply compose cmd wrapper if configured
|
||||
let compose_cmd_wrapper =
|
||||
parse_multiline_command(&stack.config.compose_cmd_wrapper);
|
||||
if !compose_cmd_wrapper.is_empty() {
|
||||
if !compose_cmd_wrapper.contains("[[COMPOSE_COMMAND]]") {
|
||||
res.logs.push(Log::error(
|
||||
"Compose Command Wrapper",
|
||||
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
|
||||
));
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
command =
|
||||
compose_cmd_wrapper.replace("[[COMPOSE_COMMAND]]", &command);
|
||||
}
|
||||
|
||||
let span = info_span!("ExecuteComposeUp");
|
||||
let Some(log) = run_komodo_command_with_sanitization(
|
||||
@@ -867,8 +762,8 @@ impl Resolve<crate::api::Args> for ComposeRun {
|
||||
}
|
||||
};
|
||||
|
||||
let run_directory = run_directory.canonicalize().with_context(||
|
||||
format!("Failed to validate run directory on host after stack write (canonicalize error), path={}", run_directory.to_string_lossy())
|
||||
let run_directory = run_directory.canonicalize().context(
|
||||
"Failed to validate run directory on host after stack write (canonicalize error)",
|
||||
)?;
|
||||
|
||||
maybe_login_registry(&stack, registry_token, &mut Vec::new())
|
||||
@@ -889,49 +784,15 @@ impl Resolve<crate::api::Args> for ComposeRun {
|
||||
|
||||
let project_name = stack.project_name(true);
|
||||
|
||||
// Parse wrapper configuration
|
||||
let compose_cmd_wrapper =
|
||||
parse_multiline_command(&stack.config.compose_cmd_wrapper);
|
||||
// If wrapper_include is empty but wrapper is set, use default ["up"] for backward compatibility
|
||||
let default_include = vec![String::from("up")];
|
||||
let wrapper_include =
|
||||
if stack.config.compose_cmd_wrapper_include.is_empty()
|
||||
&& !compose_cmd_wrapper.is_empty()
|
||||
{
|
||||
&default_include
|
||||
} else {
|
||||
&stack.config.compose_cmd_wrapper_include
|
||||
};
|
||||
|
||||
if pull.unwrap_or_default() {
|
||||
let pull_command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull {service}",
|
||||
);
|
||||
let (pull_command, wrapped) = match maybe_wrap_command(
|
||||
pull_command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"pull",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => return Ok(log),
|
||||
};
|
||||
let mode = if wrapped {
|
||||
KomodoCommandMode::Shell
|
||||
} else {
|
||||
KomodoCommandMode::Standard
|
||||
};
|
||||
let Some(pull_log) = run_komodo_command_with_sanitization(
|
||||
let pull_log = run_komodo_standard_command(
|
||||
"Compose Pull",
|
||||
run_directory.as_path(),
|
||||
pull_command,
|
||||
mode,
|
||||
&replacers,
|
||||
run_directory.as_ref(),
|
||||
format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull {service}",
|
||||
),
|
||||
)
|
||||
.await
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
.await;
|
||||
if !pull_log.success {
|
||||
return Ok(pull_log);
|
||||
}
|
||||
@@ -978,24 +839,15 @@ impl Resolve<crate::api::Args> for ComposeRun {
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let run_command = format!(
|
||||
let command = format!(
|
||||
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} run{run_flags} {service}{command_args}",
|
||||
);
|
||||
let (run_command, _) = match maybe_wrap_command(
|
||||
run_command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"run",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => return Ok(log),
|
||||
};
|
||||
|
||||
let span = info_span!("RunComposeRun", run_command);
|
||||
let span = info_span!("RunComposeRun", command);
|
||||
let Some(log) = run_komodo_command_with_sanitization(
|
||||
"Compose Run",
|
||||
run_directory.as_path(),
|
||||
run_command,
|
||||
command,
|
||||
KomodoCommandMode::Shell,
|
||||
&replacers,
|
||||
)
|
||||
@@ -1036,33 +888,6 @@ fn env_file_args(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Apply compose_cmd_wrapper to command if the subcommand is in wrapper_include list.
|
||||
/// Returns Ok((command, wrapped)) where `wrapped` indicates if wrapper was applied.
|
||||
/// Returns Err(Log) if wrapper is invalid (missing placeholder).
|
||||
fn maybe_wrap_command(
|
||||
command: String,
|
||||
wrapper: &str,
|
||||
wrapper_include: &[String],
|
||||
subcommand: &str,
|
||||
) -> Result<(String, bool), Log> {
|
||||
// Skip wrapping if wrapper is empty or subcommand is not in include list
|
||||
if wrapper.is_empty()
|
||||
|| !wrapper_include.iter().any(|c| c == subcommand)
|
||||
{
|
||||
return Ok((command, false));
|
||||
}
|
||||
|
||||
// Validate wrapper contains placeholder
|
||||
if !wrapper.contains("[[COMPOSE_COMMAND]]") {
|
||||
return Err(Log::error(
|
||||
"Compose Command Wrapper",
|
||||
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok((wrapper.replace("[[COMPOSE_COMMAND]]", &command), true))
|
||||
}
|
||||
|
||||
#[instrument("ComposeDown", skip(res))]
|
||||
async fn compose_down(
|
||||
project: &str,
|
||||
|
||||
@@ -99,9 +99,13 @@ impl Resolve<crate::api::Args> for UpdateSwarmNode {
|
||||
}
|
||||
|
||||
if let Some(label_add) = self.label_add {
|
||||
for key_value in label_add {
|
||||
for (key, value) in label_add {
|
||||
command += " --label-add ";
|
||||
command += &key_value;
|
||||
command += &key;
|
||||
if let Some(value) = value {
|
||||
command += "=";
|
||||
command += &value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,7 @@ use komodo_client::{
|
||||
entities::{
|
||||
all_logs_success,
|
||||
docker::stack::SwarmStack,
|
||||
stack::{
|
||||
AdditionalEnvFile, ComposeFile, ComposeService,
|
||||
StackServiceNames,
|
||||
},
|
||||
stack::{ComposeFile, ComposeService, StackServiceNames},
|
||||
update::Log,
|
||||
},
|
||||
parsers::parse_multiline_command,
|
||||
@@ -33,32 +30,6 @@ use crate::{
|
||||
state::docker_client,
|
||||
};
|
||||
|
||||
/// Apply compose_cmd_wrapper to command if the subcommand is in wrapper_include list.
|
||||
/// Returns Ok((command, wrapped)) where `wrapped` indicates if wrapper was applied.
|
||||
fn maybe_wrap_command(
|
||||
command: String,
|
||||
wrapper: &str,
|
||||
wrapper_include: &[String],
|
||||
subcommand: &str,
|
||||
) -> Result<(String, bool), Log> {
|
||||
// Skip wrapping if wrapper is empty or subcommand is not in include list
|
||||
if wrapper.is_empty()
|
||||
|| !wrapper_include.iter().any(|c| c == subcommand)
|
||||
{
|
||||
return Ok((command, false));
|
||||
}
|
||||
|
||||
// Validate wrapper contains placeholder
|
||||
if !wrapper.contains("[[COMPOSE_COMMAND]]") {
|
||||
return Err(Log::error(
|
||||
"Compose Command Wrapper",
|
||||
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok((wrapper.replace("[[COMPOSE_COMMAND]]", &command), true))
|
||||
}
|
||||
|
||||
impl Resolve<crate::api::Args> for InspectSwarmStack {
|
||||
async fn resolve(
|
||||
self,
|
||||
@@ -142,7 +113,7 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
replacers.extend(interpolator.secret_replacers);
|
||||
|
||||
// Env files are not supported by docker stack deploy so are ignored.
|
||||
let (run_directory, env_file_path) = match write_stack(
|
||||
let (run_directory, _) = match write_stack(
|
||||
&stack,
|
||||
repo.as_ref(),
|
||||
git_token,
|
||||
@@ -207,53 +178,16 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
let last_project_name = stack.project_name(false);
|
||||
let project_name = stack.project_name(true);
|
||||
|
||||
// Parse wrapper configuration once for reuse
|
||||
let compose_cmd_wrapper =
|
||||
parse_multiline_command(&stack.config.compose_cmd_wrapper);
|
||||
// If wrapper_include is empty but wrapper is set, use default ["deploy"] for backward compatibility
|
||||
let default_include = vec![String::from("deploy")];
|
||||
let wrapper_include =
|
||||
if stack.config.compose_cmd_wrapper_include.is_empty()
|
||||
&& !compose_cmd_wrapper.is_empty()
|
||||
{
|
||||
&default_include
|
||||
} else {
|
||||
&stack.config.compose_cmd_wrapper_include
|
||||
};
|
||||
|
||||
let env_file_args = env_file_args(
|
||||
env_file_path,
|
||||
&stack.config.additional_env_files,
|
||||
)?;
|
||||
|
||||
// Uses 'docker stack config' command to extract services (including image)
|
||||
// after performing interpolation
|
||||
{
|
||||
let command =
|
||||
format!("{env_file_args}docker stack config -c {file_args}",);
|
||||
let (command, wrapped) = match maybe_wrap_command(
|
||||
command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"config",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
let mode = if wrapped || !env_file_args.is_empty() {
|
||||
KomodoCommandMode::Shell
|
||||
} else {
|
||||
KomodoCommandMode::Standard
|
||||
};
|
||||
let command = format!("docker stack config -c {file_args}",);
|
||||
let span = info_span!("GetStackConfig", command);
|
||||
let Some(config_log) = run_komodo_command_with_sanitization(
|
||||
"Stack Config",
|
||||
run_directory.as_path(),
|
||||
command,
|
||||
mode,
|
||||
KomodoCommandMode::Standard,
|
||||
&replacers,
|
||||
)
|
||||
.instrument(span)
|
||||
@@ -297,9 +231,8 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
}
|
||||
|
||||
// Run stack deploy
|
||||
let mut command = format!(
|
||||
"{env_file_args}docker stack deploy --detach=true -c {file_args}"
|
||||
);
|
||||
let mut command =
|
||||
format!("docker stack deploy --detach=false -c {file_args}");
|
||||
if use_with_registry_auth {
|
||||
command += " --with-registry-auth";
|
||||
}
|
||||
@@ -307,18 +240,19 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
write!(&mut command, " {project_name}")?;
|
||||
|
||||
// Apply compose cmd wrapper if configured
|
||||
let (command, _) = match maybe_wrap_command(
|
||||
command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"deploy",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
let compose_cmd_wrapper =
|
||||
parse_multiline_command(&stack.config.compose_cmd_wrapper);
|
||||
if !compose_cmd_wrapper.is_empty() {
|
||||
if !compose_cmd_wrapper.contains("[[COMPOSE_COMMAND]]") {
|
||||
res.logs.push(Log::error(
|
||||
"Compose Command Wrapper",
|
||||
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
|
||||
));
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
command =
|
||||
compose_cmd_wrapper.replace("[[COMPOSE_COMMAND]]", &command);
|
||||
}
|
||||
|
||||
let span = info_span!("ExecuteStackDeploy");
|
||||
let Some(log) = run_komodo_command_with_sanitization(
|
||||
@@ -359,43 +293,6 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prefix docker stack deploy command.
|
||||
///
|
||||
/// Produces either
|
||||
/// - Empty string (no env files defined)
|
||||
/// - 'set -a && . .env1 && . .env2 && '
|
||||
fn env_file_args(
|
||||
env_file_path: Option<&str>,
|
||||
additional_env_files: &[AdditionalEnvFile],
|
||||
) -> anyhow::Result<String> {
|
||||
let mut res = String::new();
|
||||
|
||||
// Add additional env files (except komodo's own, which comes last)
|
||||
for file in additional_env_files
|
||||
.iter()
|
||||
.filter(|f| env_file_path != Some(f.path.as_str()))
|
||||
{
|
||||
let path = &file.path;
|
||||
write!(res, ". {path} && ").with_context(|| {
|
||||
format!("Failed to write env source arg for {path}")
|
||||
})?;
|
||||
}
|
||||
|
||||
// Add komodo's env file last for highest priority
|
||||
if let Some(file) = env_file_path {
|
||||
write!(res, ". {file} && ").with_context(|| {
|
||||
format!("Failed to write env source arg for {file}")
|
||||
})?;
|
||||
}
|
||||
|
||||
if !res.is_empty() {
|
||||
// Add 'set -a' so vars are auto exported to child 'docker stack' process.
|
||||
Ok(format!("set -a && {res}"))
|
||||
} else {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument("RemoveStack", skip(res))]
|
||||
async fn remove_stack(
|
||||
stack: &str,
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use anyhow::{Context, anyhow};
|
||||
use futures_util::{Stream, StreamExt, TryStreamExt};
|
||||
use komodo_client::entities::{
|
||||
KOMODO_EXIT_CODE, NoData, optional_string,
|
||||
KOMODO_EXIT_CODE, NoData,
|
||||
terminal::{Terminal, TerminalStdinMessage, TerminalTarget},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
@@ -52,32 +52,20 @@ impl Resolve<crate::api::Args> for CreateServerTerminal {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &crate::api::Args,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<NoData> {
|
||||
if periphery_config().disable_terminals {
|
||||
return Err(anyhow!(
|
||||
"Terminals are disabled in the Periphery config"
|
||||
));
|
||||
}
|
||||
let existing =
|
||||
list_terminals(Some(&TerminalTarget::Server { server: None }))
|
||||
.await;
|
||||
create_terminal(
|
||||
self
|
||||
.name
|
||||
.and_then(optional_string)
|
||||
.unwrap_or_else(|| format!("term-{}", existing.len())),
|
||||
self.name,
|
||||
TerminalTarget::Server { server: None },
|
||||
self.command,
|
||||
self.recreate,
|
||||
)
|
||||
.await
|
||||
.map(|terminal| Terminal {
|
||||
name: terminal.name.clone(),
|
||||
target: TerminalTarget::Server { server: None },
|
||||
command: terminal.command.clone(),
|
||||
stored_size_kb: terminal.history.size_kb(),
|
||||
created_at: terminal.created_at,
|
||||
})
|
||||
.map(|_| NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +87,7 @@ impl Resolve<crate::api::Args> for CreateContainerExecTerminal {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &crate::api::Args,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<NoData> {
|
||||
if periphery_config().disable_container_terminals {
|
||||
return Err(anyhow!(
|
||||
"Container Terminals are disabled in the Periphery config"
|
||||
@@ -118,23 +106,14 @@ impl Resolve<crate::api::Args> for CreateContainerExecTerminal {
|
||||
"The use of '&&' is forbidden in the container name or command"
|
||||
));
|
||||
}
|
||||
let existing = list_terminals(Some(&target)).await;
|
||||
create_terminal(
|
||||
name.and_then(optional_string).unwrap_or_else(|| {
|
||||
format!("exec-{container}-{}", existing.len())
|
||||
}),
|
||||
name,
|
||||
target,
|
||||
Some(format!("docker exec -it {container} {command}")),
|
||||
recreate,
|
||||
)
|
||||
.await
|
||||
.map(|terminal| Terminal {
|
||||
name: terminal.name.clone(),
|
||||
target: terminal.target.clone(),
|
||||
command: terminal.command.clone(),
|
||||
stored_size_kb: terminal.history.size_kb(),
|
||||
created_at: terminal.created_at,
|
||||
})
|
||||
.map(|_| NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +134,7 @@ impl Resolve<crate::api::Args> for CreateContainerAttachTerminal {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &crate::api::Args,
|
||||
) -> anyhow::Result<Terminal> {
|
||||
) -> anyhow::Result<NoData> {
|
||||
if periphery_config().disable_container_terminals {
|
||||
return Err(anyhow!(
|
||||
"Container Terminals are disabled in the Periphery config"
|
||||
@@ -172,23 +151,14 @@ impl Resolve<crate::api::Args> for CreateContainerAttachTerminal {
|
||||
"The use of '&&' is forbidden in the container name"
|
||||
));
|
||||
}
|
||||
let existing = list_terminals(Some(&target)).await;
|
||||
create_terminal(
|
||||
name.and_then(optional_string).unwrap_or_else(|| {
|
||||
format!("attach-{container}-{}", existing.len())
|
||||
}),
|
||||
name,
|
||||
target,
|
||||
Some(format!("docker attach {container} --sig-proxy=false")),
|
||||
recreate,
|
||||
)
|
||||
.await
|
||||
.map(|terminal| Terminal {
|
||||
name: terminal.name.clone(),
|
||||
target: terminal.target.clone(),
|
||||
command: terminal.command.clone(),
|
||||
stored_size_kb: terminal.history.size_kb(),
|
||||
created_at: terminal.created_at,
|
||||
})
|
||||
.map(|_| NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,9 +144,6 @@ pub fn periphery_config() -> &'static PeripheryConfig {
|
||||
ansi: env
|
||||
.periphery_logging_ansi
|
||||
.unwrap_or(config.logging.ansi),
|
||||
timestamps: env
|
||||
.periphery_logging_timestamps
|
||||
.unwrap_or(config.logging.timestamps),
|
||||
otlp_endpoint: env
|
||||
.periphery_logging_otlp_endpoint
|
||||
.unwrap_or(config.logging.otlp_endpoint),
|
||||
|
||||
@@ -94,13 +94,6 @@ pub async fn handler(
|
||||
|
||||
already_logged_connection_error = false;
|
||||
|
||||
debug!(
|
||||
host = identifiers.host(),
|
||||
query,
|
||||
sec_websocket_accept = accept.to_str().unwrap_or_default(),
|
||||
"[CORE AUTH] Zero trust identifiers"
|
||||
);
|
||||
|
||||
let identifiers =
|
||||
identifiers.build(accept.as_bytes(), query.as_bytes());
|
||||
|
||||
|
||||
@@ -109,13 +109,6 @@ async fn handler(
|
||||
|
||||
let query = format!("core={}", urlencoding::encode(&core));
|
||||
|
||||
debug!(
|
||||
host = identifiers.host.to_str().unwrap_or_default(),
|
||||
query,
|
||||
sec_websocket_accept = identifiers.accept,
|
||||
"[CORE AUTH] Zero trust identifiers"
|
||||
);
|
||||
|
||||
if let Err(e) =
|
||||
handle_login(&mut socket, identifiers.build(query.as_bytes()))
|
||||
.await
|
||||
|
||||
@@ -461,80 +461,80 @@ impl DockerClient {
|
||||
}
|
||||
|
||||
fn convert_summary_container_state(
|
||||
state: bollard::config::ContainerSummaryStateEnum,
|
||||
state: bollard::secret::ContainerSummaryStateEnum,
|
||||
) -> ContainerStateStatusEnum {
|
||||
match state {
|
||||
bollard::config::ContainerSummaryStateEnum::EMPTY => {
|
||||
bollard::secret::ContainerSummaryStateEnum::EMPTY => {
|
||||
ContainerStateStatusEnum::Empty
|
||||
}
|
||||
bollard::config::ContainerSummaryStateEnum::CREATED => {
|
||||
bollard::secret::ContainerSummaryStateEnum::CREATED => {
|
||||
ContainerStateStatusEnum::Created
|
||||
}
|
||||
bollard::config::ContainerSummaryStateEnum::RUNNING => {
|
||||
bollard::secret::ContainerSummaryStateEnum::RUNNING => {
|
||||
ContainerStateStatusEnum::Running
|
||||
}
|
||||
bollard::config::ContainerSummaryStateEnum::PAUSED => {
|
||||
bollard::secret::ContainerSummaryStateEnum::PAUSED => {
|
||||
ContainerStateStatusEnum::Paused
|
||||
}
|
||||
bollard::config::ContainerSummaryStateEnum::RESTARTING => {
|
||||
bollard::secret::ContainerSummaryStateEnum::RESTARTING => {
|
||||
ContainerStateStatusEnum::Restarting
|
||||
}
|
||||
bollard::config::ContainerSummaryStateEnum::EXITED => {
|
||||
bollard::secret::ContainerSummaryStateEnum::EXITED => {
|
||||
ContainerStateStatusEnum::Exited
|
||||
}
|
||||
bollard::config::ContainerSummaryStateEnum::REMOVING => {
|
||||
bollard::secret::ContainerSummaryStateEnum::REMOVING => {
|
||||
ContainerStateStatusEnum::Removing
|
||||
}
|
||||
bollard::config::ContainerSummaryStateEnum::DEAD => {
|
||||
bollard::secret::ContainerSummaryStateEnum::DEAD => {
|
||||
ContainerStateStatusEnum::Dead
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_container_state_status(
|
||||
state: bollard::config::ContainerStateStatusEnum,
|
||||
state: bollard::secret::ContainerStateStatusEnum,
|
||||
) -> ContainerStateStatusEnum {
|
||||
match state {
|
||||
bollard::config::ContainerStateStatusEnum::EMPTY => {
|
||||
bollard::secret::ContainerStateStatusEnum::EMPTY => {
|
||||
ContainerStateStatusEnum::Empty
|
||||
}
|
||||
bollard::config::ContainerStateStatusEnum::CREATED => {
|
||||
bollard::secret::ContainerStateStatusEnum::CREATED => {
|
||||
ContainerStateStatusEnum::Created
|
||||
}
|
||||
bollard::config::ContainerStateStatusEnum::RUNNING => {
|
||||
bollard::secret::ContainerStateStatusEnum::RUNNING => {
|
||||
ContainerStateStatusEnum::Running
|
||||
}
|
||||
bollard::config::ContainerStateStatusEnum::PAUSED => {
|
||||
bollard::secret::ContainerStateStatusEnum::PAUSED => {
|
||||
ContainerStateStatusEnum::Paused
|
||||
}
|
||||
bollard::config::ContainerStateStatusEnum::RESTARTING => {
|
||||
bollard::secret::ContainerStateStatusEnum::RESTARTING => {
|
||||
ContainerStateStatusEnum::Restarting
|
||||
}
|
||||
bollard::config::ContainerStateStatusEnum::EXITED => {
|
||||
bollard::secret::ContainerStateStatusEnum::EXITED => {
|
||||
ContainerStateStatusEnum::Exited
|
||||
}
|
||||
bollard::config::ContainerStateStatusEnum::REMOVING => {
|
||||
bollard::secret::ContainerStateStatusEnum::REMOVING => {
|
||||
ContainerStateStatusEnum::Removing
|
||||
}
|
||||
bollard::config::ContainerStateStatusEnum::DEAD => {
|
||||
bollard::secret::ContainerStateStatusEnum::DEAD => {
|
||||
ContainerStateStatusEnum::Dead
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_port_type(
|
||||
typ: bollard::config::PortSummaryTypeEnum,
|
||||
typ: bollard::secret::PortSummaryTypeEnum,
|
||||
) -> PortTypeEnum {
|
||||
match typ {
|
||||
bollard::config::PortSummaryTypeEnum::EMPTY => {
|
||||
bollard::secret::PortSummaryTypeEnum::EMPTY => {
|
||||
PortTypeEnum::EMPTY
|
||||
}
|
||||
bollard::config::PortSummaryTypeEnum::TCP => PortTypeEnum::TCP,
|
||||
bollard::config::PortSummaryTypeEnum::UDP => PortTypeEnum::UDP,
|
||||
bollard::config::PortSummaryTypeEnum::SCTP => PortTypeEnum::SCTP,
|
||||
bollard::secret::PortSummaryTypeEnum::TCP => PortTypeEnum::TCP,
|
||||
bollard::secret::PortSummaryTypeEnum::UDP => PortTypeEnum::UDP,
|
||||
bollard::secret::PortSummaryTypeEnum::SCTP => PortTypeEnum::SCTP,
|
||||
}
|
||||
}
|
||||
fn convert_port(port: bollard::config::PortSummary) -> Port {
|
||||
fn convert_port(port: bollard::secret::PortSummary) -> Port {
|
||||
Port {
|
||||
ip: port.ip,
|
||||
private_port: port.private_port,
|
||||
@@ -544,27 +544,27 @@ fn convert_port(port: bollard::config::PortSummary) -> Port {
|
||||
}
|
||||
|
||||
fn convert_health_status(
|
||||
status: bollard::config::HealthStatusEnum,
|
||||
status: bollard::secret::HealthStatusEnum,
|
||||
) -> HealthStatusEnum {
|
||||
match status {
|
||||
bollard::config::HealthStatusEnum::EMPTY => {
|
||||
bollard::secret::HealthStatusEnum::EMPTY => {
|
||||
HealthStatusEnum::Empty
|
||||
}
|
||||
bollard::config::HealthStatusEnum::NONE => HealthStatusEnum::None,
|
||||
bollard::config::HealthStatusEnum::STARTING => {
|
||||
bollard::secret::HealthStatusEnum::NONE => HealthStatusEnum::None,
|
||||
bollard::secret::HealthStatusEnum::STARTING => {
|
||||
HealthStatusEnum::Starting
|
||||
}
|
||||
bollard::config::HealthStatusEnum::HEALTHY => {
|
||||
bollard::secret::HealthStatusEnum::HEALTHY => {
|
||||
HealthStatusEnum::Healthy
|
||||
}
|
||||
bollard::config::HealthStatusEnum::UNHEALTHY => {
|
||||
bollard::secret::HealthStatusEnum::UNHEALTHY => {
|
||||
HealthStatusEnum::Unhealthy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_health_check_result(
|
||||
check: bollard::config::HealthcheckResult,
|
||||
check: bollard::secret::HealthcheckResult,
|
||||
) -> HealthcheckResult {
|
||||
HealthcheckResult {
|
||||
start: check.start,
|
||||
@@ -575,57 +575,57 @@ fn convert_health_check_result(
|
||||
}
|
||||
|
||||
fn convert_restart_policy(
|
||||
policy: bollard::config::RestartPolicyNameEnum,
|
||||
policy: bollard::secret::RestartPolicyNameEnum,
|
||||
) -> RestartPolicyNameEnum {
|
||||
match policy {
|
||||
bollard::config::RestartPolicyNameEnum::EMPTY => {
|
||||
bollard::secret::RestartPolicyNameEnum::EMPTY => {
|
||||
RestartPolicyNameEnum::Empty
|
||||
}
|
||||
bollard::config::RestartPolicyNameEnum::NO => {
|
||||
bollard::secret::RestartPolicyNameEnum::NO => {
|
||||
RestartPolicyNameEnum::No
|
||||
}
|
||||
bollard::config::RestartPolicyNameEnum::ALWAYS => {
|
||||
bollard::secret::RestartPolicyNameEnum::ALWAYS => {
|
||||
RestartPolicyNameEnum::Always
|
||||
}
|
||||
bollard::config::RestartPolicyNameEnum::UNLESS_STOPPED => {
|
||||
bollard::secret::RestartPolicyNameEnum::UNLESS_STOPPED => {
|
||||
RestartPolicyNameEnum::UnlessStopped
|
||||
}
|
||||
bollard::config::RestartPolicyNameEnum::ON_FAILURE => {
|
||||
bollard::secret::RestartPolicyNameEnum::ON_FAILURE => {
|
||||
RestartPolicyNameEnum::OnFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_cgroupns_mode(
|
||||
mode: bollard::config::HostConfigCgroupnsModeEnum,
|
||||
mode: bollard::secret::HostConfigCgroupnsModeEnum,
|
||||
) -> HostConfigCgroupnsModeEnum {
|
||||
match mode {
|
||||
bollard::config::HostConfigCgroupnsModeEnum::EMPTY => {
|
||||
bollard::secret::HostConfigCgroupnsModeEnum::EMPTY => {
|
||||
HostConfigCgroupnsModeEnum::Empty
|
||||
}
|
||||
bollard::config::HostConfigCgroupnsModeEnum::PRIVATE => {
|
||||
bollard::secret::HostConfigCgroupnsModeEnum::PRIVATE => {
|
||||
HostConfigCgroupnsModeEnum::Private
|
||||
}
|
||||
bollard::config::HostConfigCgroupnsModeEnum::HOST => {
|
||||
bollard::secret::HostConfigCgroupnsModeEnum::HOST => {
|
||||
HostConfigCgroupnsModeEnum::Host
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_isolation_mode(
|
||||
isolation: bollard::config::HostConfigIsolationEnum,
|
||||
isolation: bollard::secret::HostConfigIsolationEnum,
|
||||
) -> HostConfigIsolationEnum {
|
||||
match isolation {
|
||||
bollard::config::HostConfigIsolationEnum::EMPTY => {
|
||||
bollard::secret::HostConfigIsolationEnum::EMPTY => {
|
||||
HostConfigIsolationEnum::Empty
|
||||
}
|
||||
bollard::config::HostConfigIsolationEnum::DEFAULT => {
|
||||
bollard::secret::HostConfigIsolationEnum::DEFAULT => {
|
||||
HostConfigIsolationEnum::Default
|
||||
}
|
||||
bollard::config::HostConfigIsolationEnum::PROCESS => {
|
||||
bollard::secret::HostConfigIsolationEnum::PROCESS => {
|
||||
HostConfigIsolationEnum::Process
|
||||
}
|
||||
bollard::config::HostConfigIsolationEnum::HYPERV => {
|
||||
bollard::secret::HostConfigIsolationEnum::HYPERV => {
|
||||
HostConfigIsolationEnum::Hyperv
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,17 +27,19 @@ impl DockerClient {
|
||||
.map(|id| id == &image.id)
|
||||
.unwrap_or_default()
|
||||
});
|
||||
let name = image
|
||||
.repo_tags
|
||||
.first()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| image.id.clone());
|
||||
ImageListItem {
|
||||
name: image
|
||||
.repo_tags
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| image.id.clone()),
|
||||
digest: image
|
||||
.repo_digests
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_default(),
|
||||
id: image.id,
|
||||
parent_id: image.parent_id,
|
||||
name,
|
||||
tags: image.repo_tags,
|
||||
digests: image.repo_digests,
|
||||
created: image.created,
|
||||
size: image.size,
|
||||
in_use,
|
||||
@@ -72,10 +74,10 @@ impl DockerClient {
|
||||
content: manifest.size.content,
|
||||
},
|
||||
kind: manifest.kind.map(|kind| match kind {
|
||||
bollard::config::ImageManifestSummaryKindEnum::EMPTY => ImageManifestSummaryKindEnum::Empty,
|
||||
bollard::config::ImageManifestSummaryKindEnum::IMAGE => ImageManifestSummaryKindEnum::Image,
|
||||
bollard::config::ImageManifestSummaryKindEnum::ATTESTATION => ImageManifestSummaryKindEnum::Attestation,
|
||||
bollard::config::ImageManifestSummaryKindEnum::UNKNOWN => ImageManifestSummaryKindEnum::Unknown,
|
||||
bollard::secret::ImageManifestSummaryKindEnum::EMPTY => ImageManifestSummaryKindEnum::Empty,
|
||||
bollard::secret::ImageManifestSummaryKindEnum::IMAGE => ImageManifestSummaryKindEnum::Image,
|
||||
bollard::secret::ImageManifestSummaryKindEnum::ATTESTATION => ImageManifestSummaryKindEnum::Attestation,
|
||||
bollard::secret::ImageManifestSummaryKindEnum::UNKNOWN => ImageManifestSummaryKindEnum::Unknown,
|
||||
}),
|
||||
image_data: manifest.image_data.map(|data| {
|
||||
ImageManifestSummaryImageData {
|
||||
@@ -159,7 +161,7 @@ impl DockerClient {
|
||||
}
|
||||
|
||||
fn convert_oci_descriptor(
|
||||
descriptor: bollard::config::OciDescriptor,
|
||||
descriptor: bollard::secret::OciDescriptor,
|
||||
) -> OciDescriptor {
|
||||
OciDescriptor {
|
||||
media_type: descriptor.media_type,
|
||||
@@ -174,7 +176,7 @@ fn convert_oci_descriptor(
|
||||
}
|
||||
|
||||
fn convert_oci_platform(
|
||||
platform: bollard::config::OciPlatform,
|
||||
platform: bollard::secret::OciPlatform,
|
||||
) -> OciPlatform {
|
||||
OciPlatform {
|
||||
architecture: platform.architecture,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user