mirror of
https://github.com/moghtech/komodo.git
synced 2026-07-11 10:04:11 -05:00
Compare commits
470 Commits
2.3.0
...
v2.0.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,5 +1,2 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
|
||||
[build]
|
||||
rustflags = ["-Wunused-crate-dependencies"]
|
||||
rustflags = ["-Wunused-crate-dependencies"]
|
||||
@@ -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
|
||||
56
.github/workflows/ci.yml
vendored
56
.github/workflows/ci.yml
vendored
@@ -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
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -6,4 +6,8 @@ deno.lock
|
||||
.env.development
|
||||
.DS_Store
|
||||
.idea
|
||||
|
||||
/frontend/build
|
||||
/lib/ts_client/build
|
||||
|
||||
.dev
|
||||
|
||||
28
.vscode/tasks.json
vendored
28
.vscode/tasks.json
vendored
@@ -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": []
|
||||
},
|
||||
|
||||
3993
Cargo.lock
generated
3993
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
155
Cargo.toml
155
Cargo.toml
@@ -3,140 +3,153 @@ resolver = "2"
|
||||
members = [
|
||||
"bin/*",
|
||||
"lib/*",
|
||||
"xtask",
|
||||
"client/core/rs",
|
||||
"client/periphery/rs",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "2.3.0-dev-32"
|
||||
version = "2.0.0-dev-102"
|
||||
edition = "2024"
|
||||
authors = ["mbecker20 <becker.maxh@gmail.com>"]
|
||||
license = "GPL-3.0-or-later"
|
||||
repository = "https://github.com/moghtech/komodo"
|
||||
homepage = "https://komo.do"
|
||||
|
||||
[profile.release]
|
||||
strip = "debuginfo"
|
||||
|
||||
[workspace.dependencies]
|
||||
# LOCAL
|
||||
komodo_client = { path = "client/core/rs" }
|
||||
periphery_client = { path = "client/periphery/rs" }
|
||||
environment_file = { path = "lib/environment_file" }
|
||||
environment = { path = "lib/environment" }
|
||||
interpolate = { path = "lib/interpolate" }
|
||||
secret_file = { path = "lib/secret_file" }
|
||||
validations = { path = "lib/validations" }
|
||||
formatting = { path = "lib/formatting" }
|
||||
rate_limit = { path = "lib/rate_limit" }
|
||||
transport = { path = "lib/transport" }
|
||||
database = { path = "lib/database" }
|
||||
encoding = { path = "lib/encoding" }
|
||||
response = { path = "lib/response" }
|
||||
command = { path = "lib/command" }
|
||||
config = { path = "lib/config" }
|
||||
logger = { path = "lib/logger" }
|
||||
cache = { path = "lib/cache" }
|
||||
noise = { path = "lib/noise" }
|
||||
git = { path = "lib/git" }
|
||||
|
||||
# MOGH
|
||||
serror = { version = "0.5.3", default-features = false }
|
||||
slack = { version = "2.0.0", package = "slack_client_rs", default-features = false, features = ["rustls"] }
|
||||
mogh_error = { version = "1.0.4", default-features = false }
|
||||
derive_default_builder = "0.1.8"
|
||||
derive_empty_traits = "0.1.0"
|
||||
async_timing_util = "1.1.0"
|
||||
mogh_auth_client = "1.7.0"
|
||||
mogh_auth_server = "1.7.0"
|
||||
mogh_secret_file = "1.0.1"
|
||||
mogh_validations = "1.0.1"
|
||||
mogh_rate_limit = "1.0.1"
|
||||
partial_derive2 = "0.5.0"
|
||||
mongo_indexed = "2.1.0"
|
||||
mogh_resolver = "1.0.0"
|
||||
mogh_config = "1.1.0"
|
||||
mogh_logger = "1.3.4"
|
||||
mogh_server = "1.5.0"
|
||||
partial_derive2 = "0.4.3"
|
||||
derive_variants = "1.0.0"
|
||||
mongo_indexed = "2.0.2"
|
||||
resolver_api = "3.0.0"
|
||||
toml_pretty = "2.0.0"
|
||||
mogh_cache = "1.1.2"
|
||||
mogh_pki = "1.1.3"
|
||||
mungos = "3.7.0"
|
||||
mungos = "3.2.2"
|
||||
svi = "1.2.0"
|
||||
|
||||
# ASYNC
|
||||
reqwest = { version = "0.13.4", default-features = false, features = ["json", "stream", "form", "query", "rustls"] }
|
||||
tokio = { version = "1.52.3", 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.2"
|
||||
reqwest = { version = "0.12.24", default-features = false, features = ["json", "stream", "rustls-tls-native-roots"] }
|
||||
tokio = { version = "1.48.0", features = ["full"] }
|
||||
tokio-util = { version = "0.7.17", features = ["io", "codec"] }
|
||||
tokio-stream = { version = "0.1.17", features = ["sync"] }
|
||||
pin-project-lite = "0.2.16"
|
||||
futures-util = "0.3.31"
|
||||
arc-swap = "1.7.1"
|
||||
|
||||
# SERVER
|
||||
tokio-tungstenite = { version = "0.29.0", features = ["rustls-tls-native-roots"] }
|
||||
axum = { version = "0.8.9", features = ["ws", "json", "macros"] }
|
||||
axum-extra = { version = "0.12.6", features = ["typed-header"] }
|
||||
|
||||
# OPENAPI
|
||||
utoipa-scalar = { version = "0.3.0", features = ["axum"] }
|
||||
utoipa = "5.5.0"
|
||||
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-native-roots"] }
|
||||
tower-http = { version = "0.6.8", features = ["fs", "cors", "set-header"] }
|
||||
axum = { version = "0.8.7", features = ["ws", "json", "macros"] }
|
||||
axum-extra = { version = "0.12.2", features = ["typed-header"] }
|
||||
axum-server = { version = "0.8.0", features = ["tls-rustls"] }
|
||||
tower-sessions = "0.14.0"
|
||||
|
||||
# SER/DE
|
||||
ipnetwork = { version = "0.21.1", features = ["serde"] }
|
||||
indexmap = { version = "2.14.0", features = ["serde"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
strum = { version = "0.28.0", features = ["derive"] }
|
||||
indexmap = { version = "2.12.1", features = ["serde"] }
|
||||
serde = { version = "1.0.227", 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"
|
||||
serde_yaml_ng = "0.10.0"
|
||||
serde_json = "1.0.150"
|
||||
serde_qs = "1.1.2"
|
||||
url = "2.5.8"
|
||||
serde_json = "1.0.145"
|
||||
serde_qs = "0.15.0"
|
||||
toml = "0.9.8"
|
||||
url = "2.5.7"
|
||||
|
||||
# ERROR
|
||||
anyhow = "1.0.103"
|
||||
thiserror = "2.0.18"
|
||||
|
||||
# SYSTEM
|
||||
libc = "0.2.186"
|
||||
anyhow = "1.0.100"
|
||||
thiserror = "2.0.17"
|
||||
|
||||
# LOGGING
|
||||
tracing = "0.1.44"
|
||||
opentelemetry-otlp = { version = "0.31.0", features = ["tls-roots", "reqwest-rustls"] }
|
||||
opentelemetry_sdk = { version = "0.31.0", features = ["rt-tokio"] }
|
||||
tracing-subscriber = { version = "0.3.22", features = ["json"] }
|
||||
opentelemetry-semantic-conventions = "0.31.0"
|
||||
tracing-opentelemetry = "0.32.0"
|
||||
opentelemetry = "0.31.0"
|
||||
tracing = "0.1.43"
|
||||
|
||||
# CONFIG
|
||||
clap = { version = "4.6.1", features = ["derive"] }
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
dotenvy = "0.15.7"
|
||||
envy = "0.4.2"
|
||||
|
||||
# CRYPTO / AUTH
|
||||
uuid = { version = "1.23.4", features = ["v4", "fast-rng", "serde"] }
|
||||
rustls = { version = "0.23.41", features = ["aws-lc-rs"] }
|
||||
data-encoding = "2.11.0"
|
||||
webauthn-rs = { version = "0.5.4", features = ["danger-allow-state-serialisation"] }
|
||||
openidconnect = { version = "4.0.1", features = ["accept-rfc3339-timestamps"] }
|
||||
uuid = { version = "1.19.0", features = ["v4", "fast-rng", "serde"] }
|
||||
jsonwebtoken = { version = "10.2.0", features = ["aws_lc_rs"] } # locked back with octorust
|
||||
rustls = { version = "0.23.35", features = ["aws-lc-rs"] }
|
||||
pem-rfc7468 = { version = "1.0.0", features = ["alloc"] }
|
||||
totp-rs = { version = "5.7.0", features = ["qr"] }
|
||||
webauthn-rs-proto = "0.5.4"
|
||||
base64urlsafedata = "0.5.4"
|
||||
data-encoding = "2.9.0"
|
||||
urlencoding = "2.1.3"
|
||||
bcrypt = "0.19.2"
|
||||
hmac = "0.13.0"
|
||||
sha1 = "0.11.0"
|
||||
sha2 = "0.11.0"
|
||||
rand = "0.10.1"
|
||||
bcrypt = "0.17.1"
|
||||
pkcs8 = "0.10.2"
|
||||
snow = "0.10.0"
|
||||
hmac = "0.12.1"
|
||||
sha1 = "0.10.6"
|
||||
sha2 = "0.10.9"
|
||||
rand = "0.9.2"
|
||||
spki = "0.7.3"
|
||||
der = "0.7.10"
|
||||
hex = "0.4.3"
|
||||
|
||||
# SYSTEM
|
||||
hickory-resolver = "0.26.1"
|
||||
hickory-resolver = "0.25.2"
|
||||
portable-pty = "0.9.0"
|
||||
shell-escape = "0.1.5"
|
||||
crossterm = "0.29.0"
|
||||
bollard = "0.21.0"
|
||||
sysinfo = "0.39.5"
|
||||
shlex = "2.0.1"
|
||||
bollard = "0.19.4"
|
||||
sysinfo = "0.37.1"
|
||||
shlex = "1.3.0"
|
||||
|
||||
# CLOUD
|
||||
aws-config = "1.8.18"
|
||||
aws-sdk-ec2 = "1.235.0"
|
||||
aws-credential-types = "1.2.14"
|
||||
aws-config = "1.8.12"
|
||||
aws-sdk-ec2 = "1.196.0"
|
||||
aws-credential-types = "1.2.11"
|
||||
|
||||
## CRON
|
||||
english-to-cron = "0.1.7"
|
||||
chrono-tz = "0.10.4"
|
||||
chrono = "0.4.45"
|
||||
chrono = "0.4.42"
|
||||
croner = "3.0.1"
|
||||
|
||||
# MISC
|
||||
async-compression = { version = "0.4.42", features = ["tokio", "gzip"] }
|
||||
schemars = { version = "1.2.1", features = ["indexmap2"] }
|
||||
async-compression = { version = "0.4.35", features = ["tokio", "gzip"] }
|
||||
derive_builder = "0.20.2"
|
||||
comfy-table = "7.2.2"
|
||||
typeshare = "1.0.5"
|
||||
comfy-table = "7.2.1"
|
||||
typeshare = "1.0.4"
|
||||
wildcard = "0.3.0"
|
||||
colored = "3.1.1"
|
||||
bytes = "1.12.0"
|
||||
regex = "1.12.4"
|
||||
|
||||
[profile.release]
|
||||
strip = "debuginfo"
|
||||
colored = "3.0.0"
|
||||
bytes = "1.11.0"
|
||||
regex = "1.12.2"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const cmd = "km run -y action deploy-komodo-ui-change";
|
||||
const cmd = "km run -y action deploy-komodo-fe-change";
|
||||
new Deno.Command("bash", {
|
||||
args: ["-c", cmd],
|
||||
}).spawn();
|
||||
@@ -1,7 +1,7 @@
|
||||
## Builds the Komodo Core, Periphery, and Util binaries
|
||||
## for a specific architecture. Requires OpenSSL 3 or later.
|
||||
|
||||
FROM rust:1.96.1-bookworm AS builder
|
||||
FROM rust:1.91.1-bookworm AS builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
@@ -12,7 +12,6 @@ COPY ./client/periphery ./client/periphery
|
||||
COPY ./bin/core ./bin/core
|
||||
COPY ./bin/periphery ./bin/periphery
|
||||
COPY ./bin/cli ./bin/cli
|
||||
COPY ./xtask ./xtask
|
||||
|
||||
# Compile bin
|
||||
RUN \
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
## Uses chef for dependency caching to help speed up back-to-back builds.
|
||||
|
||||
FROM lukemathwalker/cargo-chef:latest-rust-1.96.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
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "komodo_cli"
|
||||
description = "Command line tool for Komodo"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -13,12 +14,12 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# local
|
||||
komodo_client = { workspace = true, features = ["cli"] }
|
||||
mogh_secret_file.workspace = true
|
||||
mogh_pki.workspace = true
|
||||
environment_file.workspace = true
|
||||
komodo_client.workspace = true
|
||||
database.workspace = true
|
||||
mogh_config.workspace = true
|
||||
mogh_logger.workspace = true
|
||||
config.workspace = true
|
||||
logger.workspace = true
|
||||
noise.workspace = true
|
||||
# external
|
||||
futures-util.workspace = true
|
||||
comfy-table.workspace = true
|
||||
@@ -31,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.96.1-trixie 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
|
||||
|
||||
@@ -9,7 +9,7 @@ ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
|
||||
FROM ${X86_64_BINARIES} AS x86_64
|
||||
FROM ${AARCH64_BINARIES} AS aarch64
|
||||
|
||||
FROM debian:trixie-slim
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,7 +7,9 @@ use futures_util::{
|
||||
FutureExt, TryStreamExt, stream::FuturesUnordered,
|
||||
};
|
||||
use komodo_client::{
|
||||
api::read::{self, ListAllContainers, ListServers},
|
||||
api::read::{
|
||||
InspectDockerContainer, ListAllDockerContainers, ListServers,
|
||||
},
|
||||
entities::{
|
||||
config::cli::args::container::{
|
||||
Container, ContainerCommand, InspectContainer,
|
||||
@@ -42,12 +44,11 @@ async fn list_containers(
|
||||
down,
|
||||
links,
|
||||
reverse,
|
||||
containers: terms,
|
||||
containers: names,
|
||||
images,
|
||||
networks,
|
||||
servers,
|
||||
format,
|
||||
page,
|
||||
command: _,
|
||||
}: &Container,
|
||||
) -> anyhow::Result<()> {
|
||||
@@ -59,14 +60,9 @@ async fn list_containers(
|
||||
.into_iter()
|
||||
.map(|s| (s.id.clone(), s))
|
||||
.collect::<HashMap<_, _>>())),
|
||||
client.read(ListAllContainers {
|
||||
client.read(ListAllDockerContainers {
|
||||
servers: Default::default(),
|
||||
tags: Default::default(),
|
||||
terms: terms.clone(),
|
||||
state: Default::default(),
|
||||
limit: Some(100),
|
||||
// Page is more naturally given starting as 1, 2, 3.
|
||||
page: if *page == 0 { 0 } else { *page - 1 },
|
||||
containers: Default::default(),
|
||||
}),
|
||||
)?;
|
||||
|
||||
@@ -82,6 +78,7 @@ async fn list_containers(
|
||||
(Some(server.name.as_str()), c)
|
||||
});
|
||||
|
||||
let names = parse_wildcards(names);
|
||||
let servers = parse_wildcards(servers);
|
||||
let images = parse_wildcards(images);
|
||||
let networks = parse_wildcards(networks);
|
||||
@@ -108,6 +105,7 @@ async fn list_containers(
|
||||
);
|
||||
state_check
|
||||
&& network_check
|
||||
&& matches_wildcards(&names, &[c.name.as_str()])
|
||||
&& matches_wildcards(
|
||||
&servers,
|
||||
&server_name
|
||||
@@ -147,13 +145,9 @@ pub async fn inspect_container(
|
||||
.into_iter()
|
||||
.map(|s| (s.id.clone(), s))
|
||||
.collect::<HashMap<_, _>>())),
|
||||
client.read(ListAllContainers {
|
||||
servers: inspect.servers.clone(),
|
||||
tags: Default::default(),
|
||||
terms: vec![inspect.container.clone()],
|
||||
state: Default::default(),
|
||||
limit: Some(300),
|
||||
page: 0,
|
||||
client.read(ListAllDockerContainers {
|
||||
servers: Default::default(),
|
||||
containers: Default::default()
|
||||
}),
|
||||
)?;
|
||||
|
||||
@@ -168,11 +162,25 @@ pub async fn inspect_container(
|
||||
c.server_id = Some(server.name.clone());
|
||||
});
|
||||
|
||||
let names = [inspect.container.to_string()];
|
||||
let names = parse_wildcards(&names);
|
||||
let servers = parse_wildcards(&inspect.servers);
|
||||
|
||||
let mut containers = containers
|
||||
.into_iter()
|
||||
.filter(|c| {
|
||||
matches_wildcards(&names, &[c.name.as_str()])
|
||||
&& matches_wildcards(
|
||||
&servers,
|
||||
&c.server_id
|
||||
.as_deref()
|
||||
.map(|i| vec![i])
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
})
|
||||
.map(|c| async move {
|
||||
client
|
||||
.read(read::InspectContainer {
|
||||
.read(InspectDockerContainer {
|
||||
container: c.name,
|
||||
server: c.server_id.context("No server...")?,
|
||||
})
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -358,11 +358,6 @@ async fn v1_downgrade(yes: bool) -> anyhow::Result<()> {
|
||||
.await
|
||||
.context("Failed to downgrade Server schema")?;
|
||||
|
||||
db.collection::<Document>("Deployment")
|
||||
.update_many(doc! {}, doc! { "$set": { "info": null } })
|
||||
.await
|
||||
.context("Failed to downgrade Deployment schema")?;
|
||||
|
||||
info!(
|
||||
"V1 Downgrade complete. Ready to downgrade to komodo-core:1 ✅"
|
||||
);
|
||||
|
||||
@@ -28,141 +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,
|
||||
CancelAction,
|
||||
RunProcedure,
|
||||
CancelProcedure,
|
||||
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)) => {
|
||||
|
||||
@@ -7,8 +7,7 @@ use komodo_client::{
|
||||
api::read::{
|
||||
ListActions, ListAlerters, ListBuilders, ListBuilds,
|
||||
ListDeployments, ListProcedures, ListRepos, ListResourceSyncs,
|
||||
ListSchedules, ListServers, ListStacks, ListSwarms, ListTags,
|
||||
ListTerminals,
|
||||
ListSchedules, ListServers, ListStacks, ListTags, ListTerminals,
|
||||
},
|
||||
entities::{
|
||||
ResourceTargetVariant,
|
||||
@@ -32,7 +31,6 @@ use komodo_client::{
|
||||
schedule::Schedule,
|
||||
server::{ServerListItem, ServerListItemInfo, ServerState},
|
||||
stack::{StackListItem, StackListItemInfo, StackState},
|
||||
swarm::{SwarmListItem, SwarmListItemInfo, SwarmState},
|
||||
sync::{
|
||||
ResourceSyncListItem, ResourceSyncListItemInfo,
|
||||
ResourceSyncState,
|
||||
@@ -54,46 +52,28 @@ pub async fn handle(list: &args::list::List) -> anyhow::Result<()> {
|
||||
match &list.command {
|
||||
None => list_all(list).await,
|
||||
Some(ListCommand::Servers(filters)) => {
|
||||
list_resources::<ServerListItem>(filters, list.page, false)
|
||||
.await
|
||||
}
|
||||
Some(ListCommand::Swarms(filters)) => {
|
||||
list_resources::<SwarmListItem>(filters, list.page, false).await
|
||||
list_resources::<ServerListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Stacks(filters)) => {
|
||||
list_resources::<StackListItem>(filters, list.page, false).await
|
||||
list_resources::<StackListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Deployments(filters)) => {
|
||||
list_resources::<DeploymentListItem>(filters, list.page, false)
|
||||
.await
|
||||
list_resources::<DeploymentListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Builds(filters)) => {
|
||||
list_resources::<BuildListItem>(filters, list.page, false).await
|
||||
list_resources::<BuildListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Repos(filters)) => {
|
||||
list_resources::<RepoListItem>(filters, list.page, false).await
|
||||
list_resources::<RepoListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Procedures(filters)) => {
|
||||
list_resources::<ProcedureListItem>(filters, list.page, false)
|
||||
.await
|
||||
list_resources::<ProcedureListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Actions(filters)) => {
|
||||
list_resources::<ActionListItem>(filters, list.page, false)
|
||||
.await
|
||||
list_resources::<ActionListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Syncs(filters)) => {
|
||||
list_resources::<ResourceSyncListItem>(
|
||||
filters, list.page, false,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Some(ListCommand::Builders(filters)) => {
|
||||
list_resources::<BuilderListItem>(filters, list.page, false)
|
||||
.await
|
||||
}
|
||||
Some(ListCommand::Alerters(filters)) => {
|
||||
list_resources::<AlerterListItem>(filters, list.page, false)
|
||||
.await
|
||||
list_resources::<ResourceSyncListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Terminals(filters)) => {
|
||||
list_terminals(filters).await
|
||||
@@ -101,6 +81,12 @@ pub async fn handle(list: &args::list::List) -> anyhow::Result<()> {
|
||||
Some(ListCommand::Schedules(filters)) => {
|
||||
list_schedules(filters).await
|
||||
}
|
||||
Some(ListCommand::Builders(filters)) => {
|
||||
list_resources::<BuilderListItem>(filters, false).await
|
||||
}
|
||||
Some(ListCommand::Alerters(filters)) => {
|
||||
list_resources::<AlerterListItem>(filters, false).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +97,6 @@ async fn list_all(list: &args::list::List) -> anyhow::Result<()> {
|
||||
let (
|
||||
tags,
|
||||
mut servers,
|
||||
mut swarms,
|
||||
mut stacks,
|
||||
mut deployments,
|
||||
mut builds,
|
||||
@@ -124,15 +109,14 @@ async fn list_all(list: &args::list::List) -> anyhow::Result<()> {
|
||||
.into_iter()
|
||||
.map(|t| (t.id, t.name))
|
||||
.collect::<HashMap<_, _>>())),
|
||||
ServerListItem::list(client, &filters, list.page, true),
|
||||
SwarmListItem::list(client, &filters, list.page, true),
|
||||
StackListItem::list(client, &filters, list.page, true),
|
||||
DeploymentListItem::list(client, &filters, list.page, true),
|
||||
BuildListItem::list(client, &filters, list.page, true),
|
||||
RepoListItem::list(client, &filters, list.page, true),
|
||||
ProcedureListItem::list(client, &filters, list.page, true),
|
||||
ActionListItem::list(client, &filters, list.page, true),
|
||||
ResourceSyncListItem::list(client, &filters, list.page, true),
|
||||
ServerListItem::list(client, &filters, true),
|
||||
StackListItem::list(client, &filters, true),
|
||||
DeploymentListItem::list(client, &filters, true),
|
||||
BuildListItem::list(client, &filters, true),
|
||||
RepoListItem::list(client, &filters, true),
|
||||
ProcedureListItem::list(client, &filters, true),
|
||||
ActionListItem::list(client, &filters, true),
|
||||
ResourceSyncListItem::list(client, &filters, true),
|
||||
)?;
|
||||
|
||||
if !servers.is_empty() {
|
||||
@@ -141,12 +125,6 @@ async fn list_all(list: &args::list::List) -> anyhow::Result<()> {
|
||||
println!();
|
||||
}
|
||||
|
||||
if !swarms.is_empty() {
|
||||
fix_tags(&mut swarms, &tags);
|
||||
print_items(swarms, filters.format, list.links)?;
|
||||
println!();
|
||||
}
|
||||
|
||||
if !stacks.is_empty() {
|
||||
fix_tags(&mut stacks, &tags);
|
||||
print_items(stacks, filters.format, list.links)?;
|
||||
@@ -194,7 +172,6 @@ async fn list_all(list: &args::list::List) -> anyhow::Result<()> {
|
||||
|
||||
async fn list_resources<T>(
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
@@ -203,7 +180,7 @@ where
|
||||
{
|
||||
let client = crate::command::komodo_client().await?;
|
||||
let (mut resources, tags) = tokio::try_join!(
|
||||
T::list(client, filters, page, minimal),
|
||||
T::list(client, filters, minimal),
|
||||
client.read(ListTags::default()).map(|res| res.map(|res| res
|
||||
.into_iter()
|
||||
.map(|t| (t.id, t.name))
|
||||
@@ -302,7 +279,6 @@ where
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
// For use with root `km ls`
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<ResourceListItem<Self::Info>>>;
|
||||
@@ -315,7 +291,6 @@ impl ListResources for ServerListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
_minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let servers = client
|
||||
@@ -325,10 +300,6 @@ impl ListResources for ServerListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?;
|
||||
let names = parse_wildcards(&filters.names);
|
||||
@@ -358,68 +329,17 @@ impl ListResources for ServerListItem {
|
||||
}
|
||||
}
|
||||
|
||||
impl ListResources for SwarmListItem {
|
||||
type Info = SwarmListItemInfo;
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
_minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let servers = client
|
||||
.read(ListSwarms {
|
||||
query: ResourceQuery::builder()
|
||||
.tags(filters.tags.clone())
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?;
|
||||
let names = parse_wildcards(&filters.names);
|
||||
let server_wildcards = parse_wildcards(&filters.servers);
|
||||
let mut servers = servers
|
||||
.into_iter()
|
||||
.filter(|server| {
|
||||
let state_check = if filters.all {
|
||||
true
|
||||
} else if filters.down {
|
||||
!matches!(server.info.state, SwarmState::Healthy)
|
||||
} else if filters.in_progress {
|
||||
false
|
||||
} else {
|
||||
matches!(server.info.state, SwarmState::Healthy)
|
||||
};
|
||||
let name_items = &[server.name.as_str()];
|
||||
state_check
|
||||
&& matches_wildcards(&names, name_items)
|
||||
&& matches_wildcards(&server_wildcards, name_items)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
servers.sort_by(|a, b| {
|
||||
a.info.state.cmp(&b.info.state).then(a.name.cmp(&b.name))
|
||||
});
|
||||
Ok(servers)
|
||||
}
|
||||
}
|
||||
|
||||
impl ListResources for StackListItem {
|
||||
type Info = StackListItemInfo;
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
_minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let (servers, mut stacks) = tokio::try_join!(
|
||||
client
|
||||
.read(ListServers {
|
||||
query: ResourceQuery::builder().build(),
|
||||
limit: Some(0),
|
||||
..Default::default()
|
||||
})
|
||||
.map(|res| res.map(|res| res
|
||||
.into_iter()
|
||||
@@ -431,10 +351,6 @@ impl ListResources for StackListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false
|
||||
})
|
||||
)?;
|
||||
stacks.iter_mut().for_each(|stack| {
|
||||
@@ -490,15 +406,12 @@ impl ListResources for DeploymentListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
_minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let (servers, mut deployments) = tokio::try_join!(
|
||||
client
|
||||
.read(ListServers {
|
||||
query: ResourceQuery::builder().build(),
|
||||
limit: Some(0),
|
||||
..Default::default()
|
||||
})
|
||||
.map(|res| res.map(|res| res
|
||||
.into_iter()
|
||||
@@ -510,10 +423,6 @@ impl ListResources for DeploymentListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false
|
||||
})
|
||||
)?;
|
||||
deployments.iter_mut().for_each(|deployment| {
|
||||
@@ -570,15 +479,12 @@ impl ListResources for BuildListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let (builders, mut builds) = tokio::try_join!(
|
||||
client
|
||||
.read(ListBuilders {
|
||||
query: ResourceQuery::builder().build(),
|
||||
limit: Some(0),
|
||||
..Default::default()
|
||||
})
|
||||
.map(|res| res.map(|res| res
|
||||
.into_iter()
|
||||
@@ -590,10 +496,6 @@ impl ListResources for BuildListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false
|
||||
})
|
||||
)?;
|
||||
builds.iter_mut().for_each(|build| {
|
||||
@@ -645,7 +547,6 @@ impl ListResources for RepoListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
@@ -656,10 +557,6 @@ impl ListResources for RepoListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -698,7 +595,6 @@ impl ListResources for ProcedureListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
@@ -709,10 +605,6 @@ impl ListResources for ProcedureListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -751,7 +643,6 @@ impl ListResources for ActionListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
@@ -762,10 +653,6 @@ impl ListResources for ActionListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -804,7 +691,6 @@ impl ListResources for ResourceSyncListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
@@ -815,10 +701,6 @@ impl ListResources for ResourceSyncListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -854,7 +736,6 @@ impl ListResources for BuilderListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
@@ -865,10 +746,6 @@ impl ListResources for BuilderListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -891,7 +768,6 @@ impl ListResources for AlerterListItem {
|
||||
async fn list(
|
||||
client: &KomodoClient,
|
||||
filters: &ResourceFilters,
|
||||
page: u64,
|
||||
minimal: bool,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
let names = parse_wildcards(&filters.names);
|
||||
@@ -902,10 +778,6 @@ impl ListResources for AlerterListItem {
|
||||
// .tag_behavior(TagQueryBehavior::Any)
|
||||
.templates(filters.templates)
|
||||
.build(),
|
||||
limit: Some(100),
|
||||
page: page.saturating_sub(1),
|
||||
sort_by: Default::default(),
|
||||
sort_desc: false,
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -960,39 +832,6 @@ impl PrintTable for ResourceListItem<ServerListItemInfo> {
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<SwarmListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
&["Swarm", "State", "Tags", "Link"]
|
||||
} else {
|
||||
&["Swarm", "State", "Tags"]
|
||||
}
|
||||
}
|
||||
fn row(self, links: bool) -> Vec<Cell> {
|
||||
let color = match self.info.state {
|
||||
SwarmState::Healthy => Color::Green,
|
||||
SwarmState::Unhealthy => Color::Red,
|
||||
SwarmState::Down => Color::Blue,
|
||||
SwarmState::Unknown => Color::Magenta,
|
||||
};
|
||||
let mut res = vec![
|
||||
Cell::new(self.name).add_attribute(Attribute::Bold),
|
||||
Cell::new(self.info.state.to_string())
|
||||
.fg(color)
|
||||
.add_attribute(Attribute::Bold),
|
||||
Cell::new(self.tags.join(", ")),
|
||||
];
|
||||
if links {
|
||||
res.push(Cell::new(resource_link(
|
||||
&cli_config().host,
|
||||
ResourceTargetVariant::Server,
|
||||
&self.id,
|
||||
)))
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl PrintTable for ResourceListItem<StackListItemInfo> {
|
||||
fn header(links: bool) -> &'static [&'static str] {
|
||||
if links {
|
||||
|
||||
@@ -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, ListAllContainers, 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
|
||||
}
|
||||
|
||||
@@ -118,8 +112,6 @@ async fn get_server(
|
||||
server: Option<String>,
|
||||
container: &str,
|
||||
) -> anyhow::Result<String> {
|
||||
// Don't need to find server for terminal,
|
||||
// user provided it, early return.
|
||||
if let Some(server) = server {
|
||||
return Ok(server);
|
||||
}
|
||||
@@ -127,13 +119,9 @@ async fn get_server(
|
||||
let client = super::komodo_client().await?;
|
||||
|
||||
let mut containers = client
|
||||
.read(ListAllContainers {
|
||||
.read(ListAllDockerContainers {
|
||||
servers: Default::default(),
|
||||
tags: Default::default(),
|
||||
terms: vec![container.to_string()],
|
||||
state: Default::default(),
|
||||
limit: Some(0),
|
||||
page: 0,
|
||||
containers: vec![container.to_string()],
|
||||
})
|
||||
.await?;
|
||||
|
||||
@@ -144,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
|
||||
@@ -162,8 +147,6 @@ async fn get_server(
|
||||
let servers = client
|
||||
.read(ListServers {
|
||||
query: ServerQuery::builder().names(servers).build(),
|
||||
limit: Some(0),
|
||||
..Default::default()
|
||||
})
|
||||
.await?
|
||||
.into_iter()
|
||||
@@ -180,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
|
||||
@@ -265,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
|
||||
{
|
||||
@@ -325,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")?;
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::{path::PathBuf, sync::OnceLock};
|
||||
use anyhow::Context;
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use environment_file::maybe_read_item_from_file;
|
||||
use komodo_client::entities::{
|
||||
config::{
|
||||
DatabaseConfig,
|
||||
@@ -13,7 +14,6 @@ use komodo_client::entities::{
|
||||
},
|
||||
logger::LogConfig,
|
||||
};
|
||||
use mogh_secret_file::maybe_read_item_from_file;
|
||||
|
||||
pub fn cli_args() -> &'static CliArgs {
|
||||
static CLI_ARGS: OnceLock<CliArgs> = OnceLock::new();
|
||||
@@ -74,7 +74,7 @@ pub fn cli_config() -> &'static CliConfig {
|
||||
"Config File Keywords".dimmed(),
|
||||
);
|
||||
}
|
||||
let mut unparsed_config = (mogh_config::ConfigLoader {
|
||||
let mut unparsed_config = (config::ConfigLoader {
|
||||
paths: &config_paths
|
||||
.iter()
|
||||
.map(PathBuf::as_path)
|
||||
@@ -166,7 +166,7 @@ pub fn cli_config() -> &'static CliConfig {
|
||||
else {
|
||||
panic!("Profile config is not Object type.");
|
||||
};
|
||||
mogh_config::merge_config(
|
||||
config::merge_config(
|
||||
unparsed_config,
|
||||
profile_config.clone(),
|
||||
env.komodo_cli_merge_nested_config,
|
||||
@@ -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,10 +12,7 @@ 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)?;
|
||||
logger::init(&config::cli_config().cli_logging)?;
|
||||
let args = config::cli_args();
|
||||
let env = config::cli_env();
|
||||
let debug_load =
|
||||
@@ -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
|
||||
}
|
||||
@@ -72,7 +65,7 @@ async fn app() -> anyhow::Result<()> {
|
||||
command::terminal::handle_attach(attach).await
|
||||
}
|
||||
args::Command::Key { command } => {
|
||||
mogh_pki::cli::handle(command, mogh_pki::PkiKind::Mutual).await
|
||||
noise::key::command::handle(command).await
|
||||
}
|
||||
args::Command::Database { command } => {
|
||||
command::database::handle(command).await
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "komodo_core"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -14,45 +15,50 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# local
|
||||
komodo_client = { workspace = true, features = ["core"] }
|
||||
komodo_client = { workspace = true, features = ["mongo"] }
|
||||
periphery_client.workspace = true
|
||||
mogh_validations.workspace = true
|
||||
environment_file.workspace = true
|
||||
interpolate.workspace = true
|
||||
mogh_secret_file.workspace = true
|
||||
secret_file.workspace = true
|
||||
validations.workspace = true
|
||||
formatting.workspace = true
|
||||
mogh_rate_limit.workspace = true
|
||||
rate_limit.workspace = true
|
||||
transport.workspace = true
|
||||
database.workspace = true
|
||||
encoding.workspace = true
|
||||
response.workspace = true
|
||||
command.workspace = true
|
||||
mogh_config.workspace = true
|
||||
mogh_logger.workspace = true
|
||||
mogh_cache.workspace = true
|
||||
mogh_pki.workspace = true
|
||||
config.workspace = true
|
||||
logger.workspace = true
|
||||
cache.workspace = true
|
||||
noise.workspace = true
|
||||
git.workspace = true
|
||||
# mogh
|
||||
mogh_error = { workspace = true, features = ["axum"] }
|
||||
mogh_auth_client = { workspace = true, features = ["utoipa"] }
|
||||
mogh_auth_server.workspace = true
|
||||
serror = { workspace = true, features = ["axum"] }
|
||||
async_timing_util.workspace = true
|
||||
partial_derive2.workspace = true
|
||||
mogh_resolver.workspace = true
|
||||
mogh_server.workspace = true
|
||||
derive_variants.workspace = true
|
||||
resolver_api.workspace = true
|
||||
toml_pretty.workspace = true
|
||||
slack.workspace = true
|
||||
svi.workspace = true
|
||||
# external
|
||||
aws-credential-types.workspace = true
|
||||
english-to-cron.workspace = true
|
||||
tower-sessions.workspace = true
|
||||
openidconnect.workspace = true
|
||||
data-encoding.workspace = true
|
||||
serde_yaml_ng.workspace = true
|
||||
utoipa-scalar.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
futures-util.workspace = true
|
||||
axum-server.workspace = true
|
||||
aws-sdk-ec2.workspace = true
|
||||
urlencoding.workspace = true
|
||||
webauthn-rs.workspace = true
|
||||
aws-config.workspace = true
|
||||
tokio-util.workspace = true
|
||||
axum-extra.workspace = true
|
||||
tower-http.workspace = true
|
||||
serde_json.workspace = true
|
||||
typeshare.workspace = true
|
||||
chrono-tz.workspace = true
|
||||
@@ -64,12 +70,12 @@ colored.workspace = true
|
||||
tracing.workspace = true
|
||||
reqwest.workspace = true
|
||||
dotenvy.workspace = true
|
||||
totp-rs.workspace = true
|
||||
anyhow.workspace = true
|
||||
bcrypt.workspace = true
|
||||
croner.workspace = true
|
||||
chrono.workspace = true
|
||||
bcrypt.workspace = true
|
||||
rustls.workspace = true
|
||||
utoipa.workspace = true
|
||||
bytes.workspace = true
|
||||
tokio.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
# Build Core
|
||||
FROM rust:1.96.1-trixie AS core-builder
|
||||
FROM rust:1.91.1-trixie AS core-builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
@@ -11,20 +11,19 @@ COPY ./client/core/rs ./client/core/rs
|
||||
COPY ./client/periphery ./client/periphery
|
||||
COPY ./bin/core ./bin/core
|
||||
COPY ./bin/cli ./bin/cli
|
||||
COPY ./xtask ./xtask
|
||||
|
||||
# Compile app
|
||||
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:20.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
|
||||
@@ -38,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
|
||||
@@ -59,8 +58,7 @@ ENV KOMODO_CLI_CONFIG_PATHS="/config"
|
||||
# This ensures any `komodo.cli.*` takes precedence over the Core `/config/*config.*`
|
||||
ENV KOMODO_CLI_CONFIG_KEYWORDS="*config.*,*komodo.cli*.*"
|
||||
|
||||
ENTRYPOINT [ "entrypoint.sh" ]
|
||||
CMD [ "core" ]
|
||||
CMD [ "/bin/bash", "-c", "update-ca-certificates && core" ]
|
||||
|
||||
# Label to prevent Komodo from stopping with StopAllContainers
|
||||
LABEL komodo.skip="true"
|
||||
|
||||
@@ -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:20.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
|
||||
|
||||
@@ -16,24 +16,26 @@ pub async fn send_alert(
|
||||
"{level} | If you see this message, then Alerter **{name}** is **working**\n{link}"
|
||||
)
|
||||
}
|
||||
AlertData::SwarmUnhealthy { id, name, err } => {
|
||||
let link = resource_link(ResourceTargetVariant::Swarm, id);
|
||||
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} | Swarm **{name}** is now **healthy**\n{link}"
|
||||
"{level} | **{name}**{region} | Periphery version now matches Core version ✅\n{link}"
|
||||
)
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\n**error**: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | Swarm **{name}** is **unhealthy** ❌\n{link}{err}"
|
||||
"{level} | **{name}**{region} | Version mismatch detected ⚠️\nPeriphery: **{server_version}** | Core: **{core_version}**\n{link}"
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
@@ -62,28 +64,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,
|
||||
@@ -128,8 +108,6 @@ pub async fn send_alert(
|
||||
AlertData::ContainerStateChange {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
from,
|
||||
@@ -137,64 +115,37 @@ pub async fn send_alert(
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
let to = fmt_docker_container_state(to);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: **{swarm}**")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"📦 Deployment **{name}** is now **{to}**{target}\nprevious: **{from}**\n{link}"
|
||||
"📦 Deployment **{name}** is now **{to}**\nserver: **{server_name}**\nprevious: **{from}**\n{link}"
|
||||
)
|
||||
}
|
||||
AlertData::DeploymentImageUpdateAvailable {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
image,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: **{swarm}**")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment **{name}** has an update available{target}\nimage: **{image}**\n{link}"
|
||||
"⬆ Deployment **{name}** has an update available\nserver: **{server_name}**\nimage: **{image}**\n{link}"
|
||||
)
|
||||
}
|
||||
AlertData::DeploymentAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
image,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: **{swarm}**")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment **{name}** was updated automatically ⏫{target}\nimage: **{image}**\n{link}"
|
||||
"⬆ Deployment **{name}** was updated automatically ⏫\nserver: **{server_name}**\nimage: **{image}**\n{link}"
|
||||
)
|
||||
}
|
||||
AlertData::StackStateChange {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
from,
|
||||
@@ -202,44 +153,26 @@ pub async fn send_alert(
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Stack, id);
|
||||
let to = fmt_stack_state(to);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: **{swarm}**")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"🥞 Stack **{name}** is now {to}{target}\nprevious: **{from}**\n{link}"
|
||||
"🥞 Stack **{name}** is now {to}\nserver: **{server_name}**\nprevious: **{from}**\n{link}"
|
||||
)
|
||||
}
|
||||
AlertData::StackImageUpdateAvailable {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
service,
|
||||
image,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Stack, id);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: **{swarm}**")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack **{name}** has an update available{target}\nservice: **{service}**\nimage: **{image}**\n{link}"
|
||||
"⬆ Stack **{name}** has an update available\nserver: **{server_name}**\nservice: **{service}**\nimage: **{image}**\n{link}"
|
||||
)
|
||||
}
|
||||
AlertData::StackAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
images,
|
||||
@@ -248,15 +181,8 @@ pub async fn send_alert(
|
||||
let images_label =
|
||||
if images.len() > 1 { "images" } else { "image" };
|
||||
let images = images.join(", ");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: **{swarm}**")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: **{server}**")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack **{name}** was updated automatically ⏫{target}\n{images_label}: **{images}**\n{link}"
|
||||
"⬆ Stack **{name}** was updated automatically ⏫\nserver: **{server_name}**\n{images_label}: **{images}**\n{link}"
|
||||
)
|
||||
}
|
||||
AlertData::AwsBuilderTerminationFailed {
|
||||
@@ -338,7 +264,7 @@ pub async fn send_alert(
|
||||
let sanitized_error =
|
||||
svi::replace_in_string(&format!("{e:?}"), &replacers);
|
||||
anyhow::Error::msg(format!(
|
||||
"Error with request to Discord: {sanitized_error}"
|
||||
"Error with slack request: {sanitized_error}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use database::mungos::{find::find_collect, mongodb::bson::doc};
|
||||
use derive_variants::ExtractVariant;
|
||||
use futures_util::future::join_all;
|
||||
use interpolate::Interpolator;
|
||||
use komodo_client::entities::{
|
||||
@@ -80,14 +81,14 @@ pub async fn send_alert_to_alerter(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let alert_variant: AlertDataVariant = (&alert.data).into();
|
||||
let alert_type = alert.data.extract_variant();
|
||||
|
||||
// In the test case, we don't want the filters inside this
|
||||
// block to stop the test from being sent to the alerting endpoint.
|
||||
if alert_variant != AlertDataVariant::Test {
|
||||
if alert_type != AlertDataVariant::Test {
|
||||
// Don't send if alert type not configured on the alerter
|
||||
if !alerter.config.alert_types.is_empty()
|
||||
&& !alerter.config.alert_types.contains(&alert_variant)
|
||||
&& !alerter.config.alert_types.contains(&alert_type)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
@@ -251,22 +252,26 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
"{level} | If you see this message, then Alerter {name} is working\n{link}",
|
||||
)
|
||||
}
|
||||
AlertData::SwarmUnhealthy { id, name, err } => {
|
||||
let link = resource_link(ResourceTargetVariant::Swarm, id);
|
||||
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} | Swarm {name} is now healthy\n{link}")
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
format!(
|
||||
"{level} | Swarm {name} is unhealthy ❌\n{link}{err}"
|
||||
"{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}"
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
@@ -293,28 +298,6 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
_ => 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,
|
||||
@@ -359,8 +342,6 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
AlertData::ContainerStateChange {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
from,
|
||||
@@ -368,64 +349,37 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
let to_state = fmt_docker_container_state(to);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: {swarm}")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"📦Deployment {name} is now {to_state}{target}\nprevious: {from}\n{link}",
|
||||
"📦Deployment {name} is now {to_state}\nserver: {server_name}\nprevious: {from}\n{link}",
|
||||
)
|
||||
}
|
||||
AlertData::DeploymentImageUpdateAvailable {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
image,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: {swarm}")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment {name} has an update available{target}\nimage: {image}\n{link}",
|
||||
"⬆ Deployment {name} has an update available\nserver: {server_name}\nimage: {image}\n{link}",
|
||||
)
|
||||
}
|
||||
AlertData::DeploymentAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
image,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Deployment, id);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: {swarm}")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Deployment {name} was updated automatically{target}\nimage: {image}\n{link}",
|
||||
"⬆ Deployment {name} was updated automatically\nserver: {server_name}\nimage: {image}\n{link}",
|
||||
)
|
||||
}
|
||||
AlertData::StackStateChange {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
from,
|
||||
@@ -433,44 +387,26 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Stack, id);
|
||||
let to_state = fmt_stack_state(to);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: {swarm}")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"🥞 Stack {name} is now {to_state}{target}\nprevious: {from}\n{link}",
|
||||
"🥞 Stack {name} is now {to_state}\nserver: {server_name}\nprevious: {from}\n{link}",
|
||||
)
|
||||
}
|
||||
AlertData::StackImageUpdateAvailable {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
service,
|
||||
image,
|
||||
} => {
|
||||
let link = resource_link(ResourceTargetVariant::Stack, id);
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: {swarm}")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack {name} has an update available{target}\nservice: {service}\nimage: {image}\n{link}",
|
||||
"⬆ Stack {name} has an update available\nserver: {server_name}\nservice: {service}\nimage: {image}\n{link}",
|
||||
)
|
||||
}
|
||||
AlertData::StackAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
images,
|
||||
@@ -479,15 +415,8 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
let images_label =
|
||||
if images.len() > 1 { "images" } else { "image" };
|
||||
let images_str = images.join(", ");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("\nswarm: {swarm}")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("\nserver: {server}")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
format!(
|
||||
"⬆ Stack {name} was updated automatically ⏫{target}\n{images_label}: {images_str}\n{link}",
|
||||
"⬆ Stack {name} was updated automatically ⏫\nserver: {server_name}\n{images_label}: {images_str}\n{link}",
|
||||
)
|
||||
}
|
||||
AlertData::AwsBuilderTerminationFailed {
|
||||
|
||||
@@ -31,7 +31,7 @@ pub async fn send_alert(
|
||||
let sanitized_error =
|
||||
svi::replace_in_string(&format!("{e:?}"), &replacers);
|
||||
anyhow::Error::msg(format!(
|
||||
"Error with request to Ntfy: {sanitized_error}"
|
||||
"Error with slack request: {sanitized_error}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub async fn send_alert(
|
||||
let sanitized_error =
|
||||
svi::replace_in_string(&format!("{e:?}"), &replacers);
|
||||
anyhow::Error::msg(format!(
|
||||
"Error with request to Pushover: {sanitized_error}"
|
||||
"Error with slack request: {sanitized_error}"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,82 +24,6 @@ pub async fn send_alert(
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::SwarmUnhealthy { id, name, err } => {
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
let text =
|
||||
format!("{level} | Swarm *{name}* is now *healthy*");
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
Block::section(format!(
|
||||
"Swarm *{name}* is now *healthy*"
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let text =
|
||||
format!("{level} | Swarm *{name}* is *unhealthy* ❌");
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
Block::section(format!(
|
||||
"Swarm *{name}* is *unhealthy* ❌{err}"
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Server,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
err,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
let text =
|
||||
format!("{level} | *{name}*{region} is now *connected*");
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
Block::section(format!(
|
||||
"*{name}*{region} is now *connected*"
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let text =
|
||||
format!("{level} | *{name}*{region} is *unreachable* ❌");
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
Block::section(format!(
|
||||
"*{name}*{region} is *unreachable* ❌{err}"
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Server,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
@@ -129,6 +53,47 @@ pub async fn send_alert(
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
err,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
let text =
|
||||
format!("{level} | *{name}*{region} is now *connected*");
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
Block::section(format!(
|
||||
"*{name}*{region} is now *connnected*"
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let text =
|
||||
format!("{level} | *{name}*{region} is *unreachable* ❌");
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
Block::section(format!(
|
||||
"*{name}*{region} is *unreachable* ❌{err}"
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Server,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerCpu {
|
||||
id,
|
||||
name,
|
||||
@@ -274,26 +239,19 @@ pub async fn send_alert(
|
||||
}
|
||||
AlertData::ContainerStateChange {
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
from,
|
||||
to,
|
||||
id,
|
||||
..
|
||||
} => {
|
||||
let to = fmt_docker_container_state(to);
|
||||
let text = format!("📦 Container *{name}* is now *{to}*");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("swarm: *{swarm}*\n")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(format!("{target}previous: {from}",)),
|
||||
Block::section(format!(
|
||||
"server: {server_name}\nprevious: {from}",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Deployment,
|
||||
id,
|
||||
@@ -304,24 +262,17 @@ pub async fn send_alert(
|
||||
AlertData::DeploymentImageUpdateAvailable {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
server_id: _server_id,
|
||||
image,
|
||||
} => {
|
||||
let text =
|
||||
format!("⬆ Deployment *{name}* has an update available");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("swarm: *{swarm}*\n")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(format!("{target}image: *{image}*",)),
|
||||
Block::section(format!(
|
||||
"server: *{server_name}*\nimage: *{image}*",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Deployment,
|
||||
id,
|
||||
@@ -332,24 +283,17 @@ pub async fn send_alert(
|
||||
AlertData::DeploymentAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
server_id: _server_id,
|
||||
image,
|
||||
} => {
|
||||
let text =
|
||||
format!("⬆ Deployment *{name}* was updated automatically ⏫");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("swarm: *{swarm}*\n")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(format!("{target}image: *{image}*",)),
|
||||
Block::section(format!(
|
||||
"server: *{server_name}*\nimage: *{image}*",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Deployment,
|
||||
id,
|
||||
@@ -359,26 +303,19 @@ pub async fn send_alert(
|
||||
}
|
||||
AlertData::StackStateChange {
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
from,
|
||||
to,
|
||||
id,
|
||||
..
|
||||
} => {
|
||||
let to = fmt_stack_state(to);
|
||||
let text = format!("🥞 Stack *{name}* is now *{to}*");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("swarm: *{swarm}*\n")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(format!("{target}previous: *{from}*",)),
|
||||
Block::section(format!(
|
||||
"server: *{server_name}*\nprevious: *{from}*",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Stack,
|
||||
id,
|
||||
@@ -389,25 +326,16 @@ pub async fn send_alert(
|
||||
AlertData::StackImageUpdateAvailable {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
server_id: _server_id,
|
||||
service,
|
||||
image,
|
||||
} => {
|
||||
let text = format!("⬆ Stack *{name}* has an update available");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("swarm: *{swarm}*\n")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(format!(
|
||||
"{target}service: *{service}*\nimage: *{image}*",
|
||||
"server: *{server_name}*\nservice: *{service}*\nimage: *{image}*",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Stack,
|
||||
@@ -419,10 +347,8 @@ pub async fn send_alert(
|
||||
AlertData::StackAutoUpdated {
|
||||
id,
|
||||
name,
|
||||
swarm_id: _swarm_id,
|
||||
swarm_name,
|
||||
server_id: _server_id,
|
||||
server_name,
|
||||
server_id: _server_id,
|
||||
images,
|
||||
} => {
|
||||
let text =
|
||||
@@ -430,18 +356,11 @@ pub async fn send_alert(
|
||||
let images_label =
|
||||
if images.len() > 1 { "images" } else { "image" };
|
||||
let images = images.join(", ");
|
||||
let target = if let Some(swarm) = swarm_name {
|
||||
format!("swarm: *{swarm}*\n")
|
||||
} else if let Some(server) = server_name {
|
||||
format!("server: *{server}*\n")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(
|
||||
format!("{target}{images_label}: *{images}*",),
|
||||
),
|
||||
Block::section(format!(
|
||||
"server: *{server_name}*\n{images_label}: *{images}*",
|
||||
)),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Stack,
|
||||
id,
|
||||
@@ -572,7 +491,7 @@ pub async fn send_alert(
|
||||
let sanitized_error =
|
||||
svi::replace_in_string(&format!("{e:?}"), &replacers);
|
||||
anyhow::Error::msg(format!(
|
||||
"Error with request to Slack: {sanitized_error}"
|
||||
"Error with slack request: {sanitized_error}"
|
||||
))
|
||||
})?;
|
||||
Ok(())
|
||||
|
||||
353
bin/core/src/api/auth.rs
Normal file
353
bin/core/src/api/auth.rs
Normal file
@@ -0,0 +1,353 @@
|
||||
use std::{
|
||||
net::{IpAddr, SocketAddr},
|
||||
sync::OnceLock,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use axum::{
|
||||
Router,
|
||||
extract::{ConnectInfo, Path},
|
||||
http::HeaderMap,
|
||||
routing::post,
|
||||
};
|
||||
use data_encoding::BASE32_NOPAD;
|
||||
use database::{
|
||||
bson::{doc, to_bson},
|
||||
mungos::by_id::update_one_by_id,
|
||||
};
|
||||
use derive_variants::{EnumVariants, ExtractVariant};
|
||||
use komodo_client::{api::auth::*, entities::user::User};
|
||||
use rate_limit::WithFailureRateLimit;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use response::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use serror::{AddStatusCode, AddStatusCodeError, Json};
|
||||
use tower_sessions::Session;
|
||||
use typeshare::typeshare;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
auth::{
|
||||
SessionPasskeyLogin, SessionTotpLogin, SessionUserId,
|
||||
get_user_id_from_headers,
|
||||
github::{self, client::github_oauth_client},
|
||||
google::{self, client::google_oauth_client},
|
||||
oidc::{self, client::oidc_client},
|
||||
totp::make_totp,
|
||||
},
|
||||
config::core_config,
|
||||
helpers::query::get_user,
|
||||
state::{auth_rate_limiter, db_client, jwt_client, webauthn},
|
||||
};
|
||||
|
||||
use super::Variant;
|
||||
|
||||
pub struct AuthArgs {
|
||||
pub headers: HeaderMap,
|
||||
/// Prefer extracting IP from headers.
|
||||
/// This IP will be the IP of reverse proxy itself.
|
||||
pub ip: IpAddr,
|
||||
/// Per-client session state
|
||||
pub session: Option<Session>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants,
|
||||
)]
|
||||
#[args(AuthArgs)]
|
||||
#[response(Response)]
|
||||
#[error(serror::Error)]
|
||||
#[variant_derive(Debug)]
|
||||
#[serde(tag = "type", content = "params")]
|
||||
#[allow(clippy::enum_variant_names, clippy::large_enum_variant)]
|
||||
pub enum AuthRequest {
|
||||
GetLoginOptions(GetLoginOptions),
|
||||
SignUpLocalUser(SignUpLocalUser),
|
||||
LoginLocalUser(LoginLocalUser),
|
||||
ExchangeForJwt(ExchangeForJwt),
|
||||
CompleteTotpLogin(CompleteTotpLogin),
|
||||
CompletePasskeyLogin(CompletePasskeyLogin),
|
||||
GetUser(GetUser),
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
let mut router = Router::new()
|
||||
.route("/", post(handler))
|
||||
.route("/{variant}", post(variant_handler));
|
||||
|
||||
if core_config().local_auth {
|
||||
info!("🔑 Local Login Enabled");
|
||||
}
|
||||
|
||||
if github_oauth_client().is_some() {
|
||||
info!("🔑 Github Login Enabled");
|
||||
router = router.nest("/github", github::router())
|
||||
}
|
||||
|
||||
if google_oauth_client().is_some() {
|
||||
info!("🔑 Google Login Enabled");
|
||||
router = router.nest("/google", google::router())
|
||||
}
|
||||
|
||||
if core_config().oidc_enabled {
|
||||
info!("🔑 OIDC Login Enabled");
|
||||
router = router.nest("/oidc", oidc::router())
|
||||
}
|
||||
|
||||
router
|
||||
}
|
||||
|
||||
async fn variant_handler(
|
||||
headers: HeaderMap,
|
||||
session: Session,
|
||||
info: ConnectInfo<SocketAddr>,
|
||||
Path(Variant { variant }): Path<Variant>,
|
||||
Json(params): Json<serde_json::Value>,
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let req: AuthRequest = serde_json::from_value(json!({
|
||||
"type": variant,
|
||||
"params": params,
|
||||
}))?;
|
||||
handler(headers, session, info, Json(req)).await
|
||||
}
|
||||
|
||||
async fn handler(
|
||||
headers: HeaderMap,
|
||||
session: Session,
|
||||
ConnectInfo(info): ConnectInfo<SocketAddr>,
|
||||
Json(request): Json<AuthRequest>,
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let timer = Instant::now();
|
||||
let req_id = Uuid::new_v4();
|
||||
debug!(
|
||||
"/auth request {req_id} | METHOD: {:?}",
|
||||
request.extract_variant()
|
||||
);
|
||||
let res = request
|
||||
.resolve(&AuthArgs {
|
||||
headers,
|
||||
ip: info.ip(),
|
||||
session: Some(session),
|
||||
})
|
||||
.await;
|
||||
if let Err(e) = &res {
|
||||
debug!("/auth request {req_id} | error: {:#}", e.error);
|
||||
}
|
||||
let elapsed = timer.elapsed();
|
||||
debug!("/auth request {req_id} | resolve time: {elapsed:?}");
|
||||
res.map(|res| res.0)
|
||||
}
|
||||
|
||||
fn login_options_reponse() -> &'static GetLoginOptionsResponse {
|
||||
static GET_LOGIN_OPTIONS_RESPONSE: OnceLock<
|
||||
GetLoginOptionsResponse,
|
||||
> = OnceLock::new();
|
||||
GET_LOGIN_OPTIONS_RESPONSE.get_or_init(|| {
|
||||
let config = core_config();
|
||||
GetLoginOptionsResponse {
|
||||
local: config.local_auth,
|
||||
github: github_oauth_client().is_some(),
|
||||
google: google_oauth_client().is_some(),
|
||||
oidc: oidc_client().load().is_some(),
|
||||
registration_disabled: config.disable_user_registration,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl Resolve<AuthArgs> for GetLoginOptions {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &AuthArgs,
|
||||
) -> serror::Result<GetLoginOptionsResponse> {
|
||||
Ok(*login_options_reponse())
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<AuthArgs> for ExchangeForJwt {
|
||||
async fn resolve(
|
||||
self,
|
||||
AuthArgs {
|
||||
headers,
|
||||
ip,
|
||||
session,
|
||||
}: &AuthArgs,
|
||||
) -> serror::Result<ExchangeForJwtResponse> {
|
||||
async {
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
let SessionUserId(user_id) = session
|
||||
.remove(SessionUserId::KEY)
|
||||
.await
|
||||
.context("Internal session type error")?
|
||||
.context("Authentication steps must be completed before JWT can be retrieved")?;
|
||||
|
||||
jwt_client().encode(user_id).map_err(Into::into)
|
||||
}
|
||||
.with_failure_rate_limit_using_headers(
|
||||
auth_rate_limiter(),
|
||||
headers,
|
||||
Some(*ip),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<AuthArgs> for CompleteTotpLogin {
|
||||
async fn resolve(
|
||||
self,
|
||||
AuthArgs {
|
||||
headers,
|
||||
ip,
|
||||
session,
|
||||
}: &AuthArgs,
|
||||
) -> serror::Result<CompleteTotpLoginResponse> {
|
||||
async {
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
let SessionTotpLogin { user_id } = session
|
||||
.get(SessionTotpLogin::KEY)
|
||||
.await
|
||||
.context("Internal session type error")?
|
||||
.context(
|
||||
"Totp login has not been initiated for this session",
|
||||
)?;
|
||||
|
||||
let user = get_user(&user_id)
|
||||
.await
|
||||
.status_code(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
if user.totp.secret.is_empty() {
|
||||
return Err(
|
||||
anyhow!("User is not enrolled in totp")
|
||||
.status_code(StatusCode::BAD_REQUEST),
|
||||
);
|
||||
}
|
||||
|
||||
let secret_bytes = BASE32_NOPAD
|
||||
.decode(user.totp.secret.as_bytes())
|
||||
.context("Failed to decode totp secret to bytes")?;
|
||||
|
||||
let totp = make_totp(secret_bytes, None)?;
|
||||
|
||||
let valid = totp
|
||||
.check_current(&self.code)
|
||||
.context("Failed to check TOTP code validity")?;
|
||||
|
||||
if !valid {
|
||||
return Err(
|
||||
anyhow!("Invalid totp code")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
}
|
||||
|
||||
jwt_client().encode(user_id).map_err(Into::into)
|
||||
}
|
||||
.with_failure_rate_limit_using_headers(
|
||||
auth_rate_limiter(),
|
||||
headers,
|
||||
Some(*ip),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<AuthArgs> for CompletePasskeyLogin {
|
||||
async fn resolve(
|
||||
self,
|
||||
AuthArgs {
|
||||
headers,
|
||||
ip,
|
||||
session,
|
||||
}: &AuthArgs,
|
||||
) -> serror::Result<CompletePasskeyLoginResponse> {
|
||||
async {
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
let webauthn = webauthn().context(
|
||||
"No webauthn provider available, invalid KOMODO_HOST config",
|
||||
)?;
|
||||
|
||||
let SessionPasskeyLogin { user_id, state } = session
|
||||
.get(SessionPasskeyLogin::KEY)
|
||||
.await
|
||||
.context("Internal session type error")?
|
||||
.context(
|
||||
"Passkey login has not been initiated for this session",
|
||||
)?;
|
||||
|
||||
// The result of this call must be used to
|
||||
// update the stored passkey info on database.
|
||||
let update = webauthn
|
||||
.finish_passkey_authentication(&self.credential, &state)
|
||||
.context("Failed to validate passkey")?;
|
||||
|
||||
let mut passkey = get_user(&user_id)
|
||||
.await?
|
||||
.passkey
|
||||
.passkey
|
||||
.context("Could not find passkey on database.")?;
|
||||
|
||||
passkey.update_credential(&update);
|
||||
|
||||
let passkey = to_bson(&passkey)
|
||||
.context("Failed to serialize passkey to BSON")?;
|
||||
|
||||
let update = doc! { "$set": { "passkey.passkey": passkey } };
|
||||
|
||||
let _ =
|
||||
update_one_by_id(&db_client().users, &user_id, update, None)
|
||||
.await
|
||||
.context(
|
||||
"Failed to update user passkey on database after login",
|
||||
)
|
||||
.inspect_err(|e| warn!("{e:#}"));
|
||||
|
||||
jwt_client().encode(user_id).map_err(Into::into)
|
||||
}
|
||||
.with_failure_rate_limit_using_headers(
|
||||
auth_rate_limiter(),
|
||||
headers,
|
||||
Some(*ip),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<AuthArgs> for GetUser {
|
||||
async fn resolve(
|
||||
self,
|
||||
AuthArgs {
|
||||
headers,
|
||||
ip,
|
||||
session: _,
|
||||
}: &AuthArgs,
|
||||
) -> serror::Result<User> {
|
||||
async {
|
||||
let user_id = get_user_id_from_headers(headers)
|
||||
.await
|
||||
.status_code(StatusCode::UNAUTHORIZED)?;
|
||||
let mut user = get_user(&user_id)
|
||||
.await
|
||||
.status_code(StatusCode::UNAUTHORIZED)?;
|
||||
// Sanitize before sending to client.
|
||||
user.sanitize();
|
||||
Ok(user)
|
||||
}
|
||||
.with_failure_rate_limit_using_headers(
|
||||
auth_rate_limiter(),
|
||||
headers,
|
||||
Some(*ip),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -4,45 +4,37 @@ use std::{
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use command::{CommandOptions, run_komodo_standard_command};
|
||||
use database::{
|
||||
bson::doc,
|
||||
mungos::{by_id::update_one_by_id, mongodb::bson::to_document},
|
||||
use anyhow::Context;
|
||||
use command::run_komodo_standard_command;
|
||||
use config::merge_objects;
|
||||
use database::mungos::{
|
||||
by_id::update_one_by_id, mongodb::bson::to_document,
|
||||
};
|
||||
use interpolate::Interpolator;
|
||||
use komodo_client::{
|
||||
api::execute::{
|
||||
BatchExecutionResponse, BatchRunAction, CancelAction, RunAction,
|
||||
api::{
|
||||
execute::{BatchExecutionResponse, BatchRunAction, RunAction},
|
||||
user::{CreateApiKey, CreateApiKeyResponse, DeleteApiKey},
|
||||
},
|
||||
entities::{
|
||||
FileFormat, JsonObject, Operation,
|
||||
FileFormat, JsonObject,
|
||||
action::Action,
|
||||
alert::{Alert, AlertData, SeverityLevel},
|
||||
config::core::CoreConfig,
|
||||
komodo_timestamp,
|
||||
permission::PermissionLevel,
|
||||
random_string,
|
||||
update::{Update, UpdateStatus},
|
||||
update::Update,
|
||||
user::action_user,
|
||||
},
|
||||
parsers::parse_key_value_list,
|
||||
};
|
||||
use mogh_auth_client::api::manage::{
|
||||
CreateApiKey, CreateApiKeyResponse,
|
||||
};
|
||||
use mogh_auth_server::api::manage::api_key::{
|
||||
create_api_key, delete_api_key,
|
||||
};
|
||||
use mogh_config::merge_objects;
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
use tokio::fs;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
api::execute::ExecuteRequest,
|
||||
auth::KomodoAuthImpl,
|
||||
api::{execute::ExecuteRequest, user::UserArgs},
|
||||
config::core_config,
|
||||
helpers::{
|
||||
query::{VariablesAndSecrets, get_variables_and_secrets},
|
||||
@@ -50,7 +42,7 @@ use crate::{
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource::refresh_action_state_cache,
|
||||
state::{action_cancel_cache, action_states, db_client},
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
|
||||
use super::ExecuteArgs;
|
||||
@@ -70,15 +62,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchRunAction>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -91,7 +83,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,
|
||||
@@ -99,12 +91,8 @@ impl Resolve<ExecuteArgs> for RunAction {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut action = get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
user,
|
||||
@@ -147,93 +135,74 @@ impl Resolve<ExecuteArgs> for RunAction {
|
||||
let args = serde_json::to_string(&args)
|
||||
.context("Failed to serialize action run arguments")?;
|
||||
|
||||
let CreateApiKeyResponse { key, secret } = create_api_key(
|
||||
&KomodoAuthImpl,
|
||||
action_user().id.clone(),
|
||||
CreateApiKey {
|
||||
name: update.id.clone(),
|
||||
expires: 0,
|
||||
},
|
||||
)
|
||||
let CreateApiKeyResponse { key, secret } = CreateApiKey {
|
||||
name: update.id.clone(),
|
||||
expires: 0,
|
||||
}
|
||||
.resolve(&UserArgs {
|
||||
user: action_user().to_owned(),
|
||||
session: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
// Do next steps in seperate error handling block,
|
||||
// and delete the API key before unwrapping the error.
|
||||
// If Komodo shuts down during these steps, there will
|
||||
// be a dangling api key in the DB with user_id: "000000000000000000000002".
|
||||
// These need to be
|
||||
let res = async {
|
||||
let contents = &mut action.config.file_contents;
|
||||
let contents = &mut action.config.file_contents;
|
||||
|
||||
// Wrap the file contents in the execution context.
|
||||
*contents = full_contents(contents, &args, &key, &secret);
|
||||
// Wrap the file contents in the execution context.
|
||||
*contents = full_contents(contents, &args, &key, &secret);
|
||||
|
||||
let replacers = interpolate(
|
||||
contents,
|
||||
&mut update,
|
||||
key.clone(),
|
||||
secret.clone(),
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
let replacers =
|
||||
interpolate(contents, &mut update, key.clone(), secret.clone())
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let file = format!("{}.ts", random_string(10));
|
||||
let path = core_config().action_directory.join(&file);
|
||||
let file = format!("{}.ts", random_string(10));
|
||||
let path = core_config().action_directory.join(&file);
|
||||
|
||||
mogh_secret_file::write_async(&path, contents)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Failed to write action file to {path:?}")
|
||||
})?;
|
||||
secret_file::write_async(&path, contents)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Failed to write action file to {path:?}")
|
||||
})?;
|
||||
|
||||
let CoreConfig { ssl_enabled, .. } = core_config();
|
||||
let CoreConfig { ssl_enabled, .. } = core_config();
|
||||
|
||||
let https_cert_flag = if *ssl_enabled {
|
||||
" --unsafely-ignore-certificate-errors=localhost"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let https_cert_flag = if *ssl_enabled {
|
||||
" --unsafely-ignore-certificate-errors=localhost"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let reload = if action.config.reload_deno_deps {
|
||||
" --reload"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let reload = if action.config.reload_deno_deps {
|
||||
" --reload"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let cancel = CancellationToken::new();
|
||||
|
||||
action_cancel_cache()
|
||||
.insert(update.id.clone(), cancel.clone())
|
||||
.await;
|
||||
|
||||
let mut res = run_komodo_standard_command(
|
||||
// Keep this stage name as is, the UI will find the latest update log by matching the stage name
|
||||
"Execute Action",
|
||||
format!(
|
||||
"deno run --allow-all{https_cert_flag}{reload} {}",
|
||||
path.display()
|
||||
),
|
||||
CommandOptions::default().cancel(cancel),
|
||||
)
|
||||
.await;
|
||||
|
||||
res.stdout = svi::replace_in_string(&res.stdout, &replacers)
|
||||
.replace(&key, "<ACTION_API_KEY>");
|
||||
res.stderr = svi::replace_in_string(&res.stderr, &replacers)
|
||||
.replace(&secret, "<ACTION_API_SECRET>");
|
||||
|
||||
cleanup_run(file + ".js", &path).await;
|
||||
|
||||
update.logs.push(res);
|
||||
update.finalize();
|
||||
|
||||
mogh_error::Ok(())
|
||||
}
|
||||
let mut res = run_komodo_standard_command(
|
||||
// Keep this stage name as is, the UI will find the latest update log by matching the stage name
|
||||
"Execute Action",
|
||||
None,
|
||||
format!(
|
||||
"deno run --allow-all{https_cert_flag}{reload} {}",
|
||||
path.display()
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
if let Err(e) =
|
||||
delete_api_key(&KomodoAuthImpl, &action_user().id, key).await
|
||||
res.stdout = svi::replace_in_string(&res.stdout, &replacers)
|
||||
.replace(&key, "<ACTION_API_KEY>");
|
||||
res.stderr = svi::replace_in_string(&res.stderr, &replacers)
|
||||
.replace(&secret, "<ACTION_API_SECRET>");
|
||||
|
||||
cleanup_run(file + ".js", &path).await;
|
||||
|
||||
if let Err(e) = (DeleteApiKey { key })
|
||||
.resolve(&UserArgs {
|
||||
user: action_user().to_owned(),
|
||||
session: None,
|
||||
})
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Failed to delete API key after action execution | {:#}",
|
||||
@@ -241,9 +210,8 @@ impl Resolve<ExecuteArgs> for RunAction {
|
||||
);
|
||||
};
|
||||
|
||||
action_cancel_cache().remove(&update.id).await;
|
||||
|
||||
res?;
|
||||
update.logs.push(res);
|
||||
update.finalize();
|
||||
|
||||
// Need to manually update the update before cache refresh,
|
||||
// and before broadcast with update_update.
|
||||
@@ -291,7 +259,7 @@ async fn interpolate(
|
||||
update: &mut Update,
|
||||
key: String,
|
||||
secret: String,
|
||||
) -> mogh_error::Result<HashSet<(String, String)>> {
|
||||
) -> serror::Result<HashSet<(String, String)>> {
|
||||
let VariablesAndSecrets {
|
||||
variables,
|
||||
mut secrets,
|
||||
@@ -471,65 +439,3 @@ fn parse_action_arguments(
|
||||
.context("Failed to parse Json to action args"),
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for CancelAction {
|
||||
#[instrument(
|
||||
"CancelAction",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
action = self.action,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let action = get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let update_id = if let Some(update_id) = self.update_id
|
||||
&& !update_id.is_empty()
|
||||
{
|
||||
update_id
|
||||
} else {
|
||||
db_client()
|
||||
.updates
|
||||
.find_one(doc! {
|
||||
"target.type": "Action",
|
||||
"target.id": &action.id,
|
||||
"operation": Operation::RunAction.as_ref(),
|
||||
"status": UpdateStatus::InProgress.as_ref(),
|
||||
})
|
||||
.await?
|
||||
.context("No active run to cancel found")?
|
||||
.id
|
||||
};
|
||||
|
||||
action_cancel_cache()
|
||||
.get(&update_id)
|
||||
.await
|
||||
.context("Action run cancel token not found")?
|
||||
.cancel();
|
||||
|
||||
let mut update = update.clone();
|
||||
update.push_simple_log(
|
||||
"Cancel Triggered",
|
||||
"The action cancel has been triggered.",
|
||||
);
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ use komodo_client::{
|
||||
permission::PermissionLevel,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
|
||||
use crate::{
|
||||
alert::send_alert_to_alerter, helpers::update::update_update,
|
||||
@@ -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,16 +102,10 @@ 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(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
|
||||
@@ -28,7 +28,7 @@ use komodo_client::{
|
||||
alert::{Alert, AlertData, SeverityLevel},
|
||||
all_logs_success,
|
||||
build::{Build, BuildConfig},
|
||||
builder::Builder,
|
||||
builder::{Builder, BuilderConfig},
|
||||
deployment::DeploymentState,
|
||||
komodo_timestamp, optional_string,
|
||||
permission::PermissionLevel,
|
||||
@@ -37,8 +37,8 @@ use komodo_client::{
|
||||
user::auto_redeploy_user,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api;
|
||||
use resolver_api::Resolve;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -75,15 +75,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchRunBuild>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -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,12 +104,8 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
@@ -175,6 +171,9 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
let builder =
|
||||
resource::get::<Builder>(&build.config.builder_id).await?;
|
||||
|
||||
let is_server_builder =
|
||||
matches!(&builder.config, BuilderConfig::Server(_));
|
||||
|
||||
tokio::spawn(async move {
|
||||
let poll = async {
|
||||
loop {
|
||||
@@ -183,13 +182,19 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
id = cancel_recv.recv() => id?
|
||||
};
|
||||
if incoming_build_id == build_id {
|
||||
update.push_simple_log("Cancel acknowledged", "The build cancellation has been queued, it may still take some time.");
|
||||
if is_server_builder {
|
||||
update.push_error_log("Cancel acknowledged", "Build cancellation is not possible on server builders at this time. Use an AWS builder to enable this feature.");
|
||||
} else {
|
||||
update.push_simple_log("Cancel acknowledged", "The build cancellation has been queued, it may still take some time.");
|
||||
}
|
||||
update.finalize();
|
||||
let id = update.id.clone();
|
||||
if let Err(e) = update_update(update).await {
|
||||
warn!("Failed to modify Update {id} on db | {e:#}");
|
||||
}
|
||||
cancel_clone.cancel();
|
||||
if !is_server_builder {
|
||||
cancel_clone.cancel();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -207,7 +212,7 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
build.name.clone(),
|
||||
Some(build.config.version),
|
||||
builder,
|
||||
Some(&mut update),
|
||||
&mut update,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -264,11 +269,11 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
replacers: Default::default(),
|
||||
}) => res,
|
||||
_ = cancel.cancelled() => {
|
||||
debug!("Build cancelled during repo clone, cleaning up builder");
|
||||
update.push_error_log("Build cancelled", String::from("Build cancelled during repo clone"));
|
||||
debug!("Build cancelled during clone, cleaning up builder");
|
||||
update.push_error_log("Build cancelled", String::from("user cancelled build during repo clone"));
|
||||
cleanup_builder_instance(periphery, cleanup_data, &mut update)
|
||||
.await;
|
||||
debug!("Builder cleaned up");
|
||||
info!("Builder cleaned up");
|
||||
return handle_early_return(update, build.id, build.name, true).await
|
||||
},
|
||||
};
|
||||
@@ -314,14 +319,7 @@ impl Resolve<ExecuteArgs> for RunBuild {
|
||||
}) => res.context("Failed at call to Periphery to build"),
|
||||
_ = cancel.cancelled() => {
|
||||
info!("Build cancelled during build, cleaning up builder");
|
||||
if let Err(e) = periphery.request(api::build::CancelBuild {
|
||||
id: build.id.clone()
|
||||
})
|
||||
.await
|
||||
.context("Failed to cancel build execution on Server") {
|
||||
update.push_error_log("Cancel Build", format_serror(&e.into()));
|
||||
}
|
||||
update.push_error_log("Build Cancelled", String::from("User cancelled build during image build step"));
|
||||
update.push_error_log("Build cancelled", String::from("User cancelled build during docker build"));
|
||||
cleanup_builder_instance(periphery, cleanup_data, &mut update)
|
||||
.await;
|
||||
return handle_early_return(update, build.id, build.name, true).await
|
||||
@@ -433,7 +431,7 @@ async fn handle_early_return(
|
||||
build_id: String,
|
||||
build_name: String,
|
||||
is_cancel: bool,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
update.finalize();
|
||||
// Need to manually update the update before cache refresh,
|
||||
// and before broadcast with add_update.
|
||||
@@ -507,10 +505,10 @@ pub async fn validate_cancel_build(
|
||||
)?;
|
||||
|
||||
match (latest_build, latest_cancel) {
|
||||
(Some(build), Some(cancel))
|
||||
if cancel.start_ts > build.start_ts =>
|
||||
{
|
||||
return Err(anyhow!("Build has already been cancelled"));
|
||||
(Some(build), Some(cancel)) => {
|
||||
if cancel.start_ts > build.start_ts {
|
||||
return Err(anyhow!("Build has already been cancelled"));
|
||||
}
|
||||
}
|
||||
(None, _) => return Err(anyhow!("No build in progress")),
|
||||
_ => {}
|
||||
@@ -524,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,
|
||||
@@ -532,12 +530,8 @@ impl Resolve<ExecuteArgs> for CancelBuild {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
@@ -559,8 +553,8 @@ impl Resolve<ExecuteArgs> for CancelBuild {
|
||||
let mut update = update.clone();
|
||||
|
||||
update.push_simple_log(
|
||||
"Cancel Triggered",
|
||||
"The build cancel has been triggered",
|
||||
"cancel triggered",
|
||||
"the build cancel has been triggered",
|
||||
);
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -632,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
|
||||
}
|
||||
@@ -666,7 +660,7 @@ async fn validate_account_extract_registry_tokens(
|
||||
..
|
||||
}: &Build,
|
||||
// Maps (domain, account) -> token
|
||||
) -> mogh_error::Result<Vec<(String, String, String)>> {
|
||||
) -> serror::Result<Vec<(String, String, String)>> {
|
||||
let mut res = HashMap::with_capacity(image_registry.capacity());
|
||||
|
||||
for (domain, account) in image_registry
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use cache::TimeoutCache;
|
||||
use formatting::format_serror;
|
||||
use interpolate::Interpolator;
|
||||
use komodo_client::{
|
||||
@@ -9,8 +10,7 @@ use komodo_client::{
|
||||
SwarmOrServer, Version,
|
||||
build::{Build, ImageRegistryConfig},
|
||||
deployment::{
|
||||
Deployment, DeploymentImage, DeploymentInfo,
|
||||
extract_registry_domain,
|
||||
Deployment, DeploymentImage, extract_registry_domain,
|
||||
},
|
||||
komodo_timestamp, optional_string,
|
||||
permission::PermissionLevel,
|
||||
@@ -18,11 +18,10 @@ use komodo_client::{
|
||||
update::{Log, Update},
|
||||
},
|
||||
};
|
||||
use mogh_cache::TimeoutCache;
|
||||
use mogh_error::AddStatusCodeError;
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
@@ -32,7 +31,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,15 +54,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDeploy>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -76,7 +75,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,12 +85,8 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (mut deployment, swarm_or_server) =
|
||||
setup_deployment_execution(
|
||||
&self.deployment,
|
||||
@@ -100,8 +95,6 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
// get the action state for the deployment (or insert default).
|
||||
let action_state = action_states()
|
||||
.deployment
|
||||
@@ -115,23 +108,33 @@ 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
|
||||
let (version, registry_token) = match &deployment.config.image {
|
||||
DeploymentImage::Build { build_id, version } => {
|
||||
let build = resource::get::<Build>(build_id).await?;
|
||||
let image_names = build.get_image_names();
|
||||
let image_name = image_names
|
||||
.first()
|
||||
.context("No image name could be created")
|
||||
.context("Failed to create image name")?;
|
||||
let version = if version.is_none() {
|
||||
build.config.version
|
||||
} else {
|
||||
*version
|
||||
};
|
||||
let image_name = build.get_deployment_image_name();
|
||||
let image_tag = build.get_deployment_image_tag(version);
|
||||
let version_str = version.to_string();
|
||||
// Potentially add the build image_tag postfix
|
||||
let version_str = if build.config.image_tag.is_empty() {
|
||||
version_str
|
||||
} else {
|
||||
format!("{version_str}-{}", build.config.image_tag)
|
||||
};
|
||||
// replace image with corresponding build image.
|
||||
deployment.config.image = DeploymentImage::Image {
|
||||
image: format!("{image_name}:{image_tag}"),
|
||||
image: format!("{image_name}:{version_str}"),
|
||||
};
|
||||
let first_registry = build
|
||||
.config
|
||||
@@ -200,10 +203,7 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
update.version = version;
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let deployment_id = deployment.id.clone();
|
||||
|
||||
match swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
match swarm_request(
|
||||
&swarm.config.server_ids,
|
||||
@@ -216,7 +216,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) => {
|
||||
@@ -240,7 +240,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) => {
|
||||
@@ -253,20 +253,6 @@ impl Resolve<ExecuteArgs> for Deploy {
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = resource::update_info::<Deployment>(
|
||||
&deployment_id,
|
||||
&DeploymentInfo {
|
||||
latest_image_digest: Default::default(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Failed to update deployment {} info after deploy | {e:#}",
|
||||
deployment_id
|
||||
);
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
@@ -395,7 +381,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;
|
||||
@@ -412,7 +398,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,
|
||||
@@ -420,12 +406,8 @@ impl Resolve<ExecuteArgs> for PullDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -452,7 +434,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?;
|
||||
@@ -470,7 +452,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,
|
||||
@@ -478,12 +460,8 @@ impl Resolve<ExecuteArgs> for StartDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -511,7 +489,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)
|
||||
@@ -529,7 +507,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?;
|
||||
|
||||
@@ -542,7 +520,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,
|
||||
@@ -550,12 +528,8 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -583,7 +557,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)
|
||||
@@ -603,7 +577,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?;
|
||||
|
||||
@@ -616,7 +590,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,
|
||||
@@ -624,12 +598,8 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -657,7 +627,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)
|
||||
@@ -675,7 +645,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?;
|
||||
|
||||
@@ -688,7 +658,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,
|
||||
@@ -696,12 +666,8 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -729,7 +695,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)
|
||||
@@ -749,7 +715,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?;
|
||||
|
||||
@@ -762,7 +728,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,
|
||||
@@ -772,12 +738,8 @@ impl Resolve<ExecuteArgs> for StopDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -805,7 +767,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)
|
||||
@@ -831,7 +793,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?;
|
||||
|
||||
@@ -855,15 +817,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDestroyDeployment>(
|
||||
&self.pattern,
|
||||
@@ -879,7 +841,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,
|
||||
@@ -889,12 +851,8 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -902,8 +860,6 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
// get the action state for the deployment (or insert default).
|
||||
let action_state = action_states()
|
||||
.deployment
|
||||
@@ -917,11 +873,10 @@ 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 {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
match swarm_request(
|
||||
&swarm.config.server_ids,
|
||||
@@ -932,7 +887,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(
|
||||
@@ -960,7 +915,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(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{fmt::Write as _, sync::OnceLock};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use command::{CommandOptions, run_komodo_standard_command};
|
||||
use command::run_komodo_standard_command;
|
||||
use database::{
|
||||
bson::{Document, doc},
|
||||
mungos::find::find_collect,
|
||||
@@ -14,32 +14,26 @@ use komodo_client::{
|
||||
RotateAllServerKeys, RotateCoreKeys,
|
||||
},
|
||||
entities::{
|
||||
SwarmOrServer, deployment::DeploymentState, server::ServerState,
|
||||
stack::StackState, swarm::SwarmState,
|
||||
deployment::DeploymentState, server::ServerState,
|
||||
stack::StackState,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
api::{
|
||||
execute::ExecuteArgs,
|
||||
write::{
|
||||
check_deployment_for_update_inner, check_stack_for_update_inner,
|
||||
},
|
||||
api::execute::{
|
||||
ExecuteArgs, pull_deployment_inner, pull_stack_inner,
|
||||
},
|
||||
config::{core_config, core_keys},
|
||||
helpers::{
|
||||
periphery_client, query::find_swarm_or_server,
|
||||
update::update_update,
|
||||
},
|
||||
helpers::{periphery_client, update::update_update},
|
||||
resource::rotate_server_keys,
|
||||
state::{
|
||||
db_client, deployment_status_cache, server_status_cache,
|
||||
stack_status_cache, swarm_status_cache,
|
||||
stack_status_cache,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -54,18 +48,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 +127,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(
|
||||
@@ -167,8 +153,8 @@ impl Resolve<ExecuteArgs> for BackupCoreDatabase {
|
||||
|
||||
let res = run_komodo_standard_command(
|
||||
"Backup Core Database",
|
||||
None,
|
||||
"km database backup --yes",
|
||||
CommandOptions::default(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -194,18 +180,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(
|
||||
@@ -227,9 +209,6 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
let servers = find_collect(&db_client().servers, None, None)
|
||||
.await
|
||||
.context("Failed to query for servers from database")?;
|
||||
let swarms = find_collect(&db_client().swarms, None, None)
|
||||
.await
|
||||
.context("Failed to query for swarms from database")?;
|
||||
|
||||
let query = doc! {
|
||||
"$or": [
|
||||
@@ -238,13 +217,13 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
]
|
||||
};
|
||||
|
||||
let stacks =
|
||||
find_collect(&db_client().stacks, query.clone(), None)
|
||||
.await
|
||||
.context("Failed to query for stacks from database")?;
|
||||
let (stacks, repos) = tokio::try_join!(
|
||||
find_collect(&db_client().stacks, query.clone(), None),
|
||||
find_collect(&db_client().repos, None, None)
|
||||
)
|
||||
.context("Failed to query for resources from database")?;
|
||||
|
||||
let server_status_cache = server_status_cache();
|
||||
let swarm_status_cache = swarm_status_cache();
|
||||
let stack_status_cache = stack_status_cache();
|
||||
|
||||
// Will be edited later at update.logs[0]
|
||||
@@ -255,71 +234,54 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only check running stacks.
|
||||
// Only pull running stacks.
|
||||
if !matches!(status.curr.state, StackState::Running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let swarm_or_server = find_swarm_or_server(
|
||||
&stack.config.swarm_id,
|
||||
&swarms,
|
||||
&stack.config.server_id,
|
||||
&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 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');
|
||||
let name = stack.name.clone();
|
||||
let repo = if stack.config.linked_repo.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let Some(repo) =
|
||||
repos.iter().find(|r| r.id == stack.config.linked_repo)
|
||||
else {
|
||||
update.push_error_log(
|
||||
&format!("Pull Stack {name}"),
|
||||
format!(
|
||||
"Did not find any Repo matching {}",
|
||||
stack.config.linked_repo
|
||||
),
|
||||
);
|
||||
continue;
|
||||
};
|
||||
Some(repo.clone())
|
||||
};
|
||||
if let Err(e) =
|
||||
pull_stack_inner(stack, Vec::new(), server, repo, None)
|
||||
.await
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Pull Stack {name}"),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
}
|
||||
update.logs[0]
|
||||
.stdout
|
||||
.push_str(&format!("Pulled Stack {} ✅", bold(name)));
|
||||
}
|
||||
|
||||
update.logs[0].stdout.push_str(&format!(
|
||||
"Checked Stack {} ✅",
|
||||
bold(&stack.name)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,77 +290,43 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
find_collect(&db_client().deployments, query, None)
|
||||
.await
|
||||
.context("Failed to query for deployments from database")?;
|
||||
|
||||
for deployment in deployments {
|
||||
let Some(status) =
|
||||
deployment_status_cache.get(&deployment.id).await
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only check running deployments.
|
||||
// Only pull running deployments.
|
||||
if !matches!(status.curr.state, DeploymentState::Running) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let swarm_or_server = find_swarm_or_server(
|
||||
&deployment.config.swarm_id,
|
||||
&swarms,
|
||||
&deployment.config.server_id,
|
||||
&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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let name = deployment.name.clone();
|
||||
|
||||
if let Err(e) = check_deployment_for_update_inner(
|
||||
deployment,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
if let Some(server) =
|
||||
servers.iter().find(|s| s.id == deployment.config.server_id)
|
||||
// This check is probably redundant along with running check
|
||||
// but shouldn't hurt
|
||||
&& server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Check Deployment {name}"),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
let name = deployment.name.clone();
|
||||
if let Err(e) =
|
||||
pull_deployment_inner(deployment, server).await
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Pull Deployment {name}"),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
}
|
||||
update.logs[0].stdout.push_str(&format!(
|
||||
"Pulled Deployment {} ✅",
|
||||
bold(name)
|
||||
));
|
||||
}
|
||||
update.logs[0]
|
||||
.stdout
|
||||
.push_str(&format!("Checked Deployment {} ✅", bold(name)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,18 +350,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 +464,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 +472,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(
|
||||
@@ -603,10 +523,7 @@ impl Resolve<ExecuteArgs> for RotateCoreKeys {
|
||||
);
|
||||
}
|
||||
|
||||
let public_key = core_keys
|
||||
.rotate(mogh_pki::PkiKind::Mutual)
|
||||
.await?
|
||||
.into_inner();
|
||||
let public_key = core_keys.rotate().await?.into_inner();
|
||||
|
||||
info!("New Public Key: {public_key}");
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use axum::{
|
||||
};
|
||||
use axum_extra::{TypedHeader, headers::ContentType};
|
||||
use database::mungos::by_id::find_one_by_id;
|
||||
use derive_variants::{EnumVariants, ExtractVariant};
|
||||
use formatting::format_serror;
|
||||
use futures_util::future::join_all;
|
||||
use komodo_client::{
|
||||
@@ -17,18 +18,17 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mogh_auth_server::middleware::authenticate_request;
|
||||
use mogh_error::Json;
|
||||
use mogh_error::JsonString;
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
use response::JsonString;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use strum::{Display, EnumDiscriminants};
|
||||
use serror::Json;
|
||||
use strum::Display;
|
||||
use typeshare::typeshare;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
auth::KomodoAuthImpl,
|
||||
auth::auth_request,
|
||||
helpers::update::{init_execution_update, update_update},
|
||||
resource::{KomodoResource, list_full_for_user_using_pattern},
|
||||
state::db_client,
|
||||
@@ -48,22 +48,26 @@ mod sync;
|
||||
|
||||
use super::Variant;
|
||||
|
||||
pub use {
|
||||
deployment::pull_deployment_inner, stack::pull_stack_inner,
|
||||
};
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants,
|
||||
)]
|
||||
#[strum_discriminants(name(ExecuteRequestMethod), derive(Display))]
|
||||
#[variant_derive(Debug, Display)]
|
||||
#[args(ExecuteArgs)]
|
||||
#[response(JsonString)]
|
||||
#[error(mogh_error::Error)]
|
||||
#[error(serror::Error)]
|
||||
#[serde(tag = "type", content = "params")]
|
||||
pub enum ExecuteRequest {
|
||||
// ==== STACK ====
|
||||
@@ -111,12 +115,10 @@ pub enum ExecuteRequest {
|
||||
// ==== PROCEDURE ====
|
||||
RunProcedure(RunProcedure),
|
||||
BatchRunProcedure(BatchRunProcedure),
|
||||
CancelProcedure(CancelProcedure),
|
||||
|
||||
// ==== ACTION ====
|
||||
RunAction(RunAction),
|
||||
BatchRunAction(BatchRunAction),
|
||||
CancelAction(CancelAction),
|
||||
|
||||
// ==== SYNC ====
|
||||
RunSync(RunSync),
|
||||
@@ -150,7 +152,6 @@ pub enum ExecuteRequest {
|
||||
|
||||
// ==== SWARM ====
|
||||
RemoveSwarmNodes(RemoveSwarmNodes),
|
||||
UpdateSwarmNode(UpdateSwarmNode),
|
||||
RemoveSwarmStacks(RemoveSwarmStacks),
|
||||
RemoveSwarmServices(RemoveSwarmServices),
|
||||
CreateSwarmConfig(CreateSwarmConfig),
|
||||
@@ -172,16 +173,14 @@ pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", post(handler))
|
||||
.route("/{variant}", post(variant_handler))
|
||||
.layer(middleware::from_fn(
|
||||
authenticate_request::<KomodoAuthImpl, true>,
|
||||
))
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
async fn variant_handler(
|
||||
user: Extension<User>,
|
||||
Path(Variant { variant }): Path<Variant>,
|
||||
Json(params): Json<serde_json::Value>,
|
||||
) -> mogh_error::Result<(TypedHeader<ContentType>, String)> {
|
||||
) -> serror::Result<(TypedHeader<ContentType>, String)> {
|
||||
let req: ExecuteRequest = serde_json::from_value(json!({
|
||||
"type": variant,
|
||||
"params": params,
|
||||
@@ -192,7 +191,7 @@ async fn variant_handler(
|
||||
async fn handler(
|
||||
Extension(user): Extension<User>,
|
||||
Json(request): Json<ExecuteRequest>,
|
||||
) -> mogh_error::Result<(TypedHeader<ContentType>, String)> {
|
||||
) -> serror::Result<(TypedHeader<ContentType>, String)> {
|
||||
let res = match inner_handler(request, user).await? {
|
||||
ExecutionResult::Single(update) => serde_json::to_string(&update)
|
||||
.context("Failed to serialize Update")?,
|
||||
@@ -252,19 +251,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,
|
||||
@@ -278,8 +269,8 @@ pub fn inner_handler(
|
||||
let mut update =
|
||||
find_one_by_id(&db_client().updates, &update_id)
|
||||
.await
|
||||
.context("Failed to query to db")?
|
||||
.context("No Update exists with given id")?;
|
||||
.context("failed to query to db")?
|
||||
.context("no update exists with given id")?;
|
||||
update.logs.push(log);
|
||||
update.finalize();
|
||||
update_update(update).await
|
||||
@@ -288,10 +279,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:#}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -307,44 +295,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 = request.extract_variant();
|
||||
|
||||
info!(
|
||||
api = "Execute",
|
||||
task_id = id.to_string(),
|
||||
method = method.to_string(),
|
||||
user_id,
|
||||
username,
|
||||
"EXECUTE REQUEST",
|
||||
"/execute request {id} | {variant} | user: {}",
|
||||
user.username
|
||||
);
|
||||
|
||||
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} error: {e:#}");
|
||||
}
|
||||
|
||||
res
|
||||
@@ -363,8 +332,6 @@ async fn batch_execute<E: BatchExecute>(
|
||||
let resources = list_full_for_user_using_pattern::<E::Resource>(
|
||||
pattern,
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
&[],
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use database::mungos::{
|
||||
by_id::update_one_by_id, mongodb::bson::to_document,
|
||||
};
|
||||
use formatting::{Color, bold, colored, format_serror, muted};
|
||||
use komodo_client::{
|
||||
api::execute::{
|
||||
BatchExecutionResponse, BatchRunProcedure, CancelProcedure,
|
||||
RunProcedure,
|
||||
BatchExecutionResponse, BatchRunProcedure, RunProcedure,
|
||||
},
|
||||
entities::{
|
||||
alert::{Alert, AlertData, SeverityLevel},
|
||||
@@ -19,16 +17,15 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
helpers::{procedure::execute_procedure, update::update_update},
|
||||
permission::get_check_permissions,
|
||||
resource::refresh_procedure_state_cache,
|
||||
state::{action_states, db_client, procedure_cancel_cache},
|
||||
state::{action_states, db_client},
|
||||
};
|
||||
|
||||
use super::{ExecuteArgs, ExecuteRequest};
|
||||
@@ -49,7 +46,7 @@ impl Resolve<ExecuteArgs> for BatchRunProcedure {
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs { user, .. }: &ExecuteArgs,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchRunProcedure>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -62,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,
|
||||
@@ -70,12 +67,8 @@ impl Resolve<ExecuteArgs> for RunProcedure {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
Ok(
|
||||
resolve_inner(self.procedure, user.clone(), update.clone())
|
||||
.await?,
|
||||
@@ -125,17 +118,9 @@ fn resolve_inner(
|
||||
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let cancel = CancellationToken::new();
|
||||
|
||||
procedure_cancel_cache()
|
||||
.insert(procedure.id.clone(), cancel.clone())
|
||||
.await;
|
||||
|
||||
let update = Mutex::new(update);
|
||||
|
||||
let res = execute_procedure(&procedure, &update, cancel).await;
|
||||
|
||||
procedure_cancel_cache().remove(&procedure.id).await;
|
||||
let res = execute_procedure(&procedure, &update).await;
|
||||
|
||||
let mut update = update.into_inner();
|
||||
|
||||
@@ -151,7 +136,7 @@ fn resolve_inner(
|
||||
);
|
||||
}
|
||||
Err(e) => update
|
||||
.push_error_log("Execution error", format_serror(&e.into())),
|
||||
.push_error_log("execution error", format_serror(&e.into())),
|
||||
}
|
||||
|
||||
update.finalize();
|
||||
@@ -195,47 +180,3 @@ fn resolve_inner(
|
||||
Ok(update)
|
||||
})
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for CancelProcedure {
|
||||
#[instrument(
|
||||
"CancelProcedure",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
procedure = self.procedure,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let procedure = get_check_permissions::<Procedure>(
|
||||
&self.procedure,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
procedure_cancel_cache()
|
||||
.get(&procedure.id)
|
||||
.await
|
||||
.context("Procedure run cancel token not found")?
|
||||
.cancel();
|
||||
|
||||
let mut update = update.clone();
|
||||
update.push_simple_log(
|
||||
"Cancel Triggered",
|
||||
"Procedure cancel has been triggered. The procedure will exit after the currently running stage is complete.",
|
||||
);
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ use komodo_client::{
|
||||
update::{Log, Update},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api;
|
||||
use resolver_api::Resolve;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{
|
||||
@@ -55,15 +55,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchCloneRepo>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -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,12 +84,8 @@ impl Resolve<ExecuteArgs> for CloneRepo {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
@@ -190,15 +186,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchPullRepo>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -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,12 +215,8 @@ impl Resolve<ExecuteArgs> for PullRepo {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
@@ -324,7 +316,7 @@ impl Resolve<ExecuteArgs> for PullRepo {
|
||||
)]
|
||||
async fn handle_repo_update_return(
|
||||
update: Update,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
// Need to manually update the update before cache refresh,
|
||||
// and before broadcast with add_update.
|
||||
// The Err case of to_document should be unreachable,
|
||||
@@ -371,15 +363,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchBuildRepo>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -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,12 +392,8 @@ impl Resolve<ExecuteArgs> for BuildRepo {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let mut repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
@@ -490,7 +478,7 @@ impl Resolve<ExecuteArgs> for BuildRepo {
|
||||
repo.name.clone(),
|
||||
None,
|
||||
builder,
|
||||
Some(&mut update),
|
||||
&mut update,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -626,7 +614,7 @@ async fn handle_builder_early_return(
|
||||
repo_id: String,
|
||||
repo_name: String,
|
||||
is_cancel: bool,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
update.finalize();
|
||||
// Need to manually update the update before cache refresh,
|
||||
// and before broadcast with add_update.
|
||||
@@ -698,12 +686,13 @@ pub async fn validate_cancel_repo_build(
|
||||
)?;
|
||||
|
||||
match (latest_build, latest_cancel) {
|
||||
(Some(build), Some(cancel))
|
||||
if cancel.start_ts > build.start_ts =>
|
||||
{
|
||||
return Err(anyhow!("Repo build has already been cancelled"));
|
||||
(Some(build), Some(cancel)) => {
|
||||
if cancel.start_ts > build.start_ts {
|
||||
return Err(anyhow!(
|
||||
"Repo build has already been cancelled"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
(None, _) => return Err(anyhow!("No repo build in progress")),
|
||||
_ => {}
|
||||
};
|
||||
@@ -716,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,
|
||||
@@ -724,12 +713,8 @@ impl Resolve<ExecuteArgs> for CancelRepoBuild {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
|
||||
@@ -9,12 +9,12 @@ use komodo_client::{
|
||||
update::{Log, Update},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
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,12 +35,8 @@ impl Resolve<ExecuteArgs> for StartContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +99,8 @@ impl Resolve<ExecuteArgs> for RestartContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +165,8 @@ impl Resolve<ExecuteArgs> for PauseContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +229,8 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +297,8 @@ impl Resolve<ExecuteArgs> for StopContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +365,8 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let DestroyContainer {
|
||||
server,
|
||||
container,
|
||||
@@ -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,12 +438,8 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +498,8 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +560,8 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +620,8 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +682,8 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +742,8 @@ impl Resolve<ExecuteArgs> for PruneContainers {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +808,8 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +868,8 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +932,8 @@ impl Resolve<ExecuteArgs> for DeleteImage {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +989,8 @@ impl Resolve<ExecuteArgs> for PruneImages {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +1051,8 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +1111,8 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +1172,8 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +1233,8 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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,12 +1294,8 @@ impl Resolve<ExecuteArgs> for PruneSystem {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -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?;
|
||||
|
||||
@@ -20,12 +20,12 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError as _;
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api::{
|
||||
DeployStackResponse, compose::*, swarm::DeploySwarmStack,
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError as _;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
@@ -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,15 +69,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDeployStack>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -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,12 +100,8 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (mut stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
user,
|
||||
@@ -113,8 +109,6 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
let mut repo = if !stack.config.files_on_host
|
||||
&& !stack.config.linked_repo.is_empty()
|
||||
{
|
||||
@@ -191,7 +185,6 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
commit_hash,
|
||||
commit_message,
|
||||
} = match &swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
swarm_request(
|
||||
&swarm.config.server_ids,
|
||||
@@ -315,12 +308,11 @@ impl Resolve<ExecuteArgs> for DeployStack {
|
||||
|
||||
// Ensure cached stack state up to date by updating server cache
|
||||
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,15 +338,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchDeployStackIfChanged>(
|
||||
&self.pattern,
|
||||
@@ -370,7 +362,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,12 +371,8 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
@@ -441,7 +429,7 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.clone(),
|
||||
update,
|
||||
task_id: *task_id,
|
||||
id: *id,
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -563,7 +551,7 @@ async fn deploy_services(
|
||||
stack: String,
|
||||
services: Vec<String>,
|
||||
user: &User,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
// The existing update is initialized to DeployStack,
|
||||
// but also has not been created on database.
|
||||
// Setup a new update here.
|
||||
@@ -580,7 +568,7 @@ async fn deploy_services(
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.clone(),
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -597,7 +585,7 @@ async fn restart_services(
|
||||
stack: String,
|
||||
services: Vec<String>,
|
||||
user: &User,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
// The existing update is initialized to DeployStack,
|
||||
// but also has not been created on database.
|
||||
// Setup a new update here.
|
||||
@@ -611,7 +599,7 @@ async fn restart_services(
|
||||
.resolve(&ExecuteArgs {
|
||||
user: user.clone(),
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -774,15 +762,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
Ok(
|
||||
super::batch_execute::<BatchPullStack>(&self.pattern, user)
|
||||
.await?,
|
||||
@@ -886,7 +874,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 +884,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,12 +893,8 @@ impl Resolve<ExecuteArgs> for PullStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
user,
|
||||
@@ -971,7 +955,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,12 +964,8 @@ impl Resolve<ExecuteArgs> for StartStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
execute_compose::<StartStack>(
|
||||
&self.stack,
|
||||
self.services,
|
||||
@@ -1004,7 +984,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,12 +993,8 @@ impl Resolve<ExecuteArgs> for RestartStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
execute_compose::<RestartStack>(
|
||||
&self.stack,
|
||||
self.services,
|
||||
@@ -1039,7 +1015,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,12 +1024,8 @@ impl Resolve<ExecuteArgs> for PauseStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
execute_compose::<PauseStack>(
|
||||
&self.stack,
|
||||
self.services,
|
||||
@@ -1072,7 +1044,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,12 +1053,8 @@ impl Resolve<ExecuteArgs> for UnpauseStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
execute_compose::<UnpauseStack>(
|
||||
&self.stack,
|
||||
self.services,
|
||||
@@ -1105,7 +1073,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,12 +1082,8 @@ impl Resolve<ExecuteArgs> for StopStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
execute_compose::<StopStack>(
|
||||
&self.stack,
|
||||
self.services,
|
||||
@@ -1150,15 +1114,15 @@ 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,
|
||||
) -> mogh_error::Result<BatchExecutionResponse> {
|
||||
ExecuteArgs { user, id, .. }: &ExecuteArgs,
|
||||
) -> serror::Result<BatchExecutionResponse> {
|
||||
super::batch_execute::<BatchDestroyStack>(&self.pattern, user)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
@@ -1170,7 +1134,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,12 +1145,8 @@ impl Resolve<ExecuteArgs> for DestroyStack {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
user,
|
||||
@@ -1194,10 +1154,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
match swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
if !self.services.is_empty() {
|
||||
return Err(
|
||||
@@ -1218,7 +1175,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 +1198,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 +1226,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,12 +1236,8 @@ impl Resolve<ExecuteArgs> for RunStackService {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let (mut stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
user,
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
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;
|
||||
use resolver_api::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,13 +21,12 @@ use komodo_client::{
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
update::{Log, Update},
|
||||
user::sync_user,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
api::write::WriteArgs,
|
||||
@@ -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,12 +63,8 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
ExecuteArgs { user, update, id }: &ExecuteArgs,
|
||||
) -> serror::Result<Update> {
|
||||
let RunSync {
|
||||
sync,
|
||||
resource_type: match_resource_type,
|
||||
@@ -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,
|
||||
@@ -283,11 +395,9 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
// New resource types need to be added here manually.
|
||||
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()
|
||||
@@ -315,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(
|
||||
@@ -340,7 +446,10 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
)
|
||||
.await,
|
||||
);
|
||||
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
ResourceSync::execute_sync_updates(resource_sync_deltas).await,
|
||||
);
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Server::execute_sync_updates(server_deltas).await,
|
||||
@@ -354,43 +463,34 @@ impl Resolve<ExecuteArgs> for RunSync {
|
||||
Action::execute_sync_updates(action_deltas).await,
|
||||
);
|
||||
|
||||
// Depends on server
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Swarm::execute_sync_updates(swarm_deltas).await,
|
||||
);
|
||||
// Depends on server
|
||||
// Dependent on server
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Builder::execute_sync_updates(builder_deltas).await,
|
||||
);
|
||||
// Depends on server / builder
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Repo::execute_sync_updates(repo_deltas).await,
|
||||
);
|
||||
|
||||
// Depends on builder / repo
|
||||
// Dependant on builder
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Build::execute_sync_updates(build_deltas).await,
|
||||
);
|
||||
// Depends on server / repo
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Stack::execute_sync_updates(stack_deltas).await,
|
||||
);
|
||||
// Depends on repo
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
ResourceSync::execute_sync_updates(resource_sync_deltas).await,
|
||||
);
|
||||
// Depends on server / build
|
||||
|
||||
// Dependant on server / build
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Deployment::execute_sync_updates(deployment_deltas).await,
|
||||
);
|
||||
// Depends on everything
|
||||
// stack only depends on server, but maybe will depend on build later.
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Stack::execute_sync_updates(stack_deltas).await,
|
||||
);
|
||||
|
||||
// Dependant on everything
|
||||
maybe_extend(
|
||||
&mut update.logs,
|
||||
Procedure::execute_sync_updates(procedure_deltas).await,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use axum::http::HeaderMap;
|
||||
use hex::ToHex;
|
||||
use hmac::{Hmac, KeyInit as _, Mac};
|
||||
use hmac::{Hmac, Mac};
|
||||
use serde::Deserialize;
|
||||
use sha2::Sha256;
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use axum::{Router, http::HeaderMap};
|
||||
use cache::CloneCache;
|
||||
use komodo_client::entities::resource::Resource;
|
||||
use mogh_cache::CloneCache;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::resource::KomodoResource;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{str::FromStr, sync::OnceLock, time::Duration};
|
||||
use std::{str::FromStr, sync::OnceLock};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use komodo_client::{
|
||||
@@ -11,7 +11,7 @@ use komodo_client::{
|
||||
stack::Stack, sync::ResourceSync, user::git_webhook_user,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
@@ -23,7 +23,6 @@ use crate::{
|
||||
},
|
||||
helpers::update::init_execution_update,
|
||||
resource,
|
||||
state::action_states,
|
||||
};
|
||||
|
||||
use super::{ANY_BRANCH, ListenerLockCache};
|
||||
@@ -38,6 +37,11 @@ impl super::CustomSecret for Build {
|
||||
}
|
||||
}
|
||||
|
||||
fn build_locks() -> &'static ListenerLockCache {
|
||||
static BUILD_LOCKS: OnceLock<ListenerLockCache> = OnceLock::new();
|
||||
BUILD_LOCKS.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
pub async fn handle_build_webhook<B: super::ExtractBranch>(
|
||||
build: Build,
|
||||
body: String,
|
||||
@@ -46,6 +50,12 @@ pub async fn handle_build_webhook<B: super::ExtractBranch>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Acquire and hold lock to make a task queue for
|
||||
// subsequent listener calls on same resource.
|
||||
// It would fail if we let it go through from action state busy.
|
||||
let lock = build_locks().get_or_insert_default(&build.id).await;
|
||||
let _lock = lock.lock().await;
|
||||
|
||||
// Use the correct target branch when using linked repo.
|
||||
let branch = if build.config.linked_repo.is_empty() {
|
||||
build.config.branch
|
||||
@@ -59,78 +69,23 @@ pub async fn handle_build_webhook<B: super::ExtractBranch>(
|
||||
|
||||
B::verify_branch(&body, &branch)?;
|
||||
|
||||
// Cancel if currently building
|
||||
if action_states()
|
||||
.build
|
||||
.get(&build.id)
|
||||
.await
|
||||
.and_then(|states| {
|
||||
states.get().ok().map(|states| states.building)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
let user = git_webhook_user().to_owned();
|
||||
let cancel = ExecuteRequest::CancelBuild(CancelBuild {
|
||||
build: build.id.clone(),
|
||||
});
|
||||
let update = init_execution_update(&cancel, &user).await?;
|
||||
let ExecuteRequest::CancelBuild(cancel) = cancel else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
cancel
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
|
||||
poll_build_until_cancelled(&build.id).await?;
|
||||
}
|
||||
|
||||
let user = git_webhook_user().to_owned();
|
||||
let run = ExecuteRequest::RunBuild(RunBuild { build: build.id });
|
||||
let update = init_execution_update(&run, &user).await?;
|
||||
let ExecuteRequest::RunBuild(run) = run else {
|
||||
let req = ExecuteRequest::RunBuild(RunBuild { build: build.id });
|
||||
let update = init_execution_update(&req, &user).await?;
|
||||
let ExecuteRequest::RunBuild(req) = req else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
run
|
||||
req
|
||||
.resolve(&ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id: Uuid::new_v4(),
|
||||
id: Uuid::new_v4(),
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.error)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn poll_build_until_cancelled(
|
||||
build_id: &String,
|
||||
) -> anyhow::Result<()> {
|
||||
let action_states = action_states();
|
||||
// Poll to ensure cancelled
|
||||
for _ in 0..10 {
|
||||
if !action_states
|
||||
.build
|
||||
.get(build_id)
|
||||
.await
|
||||
.and_then(|states| {
|
||||
states.get().ok().map(|states| states.building)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
Err(anyhow!("Build still running after cancel"))
|
||||
}
|
||||
|
||||
// ======
|
||||
// REPO
|
||||
// ======
|
||||
@@ -166,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)?;
|
||||
@@ -190,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)?;
|
||||
@@ -214,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)?;
|
||||
@@ -286,11 +241,11 @@ fn stack_locks() -> &'static ListenerLockCache {
|
||||
}
|
||||
|
||||
pub trait StackExecution {
|
||||
async fn resolve(stack: Stack) -> mogh_error::Result<()>;
|
||||
async fn resolve(stack: Stack) -> serror::Result<()>;
|
||||
}
|
||||
|
||||
impl StackExecution for RefreshStackCache {
|
||||
async fn resolve(stack: Stack) -> mogh_error::Result<()> {
|
||||
async fn resolve(stack: Stack) -> serror::Result<()> {
|
||||
RefreshStackCache { stack: stack.id }
|
||||
.resolve(&WriteArgs {
|
||||
user: git_webhook_user().to_owned(),
|
||||
@@ -301,7 +256,7 @@ impl StackExecution for RefreshStackCache {
|
||||
}
|
||||
|
||||
impl StackExecution for DeployStack {
|
||||
async fn resolve(stack: Stack) -> mogh_error::Result<()> {
|
||||
async fn resolve(stack: Stack) -> serror::Result<()> {
|
||||
let user = git_webhook_user().to_owned();
|
||||
if stack.config.webhook_force_deploy {
|
||||
let req = ExecuteRequest::DeployStack(DeployStack {
|
||||
@@ -317,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)?;
|
||||
@@ -335,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)?;
|
||||
@@ -448,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)?;
|
||||
@@ -562,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)?;
|
||||
@@ -628,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)?;
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
use std::net::IpAddr;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
|
||||
use axum::{Router, extract::Path, http::HeaderMap, routing::post};
|
||||
use axum::{
|
||||
Router,
|
||||
extract::{ConnectInfo, Path},
|
||||
http::HeaderMap,
|
||||
routing::post,
|
||||
};
|
||||
use komodo_client::entities::{
|
||||
action::Action, build::Build, procedure::Procedure, repo::Repo,
|
||||
resource::Resource, stack::Stack, sync::ResourceSync,
|
||||
};
|
||||
use mogh_auth_server::request_ip::RequestIp;
|
||||
use mogh_error::AddStatusCode;
|
||||
use mogh_rate_limit::WithFailureRateLimit;
|
||||
use rate_limit::WithFailureRateLimit;
|
||||
use reqwest::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use serror::AddStatusCode;
|
||||
use tracing::Instrument;
|
||||
|
||||
use crate::{auth::GENERAL_RATE_LIMITER, resource::KomodoResource};
|
||||
use crate::{resource::KomodoResource, state::auth_rate_limiter};
|
||||
|
||||
use super::{
|
||||
CustomSecret, ExtractBranch, VerifySecret,
|
||||
@@ -51,9 +55,9 @@ pub fn router<P: VerifySecret + ExtractBranch>() -> Router {
|
||||
.route(
|
||||
"/build/{id}",
|
||||
post(
|
||||
|Path(Id { id }), RequestIp(ip), headers: HeaderMap, body: String| async move {
|
||||
|Path(Id { id }), headers: HeaderMap, ConnectInfo(info): ConnectInfo<SocketAddr>, body: String| async move {
|
||||
let build =
|
||||
auth_webhook::<P, Build>(&id, &headers, ip, &body).await?;
|
||||
auth_webhook::<P, Build>(&id, &headers, info.ip(), &body).await?;
|
||||
tokio::spawn(async move {
|
||||
let span = info_span!("BuildWebhook", id);
|
||||
async {
|
||||
@@ -70,16 +74,16 @@ pub fn router<P: VerifySecret + ExtractBranch>() -> Router {
|
||||
.instrument(span)
|
||||
.await
|
||||
});
|
||||
mogh_error::Result::Ok(())
|
||||
serror::Result::Ok(())
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/repo/{id}/{option}",
|
||||
post(
|
||||
|Path(IdAndOption::<RepoWebhookOption> { id, option }), RequestIp(ip), headers: HeaderMap, body: String| async move {
|
||||
|Path(IdAndOption::<RepoWebhookOption> { id, option }), headers: HeaderMap, ConnectInfo(info): ConnectInfo<SocketAddr>, body: String| async move {
|
||||
let repo =
|
||||
auth_webhook::<P, Repo>(&id, &headers, ip, &body).await?;
|
||||
auth_webhook::<P, Repo>(&id, &headers, info.ip(), &body).await?;
|
||||
tokio::spawn(async move {
|
||||
let span = info_span!("RepoWebhook", id);
|
||||
async {
|
||||
@@ -96,16 +100,16 @@ pub fn router<P: VerifySecret + ExtractBranch>() -> Router {
|
||||
.instrument(span)
|
||||
.await
|
||||
});
|
||||
mogh_error::Result::Ok(())
|
||||
serror::Result::Ok(())
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/stack/{id}/{option}",
|
||||
post(
|
||||
|Path(IdAndOption::<StackWebhookOption> { id, option }), RequestIp(ip), headers: HeaderMap, body: String| async move {
|
||||
|Path(IdAndOption::<StackWebhookOption> { id, option }), headers: HeaderMap, ConnectInfo(info): ConnectInfo<SocketAddr>, body: String| async move {
|
||||
let stack =
|
||||
auth_webhook::<P, Stack>(&id, &headers, ip, &body).await?;
|
||||
auth_webhook::<P, Stack>(&id, &headers, info.ip(), &body).await?;
|
||||
tokio::spawn(async move {
|
||||
let span = info_span!("StackWebhook", id);
|
||||
async {
|
||||
@@ -122,16 +126,16 @@ pub fn router<P: VerifySecret + ExtractBranch>() -> Router {
|
||||
.instrument(span)
|
||||
.await
|
||||
});
|
||||
mogh_error::Result::Ok(())
|
||||
serror::Result::Ok(())
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/sync/{id}/{option}",
|
||||
post(
|
||||
|Path(IdAndOption::<SyncWebhookOption> { id, option }), RequestIp(ip), headers: HeaderMap, body: String| async move {
|
||||
|Path(IdAndOption::<SyncWebhookOption> { id, option }), headers: HeaderMap, ConnectInfo(info): ConnectInfo<SocketAddr>, body: String| async move {
|
||||
let sync =
|
||||
auth_webhook::<P, ResourceSync>(&id, &headers, ip, &body).await?;
|
||||
auth_webhook::<P, ResourceSync>(&id, &headers, info.ip(), &body).await?;
|
||||
tokio::spawn(async move {
|
||||
let span = info_span!("ResourceSyncWebhook", id);
|
||||
async {
|
||||
@@ -148,16 +152,16 @@ pub fn router<P: VerifySecret + ExtractBranch>() -> Router {
|
||||
.instrument(span)
|
||||
.await
|
||||
});
|
||||
mogh_error::Result::Ok(())
|
||||
serror::Result::Ok(())
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/procedure/{id}/{branch}",
|
||||
post(
|
||||
|Path(IdAndBranch { id, branch }), RequestIp(ip), headers: HeaderMap, body: String| async move {
|
||||
|Path(IdAndBranch { id, branch }), headers: HeaderMap, ConnectInfo(info): ConnectInfo<SocketAddr>, body: String| async move {
|
||||
let procedure =
|
||||
auth_webhook::<P, Procedure>(&id, &headers, ip, &body).await?;
|
||||
auth_webhook::<P, Procedure>(&id, &headers, info.ip(), &body).await?;
|
||||
tokio::spawn(async move {
|
||||
let span = info_span!("ProcedureWebhook", id);
|
||||
async {
|
||||
@@ -174,16 +178,16 @@ pub fn router<P: VerifySecret + ExtractBranch>() -> Router {
|
||||
.instrument(span)
|
||||
.await
|
||||
});
|
||||
mogh_error::Result::Ok(())
|
||||
serror::Result::Ok(())
|
||||
},
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/action/{id}/{branch}",
|
||||
post(
|
||||
|Path(IdAndBranch { id, branch }), RequestIp(ip), headers: HeaderMap, body: String| async move {
|
||||
|Path(IdAndBranch { id, branch }), headers: HeaderMap, ConnectInfo(info): ConnectInfo<SocketAddr>, body: String| async move {
|
||||
let action =
|
||||
auth_webhook::<P, Action>(&id, &headers, ip, &body).await?;
|
||||
auth_webhook::<P, Action>(&id, &headers, info.ip(), &body).await?;
|
||||
tokio::spawn(async move {
|
||||
let span = info_span!("ActionWebhook", id);
|
||||
async {
|
||||
@@ -200,7 +204,7 @@ pub fn router<P: VerifySecret + ExtractBranch>() -> Router {
|
||||
.instrument(span)
|
||||
.await
|
||||
});
|
||||
mogh_error::Result::Ok(())
|
||||
serror::Result::Ok(())
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -211,7 +215,7 @@ async fn auth_webhook<P, R>(
|
||||
headers: &HeaderMap,
|
||||
ip: IpAddr,
|
||||
body: &str,
|
||||
) -> mogh_error::Result<Resource<R::Config, R::Info>>
|
||||
) -> serror::Result<Resource<R::Config, R::Info>>
|
||||
where
|
||||
P: VerifySecret,
|
||||
R: KomodoResource + CustomSecret,
|
||||
@@ -222,31 +226,12 @@ 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)
|
||||
serror::Result::Ok(resource)
|
||||
}
|
||||
.with_failure_rate_limit_using_ip(&GENERAL_RATE_LIMITER, &ip)
|
||||
.with_failure_rate_limit_using_headers(
|
||||
auth_rate_limiter(),
|
||||
headers,
|
||||
Some(ip),
|
||||
)
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
warn!(
|
||||
resource_id = id,
|
||||
source_ip = ip.to_string(),
|
||||
"Incoming webhook failed | ERROR: {:#}",
|
||||
e.error
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
use axum::{Extension, Router, routing::get};
|
||||
use komodo_client::entities::user::User;
|
||||
use mogh_auth_server::middleware::authenticate_request;
|
||||
use mogh_error::Json;
|
||||
use mogh_server::{
|
||||
cors::cors_layer, session::memory_session_layer,
|
||||
ui::serve_static_ui,
|
||||
use axum::{
|
||||
Router,
|
||||
http::{HeaderName, HeaderValue},
|
||||
routing::get,
|
||||
};
|
||||
use tower_http::{
|
||||
services::{ServeDir, ServeFile},
|
||||
set_header::SetResponseHeaderLayer,
|
||||
};
|
||||
use tower_sessions::{
|
||||
Expiry, MemoryStore, SessionManagerLayer,
|
||||
cookie::{SameSite, time::Duration},
|
||||
};
|
||||
|
||||
use crate::{auth::KomodoAuthImpl, config::core_config, ts_client};
|
||||
use crate::{
|
||||
config::{core_config, core_host, cors_layer},
|
||||
ts_client,
|
||||
};
|
||||
|
||||
pub mod auth;
|
||||
pub mod execute;
|
||||
pub mod read;
|
||||
pub mod user;
|
||||
pub mod write;
|
||||
|
||||
mod listener;
|
||||
mod openapi;
|
||||
mod terminal;
|
||||
mod ws;
|
||||
|
||||
@@ -25,11 +34,18 @@ struct Variant {
|
||||
|
||||
pub fn app() -> Router {
|
||||
let config = core_config();
|
||||
|
||||
// Setup static frontend services
|
||||
let frontend_path = &config.frontend_path;
|
||||
let frontend_index =
|
||||
ServeFile::new(format!("{frontend_path}/index.html"));
|
||||
let serve_frontend = ServeDir::new(frontend_path)
|
||||
.not_found_service(frontend_index.clone());
|
||||
|
||||
Router::new()
|
||||
.merge(openapi::serve_docs())
|
||||
.route("/version", get(|| async { env!("CARGO_PKG_VERSION") }))
|
||||
.nest("/auth", mogh_auth_server::api::router::<KomodoAuthImpl>())
|
||||
.nest("/user", user_router())
|
||||
.nest("/auth", auth::router())
|
||||
.nest("/user", user::router())
|
||||
.nest("/read", read::router())
|
||||
.nest("/write", write::router())
|
||||
.nest("/execute", execute::router())
|
||||
@@ -37,21 +53,41 @@ pub fn app() -> Router {
|
||||
.nest("/listener", listener::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,
|
||||
.layer(memory_session_layer())
|
||||
.fallback_service(serve_frontend)
|
||||
.layer(cors_layer())
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
HeaderName::from_static("x-content-type-options"),
|
||||
HeaderValue::from_static("nosniff"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
HeaderName::from_static("x-frame-options"),
|
||||
HeaderValue::from_static("DENY"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
HeaderName::from_static("x-xss-protection"),
|
||||
HeaderValue::from_static("1; mode=block"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::overriding(
|
||||
HeaderName::from_static("referrer-policy"),
|
||||
HeaderValue::from_static("strict-origin-when-cross-origin"),
|
||||
))
|
||||
.layer(cors_layer(config))
|
||||
}
|
||||
|
||||
fn user_router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/",
|
||||
get(|Extension(user): Extension<User>| async { Json(user) }),
|
||||
)
|
||||
.layer(axum::middleware::from_fn(
|
||||
authenticate_request::<KomodoAuthImpl, false>,
|
||||
))
|
||||
const MEMORY_SESSION_EXPIRY_SECONDS: i64 = 60;
|
||||
|
||||
fn memory_session_layer() -> SessionManagerLayer<MemoryStore> {
|
||||
let config = core_config();
|
||||
let mut layer = SessionManagerLayer::new(MemoryStore::default())
|
||||
.with_expiry(Expiry::OnInactivity(Duration::seconds(
|
||||
MEMORY_SESSION_EXPIRY_SECONDS,
|
||||
)))
|
||||
.with_secure(config.host.starts_with("https://"))
|
||||
// Needs Lax in order for sessions to work
|
||||
// accross oauth redirects.
|
||||
.with_same_site(SameSite::Lax);
|
||||
if let Some(domain) = core_host().and_then(|url| url.domain()) {
|
||||
layer = layer.with_domain(domain);
|
||||
}
|
||||
layer
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Komodo API Docs</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script id="api-reference" type="application/json">
|
||||
$spec
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,8 +0,0 @@
|
||||
use komodo_client::openapi::KomodoApi;
|
||||
use utoipa::OpenApi as _;
|
||||
use utoipa_scalar::{Scalar, Servable as _};
|
||||
|
||||
pub fn serve_docs() -> Scalar<utoipa::openapi::OpenApi> {
|
||||
Scalar::with_url("/docs", KomodoApi::openapi())
|
||||
.custom_html(include_str!("docs.html"))
|
||||
}
|
||||
@@ -3,16 +3,15 @@ use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
action::{
|
||||
Action, ActionActionState, ActionListItem, ActionSortBy,
|
||||
ActionState,
|
||||
Action, ActionActionState, ActionListItem, ActionState,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::{get_action_state, get_all_tags},
|
||||
helpers::query::get_all_tags,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_state_cache, action_states},
|
||||
@@ -24,7 +23,7 @@ impl Resolve<ReadArgs> for GetAction {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Action> {
|
||||
) -> serror::Result<Action> {
|
||||
Ok(
|
||||
get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
@@ -40,43 +39,21 @@ impl Resolve<ReadArgs> for ListActions {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<ActionListItem>> {
|
||||
) -> serror::Result<Vec<ActionListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<ActionListItem> =
|
||||
match self.sort_by {
|
||||
ActionSortBy::Name => resource::ListItemSort::Name,
|
||||
ActionSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
ActionSortBy::NextRun => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.next_scheduled_run.cmp(&b.info.next_scheduled_run)
|
||||
}))
|
||||
}
|
||||
};
|
||||
let actions = resource::list_items_for_user::<Action>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|action| {
|
||||
states.is_empty() || states.contains(&action.info.state)
|
||||
},
|
||||
Ok(
|
||||
resource::list_for_user::<Action>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
Ok(actions)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,34 +61,18 @@ impl Resolve<ReadArgs> for ListFullActions {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullActionsResponse> {
|
||||
) -> serror::Result<ListFullActionsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user_filtered::<Action, _>(
|
||||
resource::list_full_for_user::<Action>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|action| {
|
||||
let states = states.clone();
|
||||
async move {
|
||||
if states.is_empty()
|
||||
|| states.contains(&get_action_state(&action.id).await)
|
||||
{
|
||||
Some(action)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -122,7 +83,7 @@ impl Resolve<ReadArgs> for GetActionActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ActionActionState> {
|
||||
) -> serror::Result<ActionActionState> {
|
||||
let action = get_check_permissions::<Action>(
|
||||
&self.action,
|
||||
user,
|
||||
@@ -143,11 +104,9 @@ impl Resolve<ReadArgs> for GetActionsSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetActionsSummaryResponse> {
|
||||
) -> serror::Result<GetActionsSummaryResponse> {
|
||||
let actions = resource::list_full_for_user::<Action>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
|
||||
@@ -10,7 +10,7 @@ use komodo_client::{
|
||||
},
|
||||
entities::permission::PermissionLevel,
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
@@ -28,7 +28,7 @@ impl Resolve<ReadArgs> for ListAlerts {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListAlertsResponse> {
|
||||
) -> serror::Result<ListAlertsResponse> {
|
||||
// Alerts
|
||||
let query = user_resource_target_query(user, self.query)
|
||||
.await?
|
||||
@@ -40,7 +40,7 @@ impl Resolve<ReadArgs> for ListAlerts {
|
||||
FindOptions::builder()
|
||||
.sort(doc! { "ts": -1 })
|
||||
.limit(NUM_ALERTS_PER_PAGE as i64)
|
||||
.skip(self.page.saturating_mul(NUM_ALERTS_PER_PAGE))
|
||||
.skip(self.page * NUM_ALERTS_PER_PAGE)
|
||||
.build(),
|
||||
)
|
||||
.await
|
||||
@@ -62,7 +62,7 @@ impl Resolve<ReadArgs> for GetAlert {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetAlertResponse> {
|
||||
) -> serror::Result<GetAlertResponse> {
|
||||
let alert = find_one_by_id(&db_client().alerts, &self.id)
|
||||
.await
|
||||
.context("failed to query db for alert")?
|
||||
|
||||
@@ -4,11 +4,11 @@ use database::mungos::mongodb::bson::doc;
|
||||
use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
alerter::{Alerter, AlerterListItem, AlerterSortBy},
|
||||
alerter::{Alerter, AlerterListItem},
|
||||
permission::PermissionLevel,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags,
|
||||
@@ -23,7 +23,7 @@ impl Resolve<ReadArgs> for GetAlerter {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Alerter> {
|
||||
) -> serror::Result<Alerter> {
|
||||
Ok(
|
||||
get_check_permissions::<Alerter>(
|
||||
&self.alerter,
|
||||
@@ -39,34 +39,18 @@ impl Resolve<ReadArgs> for ListAlerters {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<AlerterListItem>> {
|
||||
) -> serror::Result<Vec<AlerterListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<AlerterListItem> =
|
||||
match self.sort_by {
|
||||
AlerterSortBy::Name => resource::ListItemSort::Name,
|
||||
AlerterSortBy::Type => {
|
||||
resource::ListItemSort::DbField("config.endpoint.type")
|
||||
}
|
||||
AlerterSortBy::Enabled => {
|
||||
resource::ListItemSort::DbField("config.enabled")
|
||||
}
|
||||
};
|
||||
Ok(
|
||||
resource::list_items_for_user::<Alerter>(
|
||||
resource::list_for_user::<Alerter>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|_| true,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -77,18 +61,15 @@ impl Resolve<ReadArgs> for ListFullAlerters {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullAlertersResponse> {
|
||||
) -> serror::Result<ListFullAlertersResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user::<Alerter>(
|
||||
self.query,
|
||||
limit as i64,
|
||||
self.page.saturating_mul(limit),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
@@ -102,10 +83,8 @@ impl Resolve<ReadArgs> for GetAlertersSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetAlertersSummaryResponse> {
|
||||
) -> serror::Result<GetAlertersSummaryResponse> {
|
||||
let query = match list_resource_ids_for_user::<Alerter>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
|
||||
@@ -11,14 +11,12 @@ use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
Operation,
|
||||
build::{
|
||||
Build, BuildActionState, BuildListItem, BuildSortBy, BuildState,
|
||||
},
|
||||
build::{Build, BuildActionState, BuildListItem, BuildState},
|
||||
permission::PermissionLevel,
|
||||
update::UpdateStatus,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags,
|
||||
@@ -33,7 +31,7 @@ impl Resolve<ReadArgs> for GetBuild {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Build> {
|
||||
) -> serror::Result<Build> {
|
||||
Ok(
|
||||
get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
@@ -49,41 +47,21 @@ impl Resolve<ReadArgs> for ListBuilds {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<BuildListItem>> {
|
||||
) -> serror::Result<Vec<BuildListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<BuildListItem> =
|
||||
match self.sort_by {
|
||||
BuildSortBy::Name => resource::ListItemSort::Name,
|
||||
BuildSortBy::Source => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.repo.cmp(&b.info.repo)
|
||||
}))
|
||||
}
|
||||
BuildSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
};
|
||||
let builds = resource::list_items_for_user::<Build>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|build| states.is_empty() || states.contains(&build.info.state),
|
||||
Ok(
|
||||
resource::list_for_user::<Build>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
Ok(builds)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,35 +69,18 @@ impl Resolve<ReadArgs> for ListFullBuilds {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullBuildsResponse> {
|
||||
) -> serror::Result<ListFullBuildsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user_filtered::<Build, _>(
|
||||
resource::list_full_for_user::<Build>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|build| {
|
||||
let states = states.clone();
|
||||
async move {
|
||||
if states.is_empty()
|
||||
|| states
|
||||
.contains(&resource::get_build_state(&build.id).await)
|
||||
{
|
||||
Some(build)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -130,7 +91,7 @@ impl Resolve<ReadArgs> for GetBuildActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<BuildActionState> {
|
||||
) -> serror::Result<BuildActionState> {
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
user,
|
||||
@@ -151,11 +112,9 @@ impl Resolve<ReadArgs> for GetBuildsSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetBuildsSummaryResponse> {
|
||||
) -> serror::Result<GetBuildsSummaryResponse> {
|
||||
let builds = resource::list_full_for_user::<Build>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
@@ -201,12 +160,11 @@ impl Resolve<ReadArgs> for GetBuildMonthlyStats {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &ReadArgs,
|
||||
) -> mogh_error::Result<GetBuildMonthlyStatsResponse> {
|
||||
) -> serror::Result<GetBuildMonthlyStatsResponse> {
|
||||
let curr_ts = unix_timestamp_ms() as i64;
|
||||
let next_day = curr_ts - curr_ts % ONE_DAY_MS + ONE_DAY_MS;
|
||||
|
||||
let close_ts =
|
||||
next_day - (self.page as i64).saturating_mul(30 * ONE_DAY_MS);
|
||||
let close_ts = next_day - self.page as i64 * 30 * ONE_DAY_MS;
|
||||
let open_ts = close_ts - 30 * ONE_DAY_MS;
|
||||
|
||||
let mut build_updates = db_client()
|
||||
@@ -258,7 +216,7 @@ impl Resolve<ReadArgs> for ListBuildVersions {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<BuildVersionResponseItem>> {
|
||||
) -> serror::Result<Vec<BuildVersionResponseItem>> {
|
||||
let ListBuildVersions {
|
||||
build,
|
||||
major,
|
||||
@@ -315,7 +273,7 @@ impl Resolve<ReadArgs> for ListCommonBuildExtraArgs {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListCommonBuildExtraArgsResponse> {
|
||||
) -> serror::Result<ListCommonBuildExtraArgsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
@@ -323,8 +281,6 @@ impl Resolve<ReadArgs> for ListCommonBuildExtraArgs {
|
||||
};
|
||||
let builds = resource::list_full_for_user::<Build>(
|
||||
self.query,
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|
||||
@@ -4,11 +4,11 @@ use database::mungos::mongodb::bson::doc;
|
||||
use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
builder::{Builder, BuilderListItem, BuilderSortBy},
|
||||
builder::{Builder, BuilderListItem},
|
||||
permission::PermissionLevel,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags,
|
||||
@@ -23,7 +23,7 @@ impl Resolve<ReadArgs> for GetBuilder {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Builder> {
|
||||
) -> serror::Result<Builder> {
|
||||
Ok(
|
||||
get_check_permissions::<Builder>(
|
||||
&self.builder,
|
||||
@@ -39,36 +39,18 @@ impl Resolve<ReadArgs> for ListBuilders {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<BuilderListItem>> {
|
||||
) -> serror::Result<Vec<BuilderListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<BuilderListItem> =
|
||||
match self.sort_by {
|
||||
BuilderSortBy::Name => resource::ListItemSort::Name,
|
||||
BuilderSortBy::Provider => {
|
||||
resource::ListItemSort::DbField("config.type")
|
||||
}
|
||||
BuilderSortBy::InstanceType => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.instance_type.cmp(&b.info.instance_type)
|
||||
}))
|
||||
}
|
||||
};
|
||||
Ok(
|
||||
resource::list_items_for_user::<Builder>(
|
||||
resource::list_for_user::<Builder>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|_| true,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -79,18 +61,15 @@ impl Resolve<ReadArgs> for ListFullBuilders {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullBuildersResponse> {
|
||||
) -> serror::Result<ListFullBuildersResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user::<Builder>(
|
||||
self.query,
|
||||
limit as i64,
|
||||
self.page.saturating_mul(limit),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
@@ -104,10 +83,8 @@ impl Resolve<ReadArgs> for GetBuildersSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetBuildersSummaryResponse> {
|
||||
) -> serror::Result<GetBuildersSummaryResponse> {
|
||||
let query = match list_resource_ids_for_user::<Builder>(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
|
||||
@@ -7,7 +7,7 @@ use komodo_client::{
|
||||
SwarmOrServer,
|
||||
deployment::{
|
||||
Deployment, DeploymentActionState, DeploymentConfig,
|
||||
DeploymentListItem, DeploymentSortBy, DeploymentState,
|
||||
DeploymentListItem, DeploymentState,
|
||||
},
|
||||
docker::{
|
||||
container::{Container, ContainerStats},
|
||||
@@ -18,16 +18,14 @@ use komodo_client::{
|
||||
update::Log,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError as _;
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api::{self, container::InspectContainer};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError as _;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
periphery_client,
|
||||
query::{get_all_tags, get_deployment_state},
|
||||
swarm::swarm_request,
|
||||
periphery_client, query::get_all_tags, swarm::swarm_request,
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource::{self, setup_deployment_execution},
|
||||
@@ -42,7 +40,7 @@ impl Resolve<ReadArgs> for GetDeployment {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Deployment> {
|
||||
) -> serror::Result<Deployment> {
|
||||
Ok(
|
||||
get_check_permissions::<Deployment>(
|
||||
&self.deployment,
|
||||
@@ -58,60 +56,28 @@ impl Resolve<ReadArgs> for ListDeployments {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<DeploymentListItem>> {
|
||||
) -> serror::Result<Vec<DeploymentListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let only_update_available = self.query.specific.update_available;
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<DeploymentListItem> =
|
||||
match self.sort_by {
|
||||
DeploymentSortBy::Name => resource::ListItemSort::Name,
|
||||
DeploymentSortBy::Image => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.image.cmp(&b.info.image)
|
||||
}))
|
||||
}
|
||||
DeploymentSortBy::Host => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
let host_a = if a.info.swarm_id.is_empty() {
|
||||
&a.info.server_name
|
||||
} else {
|
||||
&a.info.swarm_name
|
||||
};
|
||||
let host_b = if b.info.swarm_id.is_empty() {
|
||||
&b.info.server_name
|
||||
} else {
|
||||
&b.info.swarm_name
|
||||
};
|
||||
host_a.cmp(host_b)
|
||||
}))
|
||||
}
|
||||
DeploymentSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
};
|
||||
let deployments = resource::list_items_for_user::<Deployment>(
|
||||
let deployments = resource::list_for_user::<Deployment>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|deployment| {
|
||||
(!only_update_available || deployment.info.update_available)
|
||||
&& (states.is_empty()
|
||||
|| states.contains(&deployment.info.state))
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let deployments = if only_update_available {
|
||||
deployments
|
||||
.into_iter()
|
||||
.filter(|deployment| deployment.info.update_available)
|
||||
.collect()
|
||||
} else {
|
||||
deployments
|
||||
};
|
||||
Ok(deployments)
|
||||
}
|
||||
}
|
||||
@@ -120,38 +86,18 @@ impl Resolve<ReadArgs> for ListFullDeployments {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullDeploymentsResponse> {
|
||||
) -> serror::Result<ListFullDeploymentsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user_filtered::<Deployment, _>(
|
||||
resource::list_full_for_user::<Deployment>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|deployment| {
|
||||
let states = states.clone();
|
||||
async move {
|
||||
if states.is_empty()
|
||||
|| states.contains(
|
||||
&get_deployment_state(&deployment.id)
|
||||
.await
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
{
|
||||
Some(deployment)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -162,7 +108,7 @@ impl Resolve<ReadArgs> for GetDeploymentContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetDeploymentContainerResponse> {
|
||||
) -> serror::Result<GetDeploymentContainerResponse> {
|
||||
let deployment = get_check_permissions::<Deployment>(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -187,7 +133,7 @@ impl Resolve<ReadArgs> for GetDeploymentLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Log> {
|
||||
) -> serror::Result<Log> {
|
||||
let GetDeploymentLog {
|
||||
deployment,
|
||||
tail,
|
||||
@@ -201,10 +147,7 @@ impl Resolve<ReadArgs> for GetDeploymentLog {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
let log = match swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => swarm_request(
|
||||
&swarm.config.server_ids,
|
||||
periphery_client::api::swarm::GetSwarmServiceLog {
|
||||
@@ -237,7 +180,7 @@ impl Resolve<ReadArgs> for SearchDeploymentLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Log> {
|
||||
) -> serror::Result<Log> {
|
||||
let SearchDeploymentLog {
|
||||
deployment,
|
||||
terms,
|
||||
@@ -253,10 +196,7 @@ impl Resolve<ReadArgs> for SearchDeploymentLog {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
let log = match swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => swarm_request(
|
||||
&swarm.config.server_ids,
|
||||
periphery_client::api::swarm::GetSwarmServiceLogSearch {
|
||||
@@ -293,7 +233,7 @@ impl Resolve<ReadArgs> for InspectDeploymentContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Container> {
|
||||
) -> serror::Result<Container> {
|
||||
let InspectDeploymentContainer { deployment } = self;
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&deployment,
|
||||
@@ -340,7 +280,7 @@ impl Resolve<ReadArgs> for InspectDeploymentSwarmService {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<SwarmService> {
|
||||
) -> serror::Result<SwarmService> {
|
||||
let InspectDeploymentSwarmService { deployment } = self;
|
||||
let (deployment, swarm_or_server) = setup_deployment_execution(
|
||||
&deployment,
|
||||
@@ -374,7 +314,7 @@ impl Resolve<ReadArgs> for GetDeploymentStats {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ContainerStats> {
|
||||
) -> serror::Result<ContainerStats> {
|
||||
let Deployment {
|
||||
name,
|
||||
config: DeploymentConfig { server_id, .. },
|
||||
@@ -404,7 +344,7 @@ impl Resolve<ReadArgs> for GetDeploymentActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<DeploymentActionState> {
|
||||
) -> serror::Result<DeploymentActionState> {
|
||||
let deployment = get_check_permissions::<Deployment>(
|
||||
&self.deployment,
|
||||
user,
|
||||
@@ -425,11 +365,9 @@ impl Resolve<ReadArgs> for GetDeploymentsSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetDeploymentsSummaryResponse> {
|
||||
) -> serror::Result<GetDeploymentsSummaryResponse> {
|
||||
let deployments = resource::list_full_for_user::<Deployment>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
@@ -470,7 +408,7 @@ impl Resolve<ReadArgs> for ListCommonDeploymentExtraArgs {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListCommonDeploymentExtraArgsResponse> {
|
||||
) -> serror::Result<ListCommonDeploymentExtraArgsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
@@ -478,8 +416,6 @@ impl Resolve<ReadArgs> for ListCommonDeploymentExtraArgs {
|
||||
};
|
||||
let deployments = resource::list_full_for_user::<Deployment>(
|
||||
self.query,
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|
||||
@@ -1,542 +0,0 @@
|
||||
use std::cmp;
|
||||
|
||||
use anyhow::{Context as _, anyhow};
|
||||
use database::bson::doc;
|
||||
use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
ResourceTarget,
|
||||
deployment::Deployment,
|
||||
docker::{
|
||||
container::{
|
||||
Container, ContainerListItem, ContainerStateStatusEnum,
|
||||
},
|
||||
image::{Image, ImageHistoryResponseItem},
|
||||
network::Network,
|
||||
volume::Volume,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
server::{Server, ServerQuery, ServerState},
|
||||
stack::{Stack, StackServiceNames},
|
||||
update::Log,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api as periphery;
|
||||
|
||||
use crate::{
|
||||
api::read::ReadArgs,
|
||||
helpers::{periphery_client, query::get_all_tags},
|
||||
permission::{get_check_permissions, list_resources_for_user},
|
||||
resource,
|
||||
stack::compose_container_match_regex,
|
||||
state::server_status_cache,
|
||||
};
|
||||
|
||||
impl Resolve<ReadArgs> for GetContainersSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetContainersSummaryResponse> {
|
||||
let servers = resource::list_full_for_user::<Server>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
.context("failed to get servers from db")?;
|
||||
|
||||
let mut res = GetContainersSummaryResponse::default();
|
||||
|
||||
for server in servers {
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
|
||||
if let Some(docker) = &cache.docker {
|
||||
for container in &docker.containers {
|
||||
res.total += 1;
|
||||
match container.state {
|
||||
ContainerStateStatusEnum::Created
|
||||
| ContainerStateStatusEnum::Paused
|
||||
| ContainerStateStatusEnum::Exited => res.stopped += 1,
|
||||
ContainerStateStatusEnum::Running => res.running += 1,
|
||||
ContainerStateStatusEnum::Empty => res.unknown += 1,
|
||||
_ => res.unhealthy += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListAllContainers {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListAllContainersResponse> {
|
||||
let all_tags = if self.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let servers = resource::list_for_user::<Server>(
|
||||
ServerQuery::builder()
|
||||
.names(self.servers.clone())
|
||||
.tags(self.tags)
|
||||
.build(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut containers = Vec::<ContainerListItem>::new();
|
||||
let mut skipped = 0;
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let limit_usize = limit as usize;
|
||||
// Eg. page 1 skips until after 100 containers, page 2 after 200.
|
||||
let skip = limit.saturating_mul(self.page);
|
||||
// Match terms case insensitively.
|
||||
let terms = self
|
||||
.terms
|
||||
.iter()
|
||||
.map(|term| term.to_lowercase())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for server in servers {
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
let Some(docker) = &cache.docker else {
|
||||
continue;
|
||||
};
|
||||
let more = docker
|
||||
.containers
|
||||
.iter()
|
||||
.filter(|container| {
|
||||
// Apply state filter if defined.
|
||||
(self.state.is_empty() || self.state.contains(&container.state)) &&
|
||||
// Apply terms filter if defined
|
||||
(terms.is_empty()
|
||||
// Match when all terms contained within a name.
|
||||
|| {
|
||||
let name = container.name.to_lowercase();
|
||||
terms.iter().all(|term| name.contains(term))
|
||||
})
|
||||
});
|
||||
for container in more {
|
||||
if skipped < skip {
|
||||
skipped += 1;
|
||||
} else {
|
||||
// push and maybe early return
|
||||
containers.push(container.clone());
|
||||
if limit > 0 && containers.len() >= limit_usize {
|
||||
return Ok(containers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(containers)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListContainers {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListContainersResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.containers.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Container> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.inspect(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot inspect container: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::container::InspectContainer {
|
||||
name: self.container,
|
||||
})
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for GetResourceMatchingContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetResourceMatchingContainerResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
// first check deployments
|
||||
if let Ok(deployment) =
|
||||
resource::get::<Deployment>(&self.container).await
|
||||
{
|
||||
return Ok(GetResourceMatchingContainerResponse {
|
||||
resource: ResourceTarget::Deployment(deployment.id).into(),
|
||||
});
|
||||
}
|
||||
|
||||
// then check stacks
|
||||
let stacks = list_resources_for_user::<Stack>(
|
||||
doc! { "config.server_id": &server.id },
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// check matching stack
|
||||
for stack in stacks {
|
||||
for StackServiceNames {
|
||||
service_name,
|
||||
container_name,
|
||||
..
|
||||
} in stack
|
||||
.info
|
||||
.deployed_services
|
||||
.unwrap_or(stack.info.latest_services)
|
||||
{
|
||||
let is_match = match compose_container_match_regex(&container_name)
|
||||
.with_context(|| format!("failed to construct container name matching regex for service {service_name}"))
|
||||
{
|
||||
Ok(regex) => regex,
|
||||
Err(e) => {
|
||||
warn!("{e:#}");
|
||||
continue;
|
||||
}
|
||||
}.is_match(&self.container);
|
||||
|
||||
if is_match {
|
||||
return Ok(GetResourceMatchingContainerResponse {
|
||||
resource: ResourceTarget::Stack(stack.id).into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(GetResourceMatchingContainerResponse { resource: None })
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_LOG_LENGTH: u64 = 5000;
|
||||
|
||||
impl Resolve<ReadArgs> for GetContainerLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Log> {
|
||||
let GetContainerLog {
|
||||
server,
|
||||
container,
|
||||
tail,
|
||||
timestamps,
|
||||
} = self;
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::container::GetContainerLog {
|
||||
name: container,
|
||||
tail: cmp::min(tail, MAX_LOG_LENGTH),
|
||||
timestamps,
|
||||
})
|
||||
.await
|
||||
.context("failed at call to periphery")?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for SearchContainerLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Log> {
|
||||
let SearchContainerLog {
|
||||
server,
|
||||
container,
|
||||
terms,
|
||||
combinator,
|
||||
invert,
|
||||
timestamps,
|
||||
} = self;
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::container::GetContainerLogSearch {
|
||||
name: container,
|
||||
terms,
|
||||
combinator,
|
||||
invert,
|
||||
timestamps,
|
||||
})
|
||||
.await
|
||||
.context("failed at call to periphery")?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListComposeProjects {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListComposeProjectsResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.projects.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListNetworks {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListNetworksResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.networks.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectNetwork {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Network> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot inspect network: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::docker::InspectNetwork {
|
||||
name: self.network,
|
||||
})
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListImages {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListImagesResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.images.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectImage {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Image> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!("Cannot inspect image: server is {:?}", cache.state)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::docker::InspectImage { name: self.image })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListImageHistory {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<ImageHistoryResponseItem>> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot get image history: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::docker::ImageHistory { name: self.image })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListVolumes {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListVolumesResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.volumes.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectVolume {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Volume> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!("Cannot inspect volume: server is {:?}", cache.state)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::docker::InspectVolume { name: self.volume })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::{collections::HashSet, time::Instant};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use axum::{
|
||||
@@ -10,7 +10,7 @@ use komodo_client::{
|
||||
ResourceTarget,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig},
|
||||
config::{GitProvider, ImageRegistry},
|
||||
config::{DockerRegistry, GitProvider},
|
||||
permission::PermissionLevel,
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
@@ -18,19 +18,16 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mogh_auth_server::middleware::authenticate_request;
|
||||
use mogh_error::Response;
|
||||
use mogh_error::{AddStatusCodeError, Json};
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use response::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use strum::{Display, EnumDiscriminants};
|
||||
use serror::Json;
|
||||
use typeshare::typeshare;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
auth::KomodoAuthImpl,
|
||||
auth::auth_request,
|
||||
config::{core_config, core_keys},
|
||||
helpers::periphery_client,
|
||||
resource,
|
||||
@@ -44,7 +41,6 @@ mod alerter;
|
||||
mod build;
|
||||
mod builder;
|
||||
mod deployment;
|
||||
mod docker;
|
||||
mod onboarding_key;
|
||||
mod permission;
|
||||
mod procedure;
|
||||
@@ -68,21 +64,17 @@ pub struct ReadArgs {
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
|
||||
)]
|
||||
#[strum_discriminants(name(ReadRequestMethod), derive(Display))]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Resolve)]
|
||||
#[args(ReadArgs)]
|
||||
#[response(Response)]
|
||||
#[error(mogh_error::Error)]
|
||||
#[error(serror::Error)]
|
||||
#[serde(tag = "type", content = "params")]
|
||||
enum ReadRequest {
|
||||
GetVersion(GetVersion),
|
||||
GetCoreInfo(GetCoreInfo),
|
||||
ListSecrets(ListSecrets),
|
||||
ListGitProvidersFromConfig(ListGitProvidersFromConfig),
|
||||
#[serde(alias = "ListDockerRegistriesFromConfig")]
|
||||
ListImageRegistriesFromConfig(ListImageRegistriesFromConfig),
|
||||
ListDockerRegistriesFromConfig(ListDockerRegistriesFromConfig),
|
||||
|
||||
// ==== SWARM ====
|
||||
GetSwarmsSummary(GetSwarmsSummary),
|
||||
@@ -119,33 +111,22 @@ enum ReadRequest {
|
||||
// ==== TERMINAL ====
|
||||
ListTerminals(ListTerminals),
|
||||
|
||||
// ==== CONTAINER ====
|
||||
#[serde(alias = "GetDockerContainersSummary")]
|
||||
GetContainersSummary(GetContainersSummary),
|
||||
#[serde(alias = "ListAllDockerContainers")]
|
||||
ListAllContainers(ListAllContainers),
|
||||
#[serde(alias = "ListDockerContainers")]
|
||||
ListContainers(ListContainers),
|
||||
#[serde(alias = "InspectDockerContainer")]
|
||||
InspectContainer(InspectContainer),
|
||||
// ==== DOCKER ====
|
||||
GetDockerContainersSummary(GetDockerContainersSummary),
|
||||
ListAllDockerContainers(ListAllDockerContainers),
|
||||
ListDockerContainers(ListDockerContainers),
|
||||
InspectDockerContainer(InspectDockerContainer),
|
||||
GetResourceMatchingContainer(GetResourceMatchingContainer),
|
||||
GetContainerLog(GetContainerLog),
|
||||
SearchContainerLog(SearchContainerLog),
|
||||
ListComposeProjects(ListComposeProjects),
|
||||
#[serde(alias = "ListDockerNetworks")]
|
||||
ListNetworks(ListNetworks),
|
||||
#[serde(alias = "InspectDockerNetwork")]
|
||||
InspectNetwork(InspectNetwork),
|
||||
#[serde(alias = "ListDockerImages")]
|
||||
ListImages(ListImages),
|
||||
#[serde(alias = "InspectDockerImage")]
|
||||
InspectImage(InspectImage),
|
||||
#[serde(alias = "ListDockerImageHistory")]
|
||||
ListImageHistory(ListImageHistory),
|
||||
#[serde(alias = "ListDockerVolumes")]
|
||||
ListVolumes(ListVolumes),
|
||||
#[serde(alias = "InspectDockerVolume")]
|
||||
InspectVolume(InspectVolume),
|
||||
ListDockerNetworks(ListDockerNetworks),
|
||||
InspectDockerNetwork(InspectDockerNetwork),
|
||||
ListDockerImages(ListDockerImages),
|
||||
InspectDockerImage(InspectDockerImage),
|
||||
ListDockerImageHistory(ListDockerImageHistory),
|
||||
ListDockerVolumes(ListDockerVolumes),
|
||||
InspectDockerVolume(InspectDockerVolume),
|
||||
|
||||
// ==== SERVER STATS ====
|
||||
GetSystemInformation(GetSystemInformation),
|
||||
@@ -164,7 +145,6 @@ enum ReadRequest {
|
||||
ListStacks(ListStacks),
|
||||
ListFullStacks(ListFullStacks),
|
||||
ListStackServices(ListStackServices),
|
||||
ListAllStackServices(ListAllStackServices),
|
||||
ListCommonStackExtraArgs(ListCommonStackExtraArgs),
|
||||
ListCommonStackBuildExtraArgs(ListCommonStackBuildExtraArgs),
|
||||
|
||||
@@ -272,10 +252,8 @@ enum ReadRequest {
|
||||
// ==== PROVIDER ====
|
||||
GetGitProviderAccount(GetGitProviderAccount),
|
||||
ListGitProviderAccounts(ListGitProviderAccounts),
|
||||
#[serde(alias = "GetDockerRegistryAccount")]
|
||||
GetImageRegistryAccount(GetImageRegistryAccount),
|
||||
#[serde(alias = "ListDockerRegistryAccounts")]
|
||||
ListImageRegistryAccounts(ListImageRegistryAccounts),
|
||||
GetDockerRegistryAccount(GetDockerRegistryAccount),
|
||||
ListDockerRegistryAccounts(ListDockerRegistryAccounts),
|
||||
|
||||
// ==== ONBOARDING KEY ====
|
||||
ListOnboardingKeys(ListOnboardingKeys),
|
||||
@@ -285,16 +263,14 @@ pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", post(handler))
|
||||
.route("/{variant}", post(variant_handler))
|
||||
.layer(middleware::from_fn(
|
||||
authenticate_request::<KomodoAuthImpl, true>,
|
||||
))
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
async fn variant_handler(
|
||||
user: Extension<User>,
|
||||
Path(Variant { variant }): Path<Variant>,
|
||||
Json(params): Json<serde_json::Value>,
|
||||
) -> mogh_error::Result<axum::response::Response> {
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let req: ReadRequest = serde_json::from_value(json!({
|
||||
"type": variant,
|
||||
"params": params,
|
||||
@@ -305,34 +281,16 @@ async fn variant_handler(
|
||||
async fn handler(
|
||||
Extension(user): Extension<User>,
|
||||
Json(request): Json<ReadRequest>,
|
||||
) -> mogh_error::Result<axum::response::Response> {
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let timer = Instant::now();
|
||||
let req_id = Uuid::new_v4();
|
||||
let method: ReadRequestMethod = (&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 | user: {}", user.username);
|
||||
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: {:#}",
|
||||
e.error
|
||||
);
|
||||
debug!("/read request {req_id} error: {:#}", e.error);
|
||||
}
|
||||
|
||||
let elapsed = timer.elapsed();
|
||||
debug!("/read request {req_id} | resolve time: {elapsed:?}");
|
||||
res.map(|res| res.0)
|
||||
}
|
||||
|
||||
@@ -340,20 +298,18 @@ impl Resolve<ReadArgs> for GetVersion {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &ReadArgs,
|
||||
) -> mogh_error::Result<GetVersionResponse> {
|
||||
) -> serror::Result<GetVersionResponse> {
|
||||
Ok(GetVersionResponse {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<ReadArgs> for GetCoreInfo {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &ReadArgs,
|
||||
) -> mogh_error::Result<GetCoreInfoResponse> {
|
||||
) -> serror::Result<GetCoreInfoResponse> {
|
||||
let config = core_config();
|
||||
let info = GetCoreInfoResponse {
|
||||
title: config.title.clone(),
|
||||
@@ -376,13 +332,11 @@ impl Resolve<ReadArgs> for GetCoreInfo {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<ReadArgs> for ListSecrets {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSecretsResponse> {
|
||||
) -> serror::Result<ListSecretsResponse> {
|
||||
let mut secrets = core_config()
|
||||
.secrets
|
||||
.keys()
|
||||
@@ -395,9 +349,7 @@ impl Resolve<ReadArgs> for ListSecrets {
|
||||
ResourceTarget::Builder(id) => {
|
||||
match resource::get::<Builder>(&id).await?.config {
|
||||
BuilderConfig::Url(_) => None,
|
||||
BuilderConfig::Server(config) => {
|
||||
config.server_ids.first().cloned()
|
||||
}
|
||||
BuilderConfig::Server(config) => Some(config.server_id),
|
||||
BuilderConfig::Aws(config) => {
|
||||
secrets.extend(config.secrets);
|
||||
None
|
||||
@@ -406,8 +358,7 @@ impl Resolve<ReadArgs> for ListSecrets {
|
||||
}
|
||||
_ => {
|
||||
return Err(
|
||||
anyhow!("target must be `Server` or `Builder`")
|
||||
.status_code(StatusCode::BAD_REQUEST),
|
||||
anyhow!("target must be `Server` or `Builder`").into(),
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -434,13 +385,11 @@ impl Resolve<ReadArgs> for ListSecrets {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<ReadArgs> for ListGitProvidersFromConfig {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListGitProvidersFromConfigResponse> {
|
||||
) -> serror::Result<ListGitProvidersFromConfigResponse> {
|
||||
let mut providers = core_config().git_providers.clone();
|
||||
|
||||
if let Some(target) = self.target {
|
||||
@@ -452,13 +401,11 @@ impl Resolve<ReadArgs> for ListGitProvidersFromConfig {
|
||||
match resource::get::<Builder>(&id).await?.config {
|
||||
BuilderConfig::Url(_) => {}
|
||||
BuilderConfig::Server(config) => {
|
||||
if let Some(server_id) = config.server_ids.first() {
|
||||
merge_git_providers_for_server(
|
||||
&mut providers,
|
||||
server_id,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
merge_git_providers_for_server(
|
||||
&mut providers,
|
||||
&config.server_id,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
BuilderConfig::Aws(config) => {
|
||||
merge_git_providers(
|
||||
@@ -470,8 +417,7 @@ impl Resolve<ReadArgs> for ListGitProvidersFromConfig {
|
||||
}
|
||||
_ => {
|
||||
return Err(
|
||||
anyhow!("target must be `Server` or `Builder`")
|
||||
.status_code(StatusCode::BAD_REQUEST),
|
||||
anyhow!("target must be `Server` or `Builder`").into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -480,24 +426,18 @@ impl Resolve<ReadArgs> for ListGitProvidersFromConfig {
|
||||
let (builds, repos, syncs) = tokio::try_join!(
|
||||
resource::list_full_for_user::<Build>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[]
|
||||
),
|
||||
resource::list_full_for_user::<Repo>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[]
|
||||
),
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[]
|
||||
@@ -547,37 +487,33 @@ impl Resolve<ReadArgs> for ListGitProvidersFromConfig {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<ReadArgs> for ListImageRegistriesFromConfig {
|
||||
impl Resolve<ReadArgs> for ListDockerRegistriesFromConfig {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &ReadArgs,
|
||||
) -> mogh_error::Result<ListImageRegistriesFromConfigResponse> {
|
||||
let mut registries = core_config().image_registries.clone();
|
||||
) -> serror::Result<ListDockerRegistriesFromConfigResponse> {
|
||||
let mut registries = core_config().docker_registries.clone();
|
||||
|
||||
if let Some(target) = self.target {
|
||||
match target {
|
||||
ResourceTarget::Server(id) => {
|
||||
merge_image_registries_for_server(&mut registries, &id)
|
||||
merge_docker_registries_for_server(&mut registries, &id)
|
||||
.await?;
|
||||
}
|
||||
ResourceTarget::Builder(id) => {
|
||||
match resource::get::<Builder>(&id).await?.config {
|
||||
BuilderConfig::Url(_) => {}
|
||||
BuilderConfig::Server(config) => {
|
||||
if let Some(server_id) = config.server_ids.first() {
|
||||
merge_image_registries_for_server(
|
||||
&mut registries,
|
||||
server_id,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
merge_docker_registries_for_server(
|
||||
&mut registries,
|
||||
&config.server_id,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
BuilderConfig::Aws(config) => {
|
||||
merge_image_registries(
|
||||
merge_docker_registries(
|
||||
&mut registries,
|
||||
config.image_registries,
|
||||
config.docker_registries,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -599,7 +535,7 @@ impl Resolve<ReadArgs> for ListImageRegistriesFromConfig {
|
||||
async fn merge_git_providers_for_server(
|
||||
providers: &mut Vec<GitProvider>,
|
||||
server_id: &str,
|
||||
) -> mogh_error::Result<()> {
|
||||
) -> serror::Result<()> {
|
||||
let server = resource::get::<Server>(server_id).await?;
|
||||
let more = periphery_client(&server)
|
||||
.await?
|
||||
@@ -635,14 +571,14 @@ fn merge_git_providers(
|
||||
}
|
||||
}
|
||||
|
||||
async fn merge_image_registries_for_server(
|
||||
registries: &mut Vec<ImageRegistry>,
|
||||
async fn merge_docker_registries_for_server(
|
||||
registries: &mut Vec<DockerRegistry>,
|
||||
server_id: &str,
|
||||
) -> mogh_error::Result<()> {
|
||||
) -> serror::Result<()> {
|
||||
let server = resource::get::<Server>(server_id).await?;
|
||||
let more = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery_client::api::ListImageRegistries {})
|
||||
.request(periphery_client::api::ListDockerRegistries {})
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
@@ -650,13 +586,13 @@ async fn merge_image_registries_for_server(
|
||||
server.name
|
||||
)
|
||||
})?;
|
||||
merge_image_registries(registries, more);
|
||||
merge_docker_registries(registries, more);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn merge_image_registries(
|
||||
registries: &mut Vec<ImageRegistry>,
|
||||
more: Vec<ImageRegistry>,
|
||||
fn merge_docker_registries(
|
||||
registries: &mut Vec<DockerRegistry>,
|
||||
more: Vec<DockerRegistry>,
|
||||
) {
|
||||
for incoming_registry in more {
|
||||
if let Some(registry) = registries
|
||||
|
||||
@@ -5,9 +5,9 @@ use database::mungos::find::find_collect;
|
||||
use komodo_client::api::read::{
|
||||
ListOnboardingKeys, ListOnboardingKeysResponse,
|
||||
};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
|
||||
use crate::{api::read::ReadArgs, state::db_client};
|
||||
|
||||
@@ -17,7 +17,7 @@ impl Resolve<ReadArgs> for ListOnboardingKeys {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user: admin }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListOnboardingKeysResponse> {
|
||||
) -> serror::Result<ListOnboardingKeysResponse> {
|
||||
if !admin.admin {
|
||||
return Err(
|
||||
anyhow!("This call is admin only")
|
||||
|
||||
@@ -8,7 +8,7 @@ use komodo_client::{
|
||||
},
|
||||
entities::permission::PermissionLevel,
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_user_permission_on_target, state::db_client,
|
||||
@@ -20,7 +20,7 @@ impl Resolve<ReadArgs> for ListPermissions {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListPermissionsResponse> {
|
||||
) -> serror::Result<ListPermissionsResponse> {
|
||||
let res = find_collect(
|
||||
&db_client().permissions,
|
||||
doc! {
|
||||
@@ -39,7 +39,7 @@ impl Resolve<ReadArgs> for GetPermission {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetPermissionResponse> {
|
||||
) -> serror::Result<GetPermissionResponse> {
|
||||
if user.admin {
|
||||
return Ok(PermissionLevel::Write.all());
|
||||
}
|
||||
@@ -51,7 +51,7 @@ impl Resolve<ReadArgs> for ListUserTargetPermissions {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListUserTargetPermissionsResponse> {
|
||||
) -> serror::Result<ListUserTargetPermissionsResponse> {
|
||||
if !user.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
|
||||
@@ -3,15 +3,13 @@ use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
permission::PermissionLevel,
|
||||
procedure::{
|
||||
Procedure, ProcedureListItem, ProcedureSortBy, ProcedureState,
|
||||
},
|
||||
procedure::{Procedure, ProcedureState},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::{get_all_tags, get_procedure_state},
|
||||
helpers::query::get_all_tags,
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
state::{action_states, procedure_state_cache},
|
||||
@@ -23,7 +21,7 @@ impl Resolve<ReadArgs> for GetProcedure {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetProcedureResponse> {
|
||||
) -> serror::Result<GetProcedureResponse> {
|
||||
Ok(
|
||||
get_check_permissions::<Procedure>(
|
||||
&self.procedure,
|
||||
@@ -39,43 +37,21 @@ impl Resolve<ReadArgs> for ListProcedures {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListProceduresResponse> {
|
||||
) -> serror::Result<ListProceduresResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<ProcedureListItem> =
|
||||
match self.sort_by {
|
||||
ProcedureSortBy::Name => resource::ListItemSort::Name,
|
||||
ProcedureSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
ProcedureSortBy::NextRun => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.next_scheduled_run.cmp(&b.info.next_scheduled_run)
|
||||
}))
|
||||
}
|
||||
};
|
||||
let procedures = resource::list_items_for_user::<Procedure>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|procedure| {
|
||||
states.is_empty() || states.contains(&procedure.info.state)
|
||||
},
|
||||
Ok(
|
||||
resource::list_for_user::<Procedure>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
Ok(procedures)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,35 +59,18 @@ impl Resolve<ReadArgs> for ListFullProcedures {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullProceduresResponse> {
|
||||
) -> serror::Result<ListFullProceduresResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user_filtered::<Procedure, _>(
|
||||
resource::list_full_for_user::<Procedure>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|procedure| {
|
||||
let states = states.clone();
|
||||
async move {
|
||||
if states.is_empty()
|
||||
|| states
|
||||
.contains(&get_procedure_state(&procedure.id).await)
|
||||
{
|
||||
Some(procedure)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -122,11 +81,9 @@ impl Resolve<ReadArgs> for GetProceduresSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetProceduresSummaryResponse> {
|
||||
) -> serror::Result<GetProceduresSummaryResponse> {
|
||||
let procedures = resource::list_full_for_user::<Procedure>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
@@ -170,7 +127,7 @@ impl Resolve<ReadArgs> for GetProcedureActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetProcedureActionStateResponse> {
|
||||
) -> serror::Result<GetProcedureActionStateResponse> {
|
||||
let procedure = get_check_permissions::<Procedure>(
|
||||
&self.procedure,
|
||||
user,
|
||||
|
||||
@@ -5,7 +5,7 @@ use database::mungos::{
|
||||
mongodb::options::FindOptions,
|
||||
};
|
||||
use komodo_client::api::read::*;
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::state::db_client;
|
||||
|
||||
@@ -15,7 +15,7 @@ impl Resolve<ReadArgs> for GetGitProviderAccount {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetGitProviderAccountResponse> {
|
||||
) -> serror::Result<GetGitProviderAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can read git provider accounts").into(),
|
||||
@@ -35,7 +35,7 @@ impl Resolve<ReadArgs> for ListGitProviderAccounts {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListGitProviderAccountsResponse> {
|
||||
) -> serror::Result<ListGitProviderAccountsResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can read git provider accounts").into(),
|
||||
@@ -61,11 +61,11 @@ impl Resolve<ReadArgs> for ListGitProviderAccounts {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for GetImageRegistryAccount {
|
||||
impl Resolve<ReadArgs> for GetDockerRegistryAccount {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetImageRegistryAccountResponse> {
|
||||
) -> serror::Result<GetDockerRegistryAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can read docker registry accounts")
|
||||
@@ -83,11 +83,11 @@ impl Resolve<ReadArgs> for GetImageRegistryAccount {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListImageRegistryAccounts {
|
||||
impl Resolve<ReadArgs> for ListDockerRegistryAccounts {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListImageRegistryAccountsResponse> {
|
||||
) -> serror::Result<ListDockerRegistryAccountsResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can read docker registry accounts")
|
||||
|
||||
@@ -3,12 +3,10 @@ use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
permission::PermissionLevel,
|
||||
repo::{
|
||||
Repo, RepoActionState, RepoListItem, RepoSortBy, RepoState,
|
||||
},
|
||||
repo::{Repo, RepoActionState, RepoListItem, RepoState},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags,
|
||||
@@ -23,7 +21,7 @@ impl Resolve<ReadArgs> for GetRepo {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Repo> {
|
||||
) -> serror::Result<Repo> {
|
||||
Ok(
|
||||
get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
@@ -39,42 +37,21 @@ impl Resolve<ReadArgs> for ListRepos {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<RepoListItem>> {
|
||||
) -> serror::Result<Vec<RepoListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<RepoListItem> =
|
||||
match self.sort_by {
|
||||
RepoSortBy::Name => resource::ListItemSort::Name,
|
||||
RepoSortBy::Repo => {
|
||||
resource::ListItemSort::DbField("config.repo")
|
||||
}
|
||||
RepoSortBy::Branch => {
|
||||
resource::ListItemSort::DbField("config.branch")
|
||||
}
|
||||
RepoSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
};
|
||||
let repos = resource::list_items_for_user::<Repo>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|repo| states.is_empty() || states.contains(&repo.info.state),
|
||||
Ok(
|
||||
resource::list_for_user::<Repo>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
Ok(repos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,35 +59,18 @@ impl Resolve<ReadArgs> for ListFullRepos {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullReposResponse> {
|
||||
) -> serror::Result<ListFullReposResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user_filtered::<Repo, _>(
|
||||
resource::list_full_for_user::<Repo>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|repo| {
|
||||
let states = states.clone();
|
||||
async move {
|
||||
if states.is_empty()
|
||||
|| states
|
||||
.contains(&resource::get_repo_state(&repo.id).await)
|
||||
{
|
||||
Some(repo)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -121,7 +81,7 @@ impl Resolve<ReadArgs> for GetRepoActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<RepoActionState> {
|
||||
) -> serror::Result<RepoActionState> {
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.repo,
|
||||
user,
|
||||
@@ -142,11 +102,9 @@ impl Resolve<ReadArgs> for GetReposSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetReposSummaryResponse> {
|
||||
) -> serror::Result<GetReposSummaryResponse> {
|
||||
let repos = resource::list_full_for_user::<Repo>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
|
||||
@@ -10,7 +10,7 @@ use komodo_client::{
|
||||
schedule::Schedule,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::{get_all_tags, get_last_run_at},
|
||||
@@ -24,31 +24,29 @@ impl Resolve<ReadArgs> for ListSchedules {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<Schedule>> {
|
||||
) -> serror::Result<Vec<Schedule>> {
|
||||
let all_tags = get_all_tags(None).await?;
|
||||
let (actions, procedures) = tokio::try_join!(
|
||||
list_full_for_user::<Action>(
|
||||
ResourceQuery {
|
||||
names: Default::default(),
|
||||
templates: TemplatesQueryBehavior::Include,
|
||||
tag_behavior: self.tag_behavior,
|
||||
tags: self.tags.clone(),
|
||||
..Default::default()
|
||||
specific: Default::default(),
|
||||
},
|
||||
None,
|
||||
None,
|
||||
&args.user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
),
|
||||
list_full_for_user::<Procedure>(
|
||||
ResourceQuery {
|
||||
names: Default::default(),
|
||||
templates: TemplatesQueryBehavior::Include,
|
||||
tag_behavior: self.tag_behavior,
|
||||
tags: self.tags.clone(),
|
||||
..Default::default()
|
||||
specific: Default::default(),
|
||||
},
|
||||
None,
|
||||
None,
|
||||
&args.user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
cmp,
|
||||
collections::HashMap,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
@@ -14,27 +15,43 @@ use database::mungos::{
|
||||
use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
ResourceTarget,
|
||||
deployment::Deployment,
|
||||
docker::{
|
||||
container::{
|
||||
Container, ContainerListItem, ContainerStateStatusEnum,
|
||||
},
|
||||
image::{Image, ImageHistoryResponseItem},
|
||||
network::Network,
|
||||
volume::Volume,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
server::{
|
||||
Server, ServerActionState, ServerListItem, ServerSortBy,
|
||||
Server, ServerActionState, ServerListItem, ServerQuery,
|
||||
ServerState,
|
||||
},
|
||||
stack::{Stack, StackServiceNames},
|
||||
stats::{SystemInformation, SystemProcess},
|
||||
update::Log,
|
||||
},
|
||||
};
|
||||
use periphery_client::api::{
|
||||
self as periphery,
|
||||
container::InspectContainer,
|
||||
docker::{
|
||||
ImageHistory, InspectImage, InspectNetwork, InspectVolume,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCode;
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api as periphery;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCode;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
periphery_client,
|
||||
query::{get_all_tags, get_cached_server_state},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
helpers::{periphery_client, query::get_all_tags},
|
||||
permission::{get_check_permissions, list_resources_for_user},
|
||||
resource,
|
||||
stack::compose_container_match_regex,
|
||||
state::{action_states, db_client, server_status_cache},
|
||||
};
|
||||
|
||||
@@ -44,11 +61,9 @@ impl Resolve<ReadArgs> for GetServersSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetServersSummaryResponse> {
|
||||
) -> serror::Result<GetServersSummaryResponse> {
|
||||
let servers = resource::list_for_user::<Server>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
@@ -88,7 +103,7 @@ impl Resolve<ReadArgs> for GetServer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Server> {
|
||||
) -> serror::Result<Server> {
|
||||
Ok(
|
||||
get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
@@ -104,46 +119,21 @@ impl Resolve<ReadArgs> for ListServers {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<ServerListItem>> {
|
||||
) -> serror::Result<Vec<ServerListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<ServerListItem> =
|
||||
match self.sort_by {
|
||||
ServerSortBy::Name => resource::ListItemSort::Name,
|
||||
ServerSortBy::Region => {
|
||||
resource::ListItemSort::DbField("config.region")
|
||||
}
|
||||
ServerSortBy::Version => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.version.cmp(&b.info.version)
|
||||
}))
|
||||
}
|
||||
ServerSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
};
|
||||
let servers = resource::list_items_for_user::<Server>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|server| {
|
||||
states.is_empty() || states.contains(&server.info.state)
|
||||
},
|
||||
Ok(
|
||||
resource::list_for_user::<Server>(
|
||||
self.query,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
.await?;
|
||||
Ok(servers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,35 +141,18 @@ impl Resolve<ReadArgs> for ListFullServers {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullServersResponse> {
|
||||
) -> serror::Result<ListFullServersResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user_filtered::<Server, _>(
|
||||
resource::list_full_for_user::<Server>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|server| {
|
||||
let states = states.clone();
|
||||
async move {
|
||||
if states.is_empty()
|
||||
|| states
|
||||
.contains(&get_cached_server_state(&server.id).await)
|
||||
{
|
||||
Some(server)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -190,7 +163,7 @@ impl Resolve<ReadArgs> for GetServerState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetServerStateResponse> {
|
||||
) -> serror::Result<GetServerStateResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -212,7 +185,7 @@ impl Resolve<ReadArgs> for GetServerActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ServerActionState> {
|
||||
) -> serror::Result<ServerActionState> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -233,7 +206,7 @@ impl Resolve<ReadArgs> for GetPeripheryInformation {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetPeripheryInformationResponse> {
|
||||
) -> serror::Result<GetPeripheryInformationResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -256,7 +229,7 @@ impl Resolve<ReadArgs> for GetSystemInformation {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<SystemInformation> {
|
||||
) -> serror::Result<SystemInformation> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -280,7 +253,7 @@ impl Resolve<ReadArgs> for GetSystemStats {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetSystemStatsResponse> {
|
||||
) -> serror::Result<GetSystemStatsResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -312,7 +285,7 @@ impl Resolve<ReadArgs> for ListSystemProcesses {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSystemProcessesResponse> {
|
||||
) -> serror::Result<ListSystemProcessesResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -347,7 +320,7 @@ impl Resolve<ReadArgs> for GetHistoricalServerStats {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetHistoricalServerStatsResponse> {
|
||||
) -> serror::Result<GetHistoricalServerStatsResponse> {
|
||||
let GetHistoricalServerStats {
|
||||
server,
|
||||
granularity,
|
||||
@@ -380,7 +353,7 @@ impl Resolve<ReadArgs> for GetHistoricalServerStats {
|
||||
},
|
||||
FindOptions::builder()
|
||||
.sort(doc! { "ts": -1 })
|
||||
.skip((page as u64).saturating_mul(STATS_PER_PAGE as u64))
|
||||
.skip(page as u64 * STATS_PER_PAGE as u64)
|
||||
.limit(STATS_PER_PAGE)
|
||||
.build(),
|
||||
)
|
||||
@@ -395,3 +368,512 @@ impl Resolve<ReadArgs> for GetHistoricalServerStats {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListDockerContainers {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerContainersResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.containers.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListAllDockerContainers {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListAllDockerContainersResponse> {
|
||||
let servers = resource::list_for_user::<Server>(
|
||||
ServerQuery::builder().names(self.servers.clone()).build(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut containers = Vec::<ContainerListItem>::new();
|
||||
|
||||
for server in servers {
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
let Some(docker) = &cache.docker else {
|
||||
continue;
|
||||
};
|
||||
let more = docker
|
||||
.containers
|
||||
.iter()
|
||||
.filter(|container| {
|
||||
self.containers.is_empty()
|
||||
|| self.containers.contains(&container.name)
|
||||
})
|
||||
.cloned();
|
||||
containers.extend(more);
|
||||
}
|
||||
|
||||
Ok(containers)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for GetDockerContainersSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetDockerContainersSummaryResponse> {
|
||||
let servers = resource::list_full_for_user::<Server>(
|
||||
Default::default(),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
.context("failed to get servers from db")?;
|
||||
|
||||
let mut res = GetDockerContainersSummaryResponse::default();
|
||||
|
||||
for server in servers {
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
|
||||
if let Some(docker) = &cache.docker {
|
||||
for container in &docker.containers {
|
||||
res.total += 1;
|
||||
match container.state {
|
||||
ContainerStateStatusEnum::Created
|
||||
| ContainerStateStatusEnum::Paused
|
||||
| ContainerStateStatusEnum::Exited => res.stopped += 1,
|
||||
ContainerStateStatusEnum::Running => res.running += 1,
|
||||
ContainerStateStatusEnum::Empty => res.unknown += 1,
|
||||
_ => res.unhealthy += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectDockerContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Container> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.inspect(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot inspect container: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(InspectContainer {
|
||||
name: self.container,
|
||||
})
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_LOG_LENGTH: u64 = 5000;
|
||||
|
||||
impl Resolve<ReadArgs> for GetContainerLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Log> {
|
||||
let GetContainerLog {
|
||||
server,
|
||||
container,
|
||||
tail,
|
||||
timestamps,
|
||||
} = self;
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::container::GetContainerLog {
|
||||
name: container,
|
||||
tail: cmp::min(tail, MAX_LOG_LENGTH),
|
||||
timestamps,
|
||||
})
|
||||
.await
|
||||
.context("failed at call to periphery")?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for SearchContainerLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Log> {
|
||||
let SearchContainerLog {
|
||||
server,
|
||||
container,
|
||||
terms,
|
||||
combinator,
|
||||
invert,
|
||||
timestamps,
|
||||
} = self;
|
||||
let server = get_check_permissions::<Server>(
|
||||
&server,
|
||||
user,
|
||||
PermissionLevel::Read.logs(),
|
||||
)
|
||||
.await?;
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(periphery::container::GetContainerLogSearch {
|
||||
name: container,
|
||||
terms,
|
||||
combinator,
|
||||
invert,
|
||||
timestamps,
|
||||
})
|
||||
.await
|
||||
.context("failed at call to periphery")?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for GetResourceMatchingContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<GetResourceMatchingContainerResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
// first check deployments
|
||||
if let Ok(deployment) =
|
||||
resource::get::<Deployment>(&self.container).await
|
||||
{
|
||||
return Ok(GetResourceMatchingContainerResponse {
|
||||
resource: ResourceTarget::Deployment(deployment.id).into(),
|
||||
});
|
||||
}
|
||||
|
||||
// then check stacks
|
||||
let stacks = list_resources_for_user::<Stack>(
|
||||
doc! { "config.server_id": &server.id },
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// check matching stack
|
||||
for stack in stacks {
|
||||
for StackServiceNames {
|
||||
service_name,
|
||||
container_name,
|
||||
..
|
||||
} in stack
|
||||
.info
|
||||
.deployed_services
|
||||
.unwrap_or(stack.info.latest_services)
|
||||
{
|
||||
let is_match = match compose_container_match_regex(&container_name)
|
||||
.with_context(|| format!("failed to construct container name matching regex for service {service_name}"))
|
||||
{
|
||||
Ok(regex) => regex,
|
||||
Err(e) => {
|
||||
warn!("{e:#}");
|
||||
continue;
|
||||
}
|
||||
}.is_match(&self.container);
|
||||
|
||||
if is_match {
|
||||
return Ok(GetResourceMatchingContainerResponse {
|
||||
resource: ResourceTarget::Stack(stack.id).into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(GetResourceMatchingContainerResponse { resource: None })
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListDockerNetworks {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerNetworksResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.networks.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectDockerNetwork {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Network> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot inspect network: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(InspectNetwork { name: self.network })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListDockerImages {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerImagesResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.images.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectDockerImage {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Image> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!("Cannot inspect image: server is {:?}", cache.state)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(InspectImage { name: self.image })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListDockerImageHistory {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Vec<ImageHistoryResponseItem>> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Cannot get image history: server is {:?}",
|
||||
cache.state
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(ImageHistory { name: self.image })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListDockerVolumes {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListDockerVolumesResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.volumes.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for InspectDockerVolume {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<Volume> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if cache.state != ServerState::Ok {
|
||||
return Err(
|
||||
anyhow!("Cannot inspect volume: server is {:?}", cache.state)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
let res = periphery_client(&server)
|
||||
.await?
|
||||
.request(InspectVolume { name: self.volume })
|
||||
.await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListComposeProjects {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> serror::Result<ListComposeProjectsResponse> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
)
|
||||
.await?;
|
||||
let cache = server_status_cache()
|
||||
.get_or_insert_default(&server.id)
|
||||
.await;
|
||||
if let Some(docker) = &cache.docker {
|
||||
Ok(docker.projects.clone())
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl Resolve<ReadArgs> for ListAllTerminals {
|
||||
// async fn resolve(
|
||||
// self,
|
||||
// args: &ReadArgs,
|
||||
// ) -> Result<Self::Response, Self::Error> {
|
||||
// // match self.tar
|
||||
// let mut terminals = resource::list_full_for_user::<Server>(
|
||||
// self.query, &args.user, &all_tags,
|
||||
// )
|
||||
// .await?
|
||||
// .into_iter()
|
||||
// .map(|server| async move {
|
||||
// (
|
||||
// list_terminals_inner(&server, self.fresh).await,
|
||||
// (server.id, server.name),
|
||||
// )
|
||||
// })
|
||||
// .collect::<FuturesUnordered<_>>()
|
||||
// .collect::<Vec<_>>()
|
||||
// .await
|
||||
// .into_iter()
|
||||
// .flat_map(|(terminals, server)| {
|
||||
// let terminals = terminals.ok()?;
|
||||
// Some((terminals, server))
|
||||
// })
|
||||
// .flat_map(|(terminals, (server_id, server_name))| {
|
||||
// terminals.into_iter().map(move |info| {
|
||||
// TerminalInfoWithServer::from_terminal_info(
|
||||
// &server_id,
|
||||
// &server_name,
|
||||
// info,
|
||||
// )
|
||||
// })
|
||||
// })
|
||||
// .collect::<Vec<_>>();
|
||||
|
||||
// terminals.sort_by(|a, b| {
|
||||
// a.server_name.cmp(&b.server_name).then(a.name.cmp(&b.name))
|
||||
// });
|
||||
|
||||
// Ok(terminals)
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -9,25 +9,20 @@ use komodo_client::{
|
||||
container::Container, service::SwarmService, stack::SwarmStack,
|
||||
},
|
||||
permission::PermissionLevel,
|
||||
stack::{
|
||||
Stack, StackActionState, StackListItem, StackQuery,
|
||||
StackService, StackSortBy, StackState,
|
||||
},
|
||||
stack::{Stack, StackActionState, StackListItem, StackState},
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError as _;
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api::{
|
||||
compose::{GetComposeLog, GetComposeLogSearch},
|
||||
container::InspectContainer,
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError as _;
|
||||
|
||||
use crate::{
|
||||
helpers::{
|
||||
periphery_client,
|
||||
query::{get_all_tags, get_cached_stack_state},
|
||||
swarm::swarm_request,
|
||||
periphery_client, query::get_all_tags, swarm::swarm_request,
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource,
|
||||
@@ -41,7 +36,7 @@ impl Resolve<ReadArgs> for GetStack {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Stack> {
|
||||
) -> serror::Result<Stack> {
|
||||
Ok(
|
||||
get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
@@ -57,7 +52,7 @@ impl Resolve<ReadArgs> for ListStackServices {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListStackServicesResponse> {
|
||||
) -> serror::Result<ListStackServicesResponse> {
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
@@ -77,80 +72,11 @@ impl Resolve<ReadArgs> for ListStackServices {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListAllStackServices {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListStackServicesResponse> {
|
||||
let all_tags = if self.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let stacks = resource::list_for_user::<Stack>(
|
||||
StackQuery::builder()
|
||||
.names(self.stacks.clone())
|
||||
.tags(self.tags)
|
||||
.build(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut services = Vec::<StackService>::new();
|
||||
let mut skipped = 0;
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let limit_usize = limit as usize;
|
||||
// Eg. page 1 skips until after 100 services, page 2 after 200.
|
||||
let skip = limit.saturating_mul(self.page);
|
||||
// Match terms case insensitively.
|
||||
let terms = self
|
||||
.terms
|
||||
.iter()
|
||||
.map(|term| term.to_lowercase())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for stack in stacks {
|
||||
let cache =
|
||||
stack_status_cache().get_or_insert_default(&stack.id).await;
|
||||
let more = cache.curr.services
|
||||
.iter()
|
||||
.filter(|service| {
|
||||
// Apply state filter if defined.
|
||||
(self.state.is_empty() || self.state.contains(&service.state)) &&
|
||||
// Apply terms filter if defined
|
||||
(terms.is_empty()
|
||||
// Match when all terms contained within a name.
|
||||
|| {
|
||||
let name = service.service.to_lowercase();
|
||||
terms.iter().all(|term| name.contains(term))
|
||||
})
|
||||
});
|
||||
for service in more {
|
||||
if skipped < skip {
|
||||
skipped += 1;
|
||||
} else {
|
||||
// push and maybe early return
|
||||
services.push(service.clone());
|
||||
if limit > 0 && services.len() >= limit_usize {
|
||||
return Ok(services);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(services)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for GetStackLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetStackLogResponse> {
|
||||
) -> serror::Result<GetStackLogResponse> {
|
||||
let GetStackLog {
|
||||
stack,
|
||||
mut services,
|
||||
@@ -164,10 +90,7 @@ impl Resolve<ReadArgs> for GetStackLog {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
let log = match swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
let service = services.pop().context(
|
||||
"Must pass single service for Swarm mode Stack logs",
|
||||
@@ -210,7 +133,7 @@ impl Resolve<ReadArgs> for SearchStackLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<SearchStackLogResponse> {
|
||||
) -> serror::Result<SearchStackLogResponse> {
|
||||
let SearchStackLog {
|
||||
stack,
|
||||
mut services,
|
||||
@@ -226,10 +149,7 @@ impl Resolve<ReadArgs> for SearchStackLog {
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
let log = match swarm_or_server {
|
||||
SwarmOrServer::None => unreachable!(),
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
let service = services.pop().context(
|
||||
"Must pass single service for Swarm mode Stack logs",
|
||||
@@ -272,7 +192,7 @@ impl Resolve<ReadArgs> for InspectStackContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Container> {
|
||||
) -> serror::Result<Container> {
|
||||
let InspectStackContainer { stack, service } = self;
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
&stack,
|
||||
@@ -321,7 +241,7 @@ impl Resolve<ReadArgs> for InspectStackSwarmService {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<SwarmService> {
|
||||
) -> serror::Result<SwarmService> {
|
||||
let InspectStackSwarmService { stack, service } = self;
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
&stack,
|
||||
@@ -372,7 +292,7 @@ impl Resolve<ReadArgs> for InspectStackSwarmInfo {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<SwarmStack> {
|
||||
) -> serror::Result<SwarmStack> {
|
||||
let (stack, swarm_or_server) = setup_stack_execution(
|
||||
&self.stack,
|
||||
user,
|
||||
@@ -405,7 +325,7 @@ impl Resolve<ReadArgs> for ListCommonStackExtraArgs {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListCommonStackExtraArgsResponse> {
|
||||
) -> serror::Result<ListCommonStackExtraArgsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
@@ -413,8 +333,6 @@ impl Resolve<ReadArgs> for ListCommonStackExtraArgs {
|
||||
};
|
||||
let stacks = resource::list_full_for_user::<Stack>(
|
||||
self.query,
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
@@ -441,7 +359,7 @@ impl Resolve<ReadArgs> for ListCommonStackBuildExtraArgs {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListCommonStackBuildExtraArgsResponse> {
|
||||
) -> serror::Result<ListCommonStackBuildExtraArgsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
@@ -449,8 +367,6 @@ impl Resolve<ReadArgs> for ListCommonStackBuildExtraArgs {
|
||||
};
|
||||
let stacks = resource::list_full_for_user::<Stack>(
|
||||
self.query,
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
@@ -477,64 +393,34 @@ impl Resolve<ReadArgs> for ListStacks {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<StackListItem>> {
|
||||
) -> serror::Result<Vec<StackListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let only_update_available = self.query.specific.update_available;
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<StackListItem> =
|
||||
match self.sort_by {
|
||||
StackSortBy::Name => resource::ListItemSort::Name,
|
||||
StackSortBy::Source => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.repo.cmp(&b.info.repo)
|
||||
}))
|
||||
}
|
||||
StackSortBy::Host => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
let host_a = if a.info.swarm_id.is_empty() {
|
||||
&a.info.server_name
|
||||
} else {
|
||||
&a.info.swarm_name
|
||||
};
|
||||
let host_b = if b.info.swarm_id.is_empty() {
|
||||
&b.info.server_name
|
||||
} else {
|
||||
&b.info.swarm_name
|
||||
};
|
||||
host_a.cmp(host_b)
|
||||
}))
|
||||
}
|
||||
StackSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
};
|
||||
let stacks = resource::list_items_for_user::<Stack>(
|
||||
let stacks = resource::list_for_user::<Stack>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|stack| {
|
||||
(!only_update_available
|
||||
|| stack
|
||||
)
|
||||
.await?;
|
||||
let stacks = if only_update_available {
|
||||
stacks
|
||||
.into_iter()
|
||||
.filter(|stack| {
|
||||
stack
|
||||
.info
|
||||
.services
|
||||
.iter()
|
||||
.any(|service| service.update_available))
|
||||
&& (states.is_empty() || states.contains(&stack.info.state))
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
.any(|service| service.update_available)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
stacks
|
||||
};
|
||||
Ok(stacks)
|
||||
}
|
||||
}
|
||||
@@ -543,35 +429,18 @@ impl Resolve<ReadArgs> for ListFullStacks {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullStacksResponse> {
|
||||
) -> serror::Result<ListFullStacksResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let states = self.query.specific.states.clone();
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user_filtered::<Stack, _>(
|
||||
resource::list_full_for_user::<Stack>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|stack| {
|
||||
let states = states.clone();
|
||||
async move {
|
||||
if states.is_empty()
|
||||
|| states
|
||||
.contains(&get_cached_stack_state(&stack.id).await)
|
||||
{
|
||||
Some(stack)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -582,7 +451,7 @@ impl Resolve<ReadArgs> for GetStackActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<StackActionState> {
|
||||
) -> serror::Result<StackActionState> {
|
||||
let stack = get_check_permissions::<Stack>(
|
||||
&self.stack,
|
||||
user,
|
||||
@@ -603,11 +472,9 @@ impl Resolve<ReadArgs> for GetStacksSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetStacksSummaryResponse> {
|
||||
) -> serror::Result<GetStacksSummaryResponse> {
|
||||
let stacks = resource::list_full_for_user::<Stack>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
|
||||
@@ -3,12 +3,10 @@ use komodo_client::{
|
||||
api::read::*,
|
||||
entities::{
|
||||
permission::PermissionLevel,
|
||||
swarm::{
|
||||
Swarm, SwarmActionState, SwarmListItem, SwarmSortBy, SwarmState,
|
||||
},
|
||||
swarm::{Swarm, SwarmActionState, SwarmListItem, SwarmState},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::{query::get_all_tags, swarm::swarm_request},
|
||||
@@ -23,7 +21,7 @@ impl Resolve<ReadArgs> for GetSwarm {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Swarm> {
|
||||
) -> serror::Result<Swarm> {
|
||||
Ok(
|
||||
get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
@@ -39,33 +37,18 @@ impl Resolve<ReadArgs> for ListSwarms {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<SwarmListItem>> {
|
||||
) -> serror::Result<Vec<SwarmListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<SwarmListItem> =
|
||||
match self.sort_by {
|
||||
SwarmSortBy::Name => resource::ListItemSort::Name,
|
||||
SwarmSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
};
|
||||
Ok(
|
||||
resource::list_items_for_user::<Swarm>(
|
||||
resource::list_for_user::<Swarm>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|_| true,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -76,18 +59,15 @@ impl Resolve<ReadArgs> for ListFullSwarms {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullSwarmsResponse> {
|
||||
) -> serror::Result<ListFullSwarmsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user::<Swarm>(
|
||||
self.query,
|
||||
limit as i64,
|
||||
self.page.saturating_mul(limit),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
@@ -101,7 +81,7 @@ impl Resolve<ReadArgs> for GetSwarmActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<SwarmActionState> {
|
||||
) -> serror::Result<SwarmActionState> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -122,11 +102,9 @@ impl Resolve<ReadArgs> for GetSwarmsSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetSwarmsSummaryResponse> {
|
||||
) -> serror::Result<GetSwarmsSummaryResponse> {
|
||||
let swarms = resource::list_full_for_user::<Swarm>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
@@ -156,9 +134,6 @@ impl Resolve<ReadArgs> for GetSwarmsSummary {
|
||||
SwarmState::Unhealthy => {
|
||||
res.unhealthy += 1;
|
||||
}
|
||||
SwarmState::Down => {
|
||||
res.down += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +145,7 @@ impl Resolve<ReadArgs> for InspectSwarm {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<InspectSwarmResponse> {
|
||||
) -> serror::Result<InspectSwarmResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -192,7 +167,7 @@ impl Resolve<ReadArgs> for ListSwarmNodes {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSwarmNodesResponse> {
|
||||
) -> serror::Result<ListSwarmNodesResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -213,7 +188,7 @@ impl Resolve<ReadArgs> for InspectSwarmNode {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<InspectSwarmNodeResponse> {
|
||||
) -> serror::Result<InspectSwarmNodeResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -235,7 +210,7 @@ impl Resolve<ReadArgs> for ListSwarmServices {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSwarmServicesResponse> {
|
||||
) -> serror::Result<ListSwarmServicesResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -256,7 +231,7 @@ impl Resolve<ReadArgs> for InspectSwarmService {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<InspectSwarmServiceResponse> {
|
||||
) -> serror::Result<InspectSwarmServiceResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -278,7 +253,7 @@ impl Resolve<ReadArgs> for GetSwarmServiceLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetSwarmServiceLogResponse> {
|
||||
) -> serror::Result<GetSwarmServiceLogResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -305,7 +280,7 @@ impl Resolve<ReadArgs> for SearchSwarmServiceLog {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<SearchSwarmServiceLogResponse> {
|
||||
) -> serror::Result<SearchSwarmServiceLogResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -334,7 +309,7 @@ impl Resolve<ReadArgs> for ListSwarmTasks {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSwarmTasksResponse> {
|
||||
) -> serror::Result<ListSwarmTasksResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -355,7 +330,7 @@ impl Resolve<ReadArgs> for InspectSwarmTask {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<InspectSwarmTaskResponse> {
|
||||
) -> serror::Result<InspectSwarmTaskResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -377,7 +352,7 @@ impl Resolve<ReadArgs> for ListSwarmSecrets {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSwarmSecretsResponse> {
|
||||
) -> serror::Result<ListSwarmSecretsResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -398,7 +373,7 @@ impl Resolve<ReadArgs> for InspectSwarmSecret {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<InspectSwarmSecretResponse> {
|
||||
) -> serror::Result<InspectSwarmSecretResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -420,7 +395,7 @@ impl Resolve<ReadArgs> for ListSwarmConfigs {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSwarmConfigsResponse> {
|
||||
) -> serror::Result<ListSwarmConfigsResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -441,7 +416,7 @@ impl Resolve<ReadArgs> for InspectSwarmConfig {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<InspectSwarmConfigResponse> {
|
||||
) -> serror::Result<InspectSwarmConfigResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -463,7 +438,7 @@ impl Resolve<ReadArgs> for ListSwarmStacks {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListSwarmStacksResponse> {
|
||||
) -> serror::Result<ListSwarmStacksResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
@@ -484,7 +459,7 @@ impl Resolve<ReadArgs> for InspectSwarmStack {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<InspectSwarmStackResponse> {
|
||||
) -> serror::Result<InspectSwarmStackResponse> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
|
||||
@@ -5,11 +5,10 @@ use komodo_client::{
|
||||
permission::PermissionLevel,
|
||||
sync::{
|
||||
ResourceSync, ResourceSyncActionState, ResourceSyncListItem,
|
||||
ResourceSyncSortBy,
|
||||
},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::get_all_tags, permission::get_check_permissions,
|
||||
@@ -22,7 +21,7 @@ impl Resolve<ReadArgs> for GetResourceSync {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ResourceSync> {
|
||||
) -> serror::Result<ResourceSync> {
|
||||
Ok(
|
||||
get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
@@ -38,43 +37,18 @@ impl Resolve<ReadArgs> for ListResourceSyncs {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<ResourceSyncListItem>> {
|
||||
) -> serror::Result<Vec<ResourceSyncListItem>> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
let sort_by: resource::ListItemSort<ResourceSyncListItem> =
|
||||
match self.sort_by {
|
||||
ResourceSyncSortBy::Name => resource::ListItemSort::Name,
|
||||
ResourceSyncSortBy::Source => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.repo.cmp(&b.info.repo)
|
||||
}))
|
||||
}
|
||||
ResourceSyncSortBy::Branch => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.branch.cmp(&b.info.branch)
|
||||
}))
|
||||
}
|
||||
ResourceSyncSortBy::State => {
|
||||
resource::ListItemSort::InMemory(Box::new(|a, b| {
|
||||
a.info.state.to_string().cmp(&b.info.state.to_string())
|
||||
}))
|
||||
}
|
||||
};
|
||||
Ok(
|
||||
resource::list_items_for_user::<ResourceSync>(
|
||||
resource::list_for_user::<ResourceSync>(
|
||||
self.query,
|
||||
limit,
|
||||
self.page,
|
||||
self.sort_desc,
|
||||
sort_by,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
|_| true,
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
@@ -85,18 +59,15 @@ impl Resolve<ReadArgs> for ListFullResourceSyncs {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListFullResourceSyncsResponse> {
|
||||
) -> serror::Result<ListFullResourceSyncsResponse> {
|
||||
let all_tags = if self.query.tags.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
get_all_tags(None).await?
|
||||
};
|
||||
let limit = self.limit.unwrap_or(DEFAULT_LIST_LIMIT);
|
||||
Ok(
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
self.query,
|
||||
limit as i64,
|
||||
self.page.saturating_mul(limit),
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&all_tags,
|
||||
@@ -110,7 +81,7 @@ impl Resolve<ReadArgs> for GetResourceSyncActionState {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ResourceSyncActionState> {
|
||||
) -> serror::Result<ResourceSyncActionState> {
|
||||
let sync = get_check_permissions::<ResourceSync>(
|
||||
&self.sync,
|
||||
user,
|
||||
@@ -131,12 +102,10 @@ impl Resolve<ReadArgs> for GetResourceSyncsSummary {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetResourceSyncsSummaryResponse> {
|
||||
) -> serror::Result<GetResourceSyncsSummaryResponse> {
|
||||
let resource_syncs =
|
||||
resource::list_full_for_user::<ResourceSync>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.into(),
|
||||
&[],
|
||||
@@ -151,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())
|
||||
|
||||
@@ -7,23 +7,20 @@ use komodo_client::{
|
||||
api::read::{GetTag, ListTags},
|
||||
entities::tag::Tag,
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{helpers::query::get_tag, state::db_client};
|
||||
|
||||
use super::ReadArgs;
|
||||
|
||||
impl Resolve<ReadArgs> for GetTag {
|
||||
async fn resolve(self, _: &ReadArgs) -> mogh_error::Result<Tag> {
|
||||
async fn resolve(self, _: &ReadArgs) -> serror::Result<Tag> {
|
||||
Ok(get_tag(&self.tag).await?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ReadArgs> for ListTags {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &ReadArgs,
|
||||
) -> mogh_error::Result<Vec<Tag>> {
|
||||
async fn resolve(self, _: &ReadArgs) -> serror::Result<Vec<Tag>> {
|
||||
let res = find_collect(
|
||||
&db_client().tags,
|
||||
self.query,
|
||||
|
||||
@@ -13,9 +13,9 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCode;
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCode;
|
||||
|
||||
use crate::{
|
||||
helpers::periphery_client, permission::get_check_permissions,
|
||||
@@ -30,7 +30,7 @@ impl Resolve<ReadArgs> for ListTerminals {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListTerminalsResponse> {
|
||||
) -> serror::Result<ListTerminalsResponse> {
|
||||
let Some(target) = self.target else {
|
||||
return list_all_terminals_for_user(user, self.use_names).await;
|
||||
};
|
||||
@@ -88,12 +88,10 @@ impl Resolve<ReadArgs> for ListTerminals {
|
||||
async fn list_all_terminals_for_user(
|
||||
user: &User,
|
||||
use_names: bool,
|
||||
) -> mogh_error::Result<Vec<Terminal>> {
|
||||
) -> serror::Result<Vec<Terminal>> {
|
||||
let (mut servers, stacks, deployments) = tokio::try_join!(
|
||||
resource::list_full_for_user::<Server>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.terminal(),
|
||||
&[]
|
||||
@@ -105,16 +103,12 @@ async fn list_all_terminals_for_user(
|
||||
.collect::<Vec<_>>())),
|
||||
resource::list_full_for_user::<Stack>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.terminal(),
|
||||
&[]
|
||||
),
|
||||
resource::list_full_for_user::<Deployment>(
|
||||
Default::default(),
|
||||
None,
|
||||
None,
|
||||
user,
|
||||
PermissionLevel::Read.terminal(),
|
||||
&[]
|
||||
@@ -174,7 +168,6 @@ async fn list_all_terminals_for_user(
|
||||
server_id.clone()
|
||||
}),
|
||||
};
|
||||
terminal.target_name = Some(server_name.clone());
|
||||
terminal
|
||||
}),
|
||||
TerminalTarget::Container { container, .. } => {
|
||||
@@ -187,7 +180,6 @@ async fn list_all_terminals_for_user(
|
||||
},
|
||||
container,
|
||||
};
|
||||
terminal.target_name = Some(server_name.clone());
|
||||
terminal
|
||||
})
|
||||
}
|
||||
@@ -201,7 +193,6 @@ async fn list_all_terminals_for_user(
|
||||
},
|
||||
service,
|
||||
};
|
||||
terminal.target_name = Some(s.name.clone());
|
||||
terminal
|
||||
})
|
||||
}
|
||||
@@ -215,7 +206,6 @@ async fn list_all_terminals_for_user(
|
||||
d.id.clone()
|
||||
},
|
||||
};
|
||||
terminal.target_name = Some(d.name.clone());
|
||||
terminal
|
||||
},
|
||||
)
|
||||
@@ -240,7 +230,7 @@ async fn list_all_terminals_for_user(
|
||||
async fn list_terminals_on_server(
|
||||
server: &Server,
|
||||
target: Option<TerminalTarget>,
|
||||
) -> mogh_error::Result<Vec<Terminal>> {
|
||||
) -> serror::Result<Vec<Terminal>> {
|
||||
periphery_client(server)
|
||||
.await?
|
||||
.request(periphery_client::api::terminal::ListTerminals {
|
||||
|
||||
@@ -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,
|
||||
@@ -18,7 +15,7 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
helpers::query::{
|
||||
@@ -28,7 +25,6 @@ use crate::{
|
||||
resource,
|
||||
state::db_client,
|
||||
sync::{
|
||||
replace_ids::ReplaceIds,
|
||||
toml::{ToToml, convert_resource},
|
||||
user_groups::{convert_user_groups, user_group_to_toml},
|
||||
variables::variable_to_toml,
|
||||
@@ -42,47 +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(),
|
||||
None,
|
||||
None,
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -90,7 +161,7 @@ impl Resolve<ReadArgs> for ExportAllResourcesToToml {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &ReadArgs,
|
||||
) -> mogh_error::Result<ExportAllResourcesToTomlResponse> {
|
||||
) -> serror::Result<ExportAllResourcesToTomlResponse> {
|
||||
let targets = if self.include_resources {
|
||||
get_all_targets(&self.tags, &args.user).await?
|
||||
} else {
|
||||
@@ -116,7 +187,6 @@ impl Resolve<ReadArgs> for ExportAllResourcesToToml {
|
||||
targets,
|
||||
user_groups,
|
||||
include_variables: self.include_variables,
|
||||
existing: self.existing,
|
||||
}
|
||||
.resolve(args)
|
||||
.await
|
||||
@@ -127,69 +197,136 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &ReadArgs,
|
||||
) -> mogh_error::Result<ExportResourcesToTomlResponse> {
|
||||
) -> serror::Result<ExportResourcesToTomlResponse> {
|
||||
let 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.config);
|
||||
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>(
|
||||
@@ -203,7 +340,7 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
|| !sync.config.repo.is_empty()
|
||||
|| !sync.config.linked_repo.is_empty())
|
||||
{
|
||||
ResourceSync::replace_ids(&mut sync.config);
|
||||
ResourceSync::replace_ids(&mut sync);
|
||||
res.resource_syncs.push(convert_resource::<ResourceSync>(
|
||||
sync,
|
||||
false,
|
||||
@@ -212,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,
|
||||
};
|
||||
}
|
||||
@@ -221,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)
|
||||
@@ -270,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() {
|
||||
|
||||
@@ -14,7 +14,7 @@ use komodo_client::{
|
||||
user::User,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
@@ -32,7 +32,7 @@ impl Resolve<ReadArgs> for ListUpdates {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListUpdatesResponse> {
|
||||
) -> serror::Result<ListUpdatesResponse> {
|
||||
let query = user_resource_target_query(user, self.query).await?;
|
||||
|
||||
let usernames = find_collect(&db_client().users, None, None)
|
||||
@@ -47,9 +47,7 @@ impl Resolve<ReadArgs> for ListUpdates {
|
||||
query,
|
||||
FindOptions::builder()
|
||||
.sort(doc! { "start_ts": -1 })
|
||||
.skip(
|
||||
(self.page as u64).saturating_mul(UPDATES_PER_PAGE as u64),
|
||||
)
|
||||
.skip(self.page as u64 * UPDATES_PER_PAGE as u64)
|
||||
.limit(UPDATES_PER_PAGE)
|
||||
.build(),
|
||||
)
|
||||
@@ -94,7 +92,7 @@ impl Resolve<ReadArgs> for GetUpdate {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
let update = find_one_by_id(&db_client().updates, &self.id)
|
||||
.await
|
||||
.context("failed to query to db")?
|
||||
|
||||
@@ -13,7 +13,7 @@ use komodo_client::{
|
||||
},
|
||||
entities::user::{UserConfig, admin_service_user},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{helpers::query::get_user, state::db_client};
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Resolve<ReadArgs> for GetUsername {
|
||||
async fn resolve(
|
||||
self,
|
||||
_: &ReadArgs,
|
||||
) -> mogh_error::Result<GetUsernameResponse> {
|
||||
) -> serror::Result<GetUsernameResponse> {
|
||||
if let Some(user) = admin_service_user(&self.user_id) {
|
||||
return Ok(GetUsernameResponse {
|
||||
username: user.username,
|
||||
@@ -53,7 +53,7 @@ impl Resolve<ReadArgs> for FindUser {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user: admin }: &ReadArgs,
|
||||
) -> mogh_error::Result<FindUserResponse> {
|
||||
) -> serror::Result<FindUserResponse> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("This method is admin only.").into());
|
||||
}
|
||||
@@ -65,26 +65,15 @@ impl Resolve<ReadArgs> for ListUsers {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListUsersResponse> {
|
||||
) -> serror::Result<ListUsersResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
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
|
||||
@@ -98,7 +87,7 @@ impl Resolve<ReadArgs> for ListApiKeys {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListApiKeysResponse> {
|
||||
) -> serror::Result<ListApiKeysResponse> {
|
||||
let api_keys = find_collect(
|
||||
&db_client().api_keys,
|
||||
doc! { "user_id": &user.id },
|
||||
@@ -120,7 +109,7 @@ impl Resolve<ReadArgs> for ListApiKeysForServiceUser {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user: admin }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListApiKeysForServiceUserResponse> {
|
||||
) -> serror::Result<ListApiKeysForServiceUserResponse> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("This method is admin only.").into());
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use database::mungos::{
|
||||
},
|
||||
};
|
||||
use komodo_client::api::read::*;
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::state::db_client;
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Resolve<ReadArgs> for GetUserGroup {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetUserGroupResponse> {
|
||||
) -> serror::Result<GetUserGroupResponse> {
|
||||
let mut filter = match ObjectId::from_str(&self.user_group) {
|
||||
Ok(id) => doc! { "_id": id },
|
||||
Err(_) => doc! { "name": &self.user_group },
|
||||
@@ -43,7 +43,7 @@ impl Resolve<ReadArgs> for ListUserGroups {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListUserGroupsResponse> {
|
||||
) -> serror::Result<ListUserGroupsResponse> {
|
||||
let mut filter = Document::new();
|
||||
if !user.admin {
|
||||
filter.insert("users", &user.id);
|
||||
|
||||
@@ -4,7 +4,7 @@ use database::mungos::{
|
||||
find::find_collect, mongodb::options::FindOptions,
|
||||
};
|
||||
use komodo_client::api::read::*;
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{helpers::query::get_variable, state::db_client};
|
||||
|
||||
@@ -14,7 +14,7 @@ impl Resolve<ReadArgs> for GetVariable {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<GetVariableResponse> {
|
||||
) -> serror::Result<GetVariableResponse> {
|
||||
let mut variable = get_variable(&self.name).await?;
|
||||
if !variable.is_secret || user.admin {
|
||||
return Ok(variable);
|
||||
@@ -28,7 +28,7 @@ impl Resolve<ReadArgs> for ListVariables {
|
||||
async fn resolve(
|
||||
self,
|
||||
ReadArgs { user }: &ReadArgs,
|
||||
) -> mogh_error::Result<ListVariablesResponse> {
|
||||
) -> serror::Result<ListVariablesResponse> {
|
||||
let variables = find_collect(
|
||||
&db_client().variables,
|
||||
None,
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
use anyhow::Context;
|
||||
use axum::{Extension, Router, middleware, routing::post};
|
||||
use komodo_client::{api::terminal::*, entities::user::User};
|
||||
use mogh_auth_server::middleware::authenticate_request;
|
||||
use mogh_error::Json;
|
||||
use serror::Json;
|
||||
|
||||
use crate::{
|
||||
auth::KomodoAuthImpl, helpers::terminal::setup_target_for_user,
|
||||
auth::auth_request, helpers::terminal::setup_target_for_user,
|
||||
};
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/execute", post(execute_terminal))
|
||||
.layer(middleware::from_fn(
|
||||
authenticate_request::<KomodoAuthImpl, true>,
|
||||
))
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
// =================
|
||||
@@ -38,11 +35,8 @@ async fn execute_terminal(
|
||||
command,
|
||||
init,
|
||||
}): Json<ExecuteTerminalBody>,
|
||||
) -> mogh_error::Result<axum::body::Body> {
|
||||
info!(
|
||||
"TERMINAL EXECUTE REQUEST | USER: {} ({})",
|
||||
user.username, user.id
|
||||
);
|
||||
) -> serror::Result<axum::body::Body> {
|
||||
info!("/terminal/execute request | user: {}", user.username);
|
||||
|
||||
let (target, terminal, periphery) =
|
||||
setup_target_for_user(target, terminal, init, &user).await?;
|
||||
|
||||
97
bin/core/src/api/user/api_key.rs
Normal file
97
bin/core/src/api/user/api_key.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use anyhow::{Context as _, anyhow};
|
||||
use database::bson::doc;
|
||||
use komodo_client::{
|
||||
api::user::{
|
||||
CreateApiKey, CreateApiKeyResponse, DeleteApiKey,
|
||||
DeleteApiKeyResponse,
|
||||
},
|
||||
entities::{api_key::ApiKey, komodo_timestamp, random_string},
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::{AddStatusCode as _, AddStatusCodeError as _};
|
||||
|
||||
use crate::{
|
||||
helpers::{query::get_user, validations::validate_api_key_name},
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
use super::UserArgs;
|
||||
|
||||
const SECRET_LENGTH: usize = 40;
|
||||
const BCRYPT_COST: u32 = 10;
|
||||
|
||||
impl Resolve<UserArgs> for CreateApiKey {
|
||||
#[instrument(
|
||||
"CreateApiKey",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<CreateApiKeyResponse> {
|
||||
let user = get_user(&user.id).await?;
|
||||
|
||||
validate_api_key_name(&self.name)
|
||||
.status_code(StatusCode::BAD_REQUEST)?;
|
||||
|
||||
let key = format!("K_{}_K", random_string(SECRET_LENGTH));
|
||||
let secret = format!("S_{}_S", random_string(SECRET_LENGTH));
|
||||
let secret_hash = bcrypt::hash(&secret, BCRYPT_COST)
|
||||
.context("Failed at hashing secret string")?;
|
||||
|
||||
let api_key = ApiKey {
|
||||
name: self.name,
|
||||
key: key.clone(),
|
||||
secret: secret_hash,
|
||||
user_id: user.id.clone(),
|
||||
created_at: komodo_timestamp(),
|
||||
expires: self.expires,
|
||||
};
|
||||
|
||||
db_client()
|
||||
.api_keys
|
||||
.insert_one(api_key)
|
||||
.await
|
||||
.context("Failed to create api key on database")?;
|
||||
|
||||
Ok(CreateApiKeyResponse { key, secret })
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for DeleteApiKey {
|
||||
#[instrument(
|
||||
"DeleteApiKey",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<DeleteApiKeyResponse> {
|
||||
let client = db_client();
|
||||
|
||||
let key = client
|
||||
.api_keys
|
||||
.find_one(doc! { "key": &self.key })
|
||||
.await
|
||||
.context("Failed at database query")?
|
||||
.context("No api key with key found")?;
|
||||
|
||||
if user.id != key.user_id {
|
||||
return Err(
|
||||
anyhow!("Api key does not belong to user")
|
||||
.status_code(StatusCode::FORBIDDEN),
|
||||
);
|
||||
}
|
||||
|
||||
client
|
||||
.api_keys
|
||||
.delete_one(doc! { "key": key.key })
|
||||
.await
|
||||
.context("Failed to delete api key from database")?;
|
||||
|
||||
Ok(DeleteApiKeyResponse {})
|
||||
}
|
||||
}
|
||||
367
bin/core/src/api/user/mod.rs
Normal file
367
bin/core/src/api/user/mod.rs
Normal file
@@ -0,0 +1,367 @@
|
||||
use std::{collections::VecDeque, time::Instant};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use axum::{
|
||||
Extension, Json, Router, extract::Path, middleware, routing::post,
|
||||
};
|
||||
use database::{
|
||||
bson::{doc, to_bson},
|
||||
mungos::by_id::update_one_by_id,
|
||||
};
|
||||
use derive_variants::EnumVariants;
|
||||
use komodo_client::{
|
||||
api::user::*,
|
||||
entities::{NoData, komodo_timestamp, user::User},
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use response::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use serror::{AddStatusCode as _, AddStatusCodeError};
|
||||
use tower_sessions::Session;
|
||||
use typeshare::typeshare;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
auth::auth_request,
|
||||
config::core_config,
|
||||
helpers::{
|
||||
query::get_user,
|
||||
validations::{validate_password, validate_username},
|
||||
},
|
||||
state::db_client,
|
||||
};
|
||||
|
||||
use super::Variant;
|
||||
|
||||
mod api_key;
|
||||
mod passkey;
|
||||
mod totp;
|
||||
|
||||
pub struct UserArgs {
|
||||
pub user: User,
|
||||
/// Per-client session state
|
||||
pub session: Option<Session>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Debug, Clone, Serialize, Deserialize, Resolve, EnumVariants,
|
||||
)]
|
||||
#[args(UserArgs)]
|
||||
#[response(Response)]
|
||||
#[error(serror::Error)]
|
||||
#[serde(tag = "type", content = "params")]
|
||||
enum UserRequest {
|
||||
PushRecentlyViewed(PushRecentlyViewed),
|
||||
SetLastSeenUpdate(SetLastSeenUpdate),
|
||||
UpdateUsername(UpdateUsername),
|
||||
UpdatePassword(UpdatePassword),
|
||||
CreateApiKey(CreateApiKey),
|
||||
DeleteApiKey(DeleteApiKey),
|
||||
BeginTotpEnrollment(BeginTotpEnrollment),
|
||||
ConfirmTotpEnrollment(ConfirmTotpEnrollment),
|
||||
UnenrollTotp(UnenrollTotp),
|
||||
BeginPasskeyEnrollment(BeginPasskeyEnrollment),
|
||||
ConfirmPasskeyEnrollment(ConfirmPasskeyEnrollment),
|
||||
UnenrollPasskey(UnenrollPasskey),
|
||||
BeginThirdPartyLoginLink(BeginThirdPartyLoginLink),
|
||||
UnlinkLogin(UnlinkLogin),
|
||||
UpdateThirdPartySkip2fa(UpdateThirdPartySkip2fa),
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", post(handler))
|
||||
.route("/{variant}", post(variant_handler))
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
async fn variant_handler(
|
||||
session: Session,
|
||||
user: Extension<User>,
|
||||
Path(Variant { variant }): Path<Variant>,
|
||||
Json(params): Json<serde_json::Value>,
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let req: UserRequest = serde_json::from_value(json!({
|
||||
"type": variant,
|
||||
"params": params,
|
||||
}))?;
|
||||
handler(session, user, Json(req)).await
|
||||
}
|
||||
|
||||
async fn handler(
|
||||
session: Session,
|
||||
Extension(user): Extension<User>,
|
||||
Json(request): Json<UserRequest>,
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let timer = Instant::now();
|
||||
let req_id = Uuid::new_v4();
|
||||
debug!(
|
||||
"/user request {req_id} | user: {} ({})",
|
||||
user.username, user.id
|
||||
);
|
||||
let res = request
|
||||
.resolve(&UserArgs {
|
||||
user,
|
||||
session: Some(session),
|
||||
})
|
||||
.await;
|
||||
if let Err(e) = &res {
|
||||
warn!("/user request {req_id} error: {:#}", e.error);
|
||||
}
|
||||
let elapsed = timer.elapsed();
|
||||
debug!("/user request {req_id} | resolve time: {elapsed:?}");
|
||||
res.map(|res| res.0)
|
||||
}
|
||||
|
||||
const RECENTLY_VIEWED_MAX: usize = 10;
|
||||
|
||||
impl Resolve<UserArgs> for PushRecentlyViewed {
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<PushRecentlyViewedResponse> {
|
||||
let user = get_user(&user.id).await?;
|
||||
|
||||
let (resource_type, id) = self.resource.extract_variant_id();
|
||||
|
||||
let field = format!("recents.{resource_type}");
|
||||
|
||||
let update = match user.recents.get(&resource_type) {
|
||||
Some(recents) => {
|
||||
let mut recents = recents
|
||||
.iter()
|
||||
.filter(|_id| !id.eq(*_id))
|
||||
.take(RECENTLY_VIEWED_MAX - 1)
|
||||
.collect::<VecDeque<_>>();
|
||||
|
||||
recents.push_front(id);
|
||||
|
||||
doc! { &field: to_bson(&recents)? }
|
||||
}
|
||||
None => {
|
||||
doc! { &field: [id] }
|
||||
}
|
||||
};
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().users,
|
||||
&user.id,
|
||||
database::mungos::update::Update::Set(update),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.with_context(|| format!("Failed to update user '{field}'"))?;
|
||||
|
||||
Ok(PushRecentlyViewedResponse {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for SetLastSeenUpdate {
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<SetLastSeenUpdateResponse> {
|
||||
update_one_by_id(
|
||||
&db_client().users,
|
||||
&user.id,
|
||||
database::mungos::update::Update::Set(doc! {
|
||||
"last_update_view": komodo_timestamp()
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("Failed to update user 'last_update_view'")?;
|
||||
|
||||
Ok(SetLastSeenUpdateResponse {})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<UserArgs> for UpdateUsername {
|
||||
#[instrument(
|
||||
"UpdateUsername",
|
||||
skip_all,
|
||||
fields(
|
||||
operator = user.id,
|
||||
new_username = self.username,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<UpdateUsernameResponse> {
|
||||
check_locked(&user.username)?;
|
||||
|
||||
validate_username(&self.username)?;
|
||||
|
||||
let db = db_client();
|
||||
|
||||
if db
|
||||
.users
|
||||
.find_one(doc! { "username": &self.username })
|
||||
.await
|
||||
.context("Failed to query for existing users")?
|
||||
.is_some()
|
||||
{
|
||||
return Err(anyhow!("Username already taken.").into());
|
||||
}
|
||||
|
||||
let update = doc! { "$set": { "username": self.username } };
|
||||
|
||||
update_one_by_id(&db.users, &user.id, update, None)
|
||||
.await
|
||||
.context("Failed to update user username on database.")?;
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<UserArgs> for UpdatePassword {
|
||||
#[instrument(
|
||||
"UpdatePassword",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<UpdatePasswordResponse> {
|
||||
check_locked(&user.username)?;
|
||||
|
||||
validate_password(&self.password)
|
||||
.status_code(StatusCode::BAD_REQUEST)?;
|
||||
|
||||
db_client().set_user_password(user, &self.password).await?;
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SessionThirdPartyLinkInfo {
|
||||
pub user_id: String,
|
||||
}
|
||||
|
||||
impl SessionThirdPartyLinkInfo {
|
||||
pub const KEY: &str = "third-party-link-info";
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for BeginThirdPartyLoginLink {
|
||||
#[instrument(
|
||||
"BeginThirdPartyLoginLink",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, session }: &UserArgs,
|
||||
) -> serror::Result<BeginThirdPartyLoginLinkResponse> {
|
||||
check_locked(&user.username)?;
|
||||
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
session
|
||||
.insert(
|
||||
SessionThirdPartyLinkInfo::KEY,
|
||||
SessionThirdPartyLinkInfo {
|
||||
user_id: user.id.clone(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.context(
|
||||
"Failed to insert third party link info into client session",
|
||||
)?;
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for UnlinkLogin {
|
||||
#[instrument(
|
||||
"UnlinkLogin",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<UnlinkLoginResponse> {
|
||||
check_locked(&user.username)?;
|
||||
|
||||
let field = match self.provider.to_lowercase().as_str() {
|
||||
"local" => "linked_logins.Local",
|
||||
"github" => "linked_logins.Github",
|
||||
"google" => "linked_logins.Google",
|
||||
"oidc" => "linked_logins.Oidc",
|
||||
_ => {
|
||||
return Err(
|
||||
anyhow!(
|
||||
"Unrecognized third party login provider: {}",
|
||||
self.provider
|
||||
)
|
||||
.status_code(StatusCode::BAD_REQUEST),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let update = doc! {
|
||||
"$unset": {
|
||||
field: ""
|
||||
}
|
||||
};
|
||||
|
||||
update_one_by_id(&db_client().users, &user.id, update, None)
|
||||
.await
|
||||
.context("Failed to unlink third partly login on database")?;
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for UpdateThirdPartySkip2fa {
|
||||
#[instrument(
|
||||
"UpdateThirdPartySkip2fa",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<UpdateThirdPartySkip2faResponse> {
|
||||
check_locked(&user.username)?;
|
||||
|
||||
let update = doc! {
|
||||
"$set": {
|
||||
"third_party_skip_2fa": self.skip
|
||||
}
|
||||
};
|
||||
|
||||
update_one_by_id(&db_client().users, &user.id, update, None)
|
||||
.await
|
||||
.context("Failed to unlink third partly login on database")?;
|
||||
|
||||
Ok(NoData {})
|
||||
}
|
||||
}
|
||||
|
||||
fn check_locked(username: &str) -> serror::Result<()> {
|
||||
for locked_username in &core_config().lock_login_credentials_for {
|
||||
if locked_username == "__ALL__" || *locked_username == username {
|
||||
return Err(
|
||||
anyhow!("User login credentials are locked.")
|
||||
.status_code(StatusCode::UNAUTHORIZED),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
151
bin/core/src/api/user/passkey.rs
Normal file
151
bin/core/src/api/user/passkey.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
use anyhow::Context as _;
|
||||
use database::{
|
||||
bson::{doc, to_bson},
|
||||
mungos::by_id::update_one_by_id,
|
||||
};
|
||||
use komodo_client::{
|
||||
api::user::{
|
||||
BeginPasskeyEnrollment, BeginPasskeyEnrollmentResponse,
|
||||
ConfirmPasskeyEnrollment, ConfirmPasskeyEnrollmentResponse,
|
||||
UnenrollPasskey, UnenrollPasskeyResponse,
|
||||
},
|
||||
entities::komodo_timestamp,
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use webauthn_rs::prelude::PasskeyRegistration;
|
||||
|
||||
use crate::state::{db_client, webauthn};
|
||||
|
||||
use super::UserArgs;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SessionPasskeyEnrollment {
|
||||
user_id: String,
|
||||
state: PasskeyRegistration,
|
||||
}
|
||||
impl SessionPasskeyEnrollment {
|
||||
const KEY: &str = "passkey-enrollment";
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for BeginPasskeyEnrollment {
|
||||
#[instrument(
|
||||
"BeginPasskeyEnrollment",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, session }: &UserArgs,
|
||||
) -> serror::Result<BeginPasskeyEnrollmentResponse> {
|
||||
super::check_locked(&user.username)?;
|
||||
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
let webauthn = webauthn().context(
|
||||
"No webauthn provider available, invalid KOMODO_HOST config",
|
||||
)?;
|
||||
|
||||
// Get two parts from this, the first is returned to the client.
|
||||
// The second must stay server side and is used in confirmation flow.
|
||||
let (challenge, state) = webauthn.start_passkey_registration(
|
||||
Uuid::new_v4(),
|
||||
&user.username,
|
||||
&user.username,
|
||||
None,
|
||||
)?;
|
||||
|
||||
session
|
||||
.insert(
|
||||
SessionPasskeyEnrollment::KEY,
|
||||
SessionPasskeyEnrollment {
|
||||
user_id: user.id.clone(),
|
||||
state
|
||||
},
|
||||
)
|
||||
.await
|
||||
.context(
|
||||
"Failed to store passkey enrollment state in server side client session",
|
||||
)?;
|
||||
|
||||
Ok(challenge)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<UserArgs> for ConfirmPasskeyEnrollment {
|
||||
#[instrument(
|
||||
"ConfirmPasskeyEnrollment",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, session }: &UserArgs,
|
||||
) -> serror::Result<ConfirmPasskeyEnrollmentResponse> {
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
let webauthn = webauthn().context(
|
||||
"No webauthn provider available, invalid KOMODO_HOST config",
|
||||
)?;
|
||||
|
||||
let SessionPasskeyEnrollment { user_id, state } = session
|
||||
.remove(SessionPasskeyEnrollment::KEY)
|
||||
.await
|
||||
.context("Passkey enrollment was not initiated correctly")?
|
||||
.context(
|
||||
"Passkey enrollment was not initiated correctly or timed out",
|
||||
)?;
|
||||
|
||||
let passkey = webauthn
|
||||
.finish_passkey_registration(&self.credential, &state)
|
||||
.context("Failed to finish passkey registration")?;
|
||||
|
||||
let passkey = to_bson(&passkey)
|
||||
.context("Failed to serialize passkey to BSON")?;
|
||||
|
||||
let update = doc! {
|
||||
"$set": {
|
||||
"passkey.passkey": passkey,
|
||||
"passkey.created_at": komodo_timestamp()
|
||||
}
|
||||
};
|
||||
|
||||
update_one_by_id(&db_client().users, &user_id, update, None)
|
||||
.await
|
||||
.context("Failed to update user passkey options on database")?;
|
||||
|
||||
Ok(ConfirmPasskeyEnrollmentResponse {})
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<UserArgs> for UnenrollPasskey {
|
||||
#[instrument(
|
||||
"UnenrollPasskey",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<UnenrollPasskeyResponse> {
|
||||
let update = doc! {
|
||||
"$set": {
|
||||
"passkey.passkey": null,
|
||||
"passkey.created_at": 0
|
||||
}
|
||||
};
|
||||
update_one_by_id(&db_client().users, &user.id, update, None)
|
||||
.await
|
||||
.context("Failed to update user passkey options on database")?;
|
||||
Ok(UnenrollPasskeyResponse {})
|
||||
}
|
||||
}
|
||||
158
bin/core/src/api/user/totp.rs
Normal file
158
bin/core/src/api/user/totp.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
use anyhow::{Context as _, anyhow};
|
||||
use data_encoding::BASE32_NOPAD;
|
||||
use database::{
|
||||
bson::doc, hash_password, mungos::by_id::update_one_by_id,
|
||||
};
|
||||
use komodo_client::{
|
||||
api::user::{
|
||||
BeginTotpEnrollment, BeginTotpEnrollmentResponse,
|
||||
ConfirmTotpEnrollment, ConfirmTotpEnrollmentResponse,
|
||||
UnenrollTotp, UnenrollTotpResponse,
|
||||
},
|
||||
entities::{komodo_timestamp, random_bytes, random_string},
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serror::AddStatusCodeError as _;
|
||||
|
||||
use crate::{auth::totp::make_totp, state::db_client};
|
||||
|
||||
use super::UserArgs;
|
||||
|
||||
const TOTP_ENROLLMENT_SECRET_LENGTH: usize = 20;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SessionTotpEnrollment {
|
||||
secret: Vec<u8>,
|
||||
}
|
||||
impl SessionTotpEnrollment {
|
||||
const KEY: &str = "totp-enrollment";
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for BeginTotpEnrollment {
|
||||
#[instrument(
|
||||
"BeginTotpEnrollment",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, session }: &UserArgs,
|
||||
) -> serror::Result<BeginTotpEnrollmentResponse> {
|
||||
super::check_locked(&user.username)?;
|
||||
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
let secret = random_bytes(TOTP_ENROLLMENT_SECRET_LENGTH);
|
||||
let totp = make_totp(secret.clone(), user.id.clone())?;
|
||||
let png = totp
|
||||
.get_qr_base64()
|
||||
.map_err(anyhow::Error::msg)
|
||||
.context("Failed to generate QR code png")?;
|
||||
session
|
||||
.insert(
|
||||
SessionTotpEnrollment::KEY,
|
||||
SessionTotpEnrollment { secret },
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(BeginTotpEnrollmentResponse {
|
||||
uri: totp.get_url(),
|
||||
png,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for ConfirmTotpEnrollment {
|
||||
#[instrument(
|
||||
"ConfirmTotpEnrollment",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, session }: &UserArgs,
|
||||
) -> serror::Result<ConfirmTotpEnrollmentResponse> {
|
||||
let session = session.as_ref().context(
|
||||
"Method called in invalid context. This should not happen",
|
||||
)?;
|
||||
|
||||
let SessionTotpEnrollment { secret } = session
|
||||
.remove(SessionTotpEnrollment::KEY)
|
||||
.await
|
||||
.context("Totp enrollment was not initiated correctly")?
|
||||
.context(
|
||||
"Totp enrollment was not initiated correctly or timed out",
|
||||
)?;
|
||||
|
||||
let encoded_secret = BASE32_NOPAD.encode(&secret);
|
||||
|
||||
let totp = make_totp(secret, None)?;
|
||||
|
||||
let valid = totp
|
||||
.check_current(&self.code)
|
||||
.context("Failed to check code validity")?;
|
||||
|
||||
if !valid {
|
||||
return Err(anyhow!(
|
||||
"The provided code was not valid. Please try BeginTotpEnrollment flow again."
|
||||
).status_code(StatusCode::BAD_REQUEST));
|
||||
}
|
||||
|
||||
let recovery_codes =
|
||||
(0..10).map(|_| random_string(20)).collect::<Vec<_>>();
|
||||
let hashed_recovery_codes = recovery_codes
|
||||
.iter()
|
||||
.map(hash_password)
|
||||
.collect::<anyhow::Result<Vec<_>>>()
|
||||
.context("Failed to generate valid recovery codes")?;
|
||||
|
||||
update_one_by_id(
|
||||
&db_client().users,
|
||||
&user.id,
|
||||
doc! {
|
||||
"$set": {
|
||||
"totp.secret": encoded_secret,
|
||||
"totp.confirmed_at": komodo_timestamp(),
|
||||
"totp.recovery_codes": hashed_recovery_codes,
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("Failed to update user totp fields on database")?;
|
||||
|
||||
Ok(ConfirmTotpEnrollmentResponse { recovery_codes })
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<UserArgs> for UnenrollTotp {
|
||||
#[instrument(
|
||||
"UnenrollTotp",
|
||||
skip_all,
|
||||
fields(operator = user.id)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
UserArgs { user, .. }: &UserArgs,
|
||||
) -> serror::Result<UnenrollTotpResponse> {
|
||||
update_one_by_id(
|
||||
&db_client().users,
|
||||
&user.id,
|
||||
doc! {
|
||||
"$set": {
|
||||
"totp.secret": "",
|
||||
"totp.confirmed_at": 0,
|
||||
"totp.recovery_codes": [],
|
||||
}
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.context("Failed to clear user totp fields on database")?;
|
||||
Ok(UnenrollTotpResponse {})
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ use komodo_client::{
|
||||
action::Action, permission::PermissionLevel, update::Update,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Resolve<WriteArgs> for CreateAction {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Action> {
|
||||
) -> serror::Result<Action> {
|
||||
resource::create::<Action>(&self.name, self.config, None, user)
|
||||
.await
|
||||
}
|
||||
@@ -42,7 +42,7 @@ impl Resolve<WriteArgs> for CopyAction {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Action> {
|
||||
) -> serror::Result<Action> {
|
||||
let Action { config, .. } = get_check_permissions::<Action>(
|
||||
&self.id,
|
||||
user,
|
||||
@@ -67,7 +67,7 @@ impl Resolve<WriteArgs> for UpdateAction {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Action> {
|
||||
) -> serror::Result<Action> {
|
||||
Ok(resource::update::<Action>(&self.id, self.config, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ impl Resolve<WriteArgs> for RenameAction {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
Ok(resource::rename::<Action>(&self.id, &self.name, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,7 @@ impl Resolve<WriteArgs> for DeleteAction {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Action> {
|
||||
) -> serror::Result<Action> {
|
||||
Ok(resource::delete::<Action>(&self.id, user).await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ use std::str::FromStr;
|
||||
use anyhow::{Context, anyhow};
|
||||
use database::mungos::mongodb::bson::{doc, oid::ObjectId};
|
||||
use komodo_client::{api::write::CloseAlert, entities::NoData};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
|
||||
use crate::{api::write::WriteArgs, state::db_client};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use komodo_client::{
|
||||
alerter::Alerter, permission::PermissionLevel, update::Update,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Resolve<WriteArgs> for CreateAlerter {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Alerter> {
|
||||
) -> serror::Result<Alerter> {
|
||||
resource::create::<Alerter>(&self.name, self.config, None, user)
|
||||
.await
|
||||
}
|
||||
@@ -42,7 +42,7 @@ impl Resolve<WriteArgs> for CopyAlerter {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Alerter> {
|
||||
) -> serror::Result<Alerter> {
|
||||
let Alerter { config, .. } = get_check_permissions::<Alerter>(
|
||||
&self.id,
|
||||
user,
|
||||
@@ -66,7 +66,7 @@ impl Resolve<WriteArgs> for DeleteAlerter {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Alerter> {
|
||||
) -> serror::Result<Alerter> {
|
||||
Ok(resource::delete::<Alerter>(&self.id, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ impl Resolve<WriteArgs> for UpdateAlerter {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Alerter> {
|
||||
) -> serror::Result<Alerter> {
|
||||
Ok(
|
||||
resource::update::<Alerter>(&self.id, self.config, user)
|
||||
.await?,
|
||||
@@ -105,7 +105,7 @@ impl Resolve<WriteArgs> for RenameAlerter {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
Ok(resource::rename::<Alerter>(&self.id, &self.name, user).await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use database::mongo_indexed::doc;
|
||||
use database::mungos::mongodb::bson::to_document;
|
||||
use database::{
|
||||
mongo_indexed::doc, mungos::mongodb::bson::oid::ObjectId,
|
||||
};
|
||||
use formatting::format_serror;
|
||||
use komodo_client::{
|
||||
api::write::*,
|
||||
@@ -13,20 +15,22 @@ use komodo_client::{
|
||||
builder::{Builder, BuilderConfig},
|
||||
permission::PermissionLevel,
|
||||
repo::Repo,
|
||||
server::ServerState,
|
||||
update::Update,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api::build::{
|
||||
GetDockerfileContentsOnHost, WriteDockerfileContentsToHost,
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
use tokio::fs;
|
||||
|
||||
use crate::helpers::builder::connect_builder_periphery;
|
||||
use crate::{
|
||||
config::core_config,
|
||||
connection::PeripheryConnectionArgs,
|
||||
helpers::{
|
||||
git_token,
|
||||
git_token, periphery_client,
|
||||
query::get_server_with_state,
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
periphery::PeripheryClient,
|
||||
@@ -50,7 +54,7 @@ impl Resolve<WriteArgs> for CreateBuild {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Build> {
|
||||
) -> serror::Result<Build> {
|
||||
resource::create::<Build>(&self.name, self.config, None, user)
|
||||
.await
|
||||
}
|
||||
@@ -69,7 +73,7 @@ impl Resolve<WriteArgs> for CopyBuild {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Build> {
|
||||
) -> serror::Result<Build> {
|
||||
let Build { mut config, .. } = get_check_permissions::<Build>(
|
||||
&self.id,
|
||||
user,
|
||||
@@ -95,7 +99,7 @@ impl Resolve<WriteArgs> for DeleteBuild {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Build> {
|
||||
) -> serror::Result<Build> {
|
||||
Ok(resource::delete::<Build>(&self.id, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -113,7 +117,7 @@ impl Resolve<WriteArgs> for UpdateBuild {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Build> {
|
||||
) -> serror::Result<Build> {
|
||||
Ok(resource::update::<Build>(&self.id, self.config, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -131,7 +135,7 @@ impl Resolve<WriteArgs> for RenameBuild {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
Ok(resource::rename::<Build>(&self.id, &self.name, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -145,10 +149,7 @@ impl Resolve<WriteArgs> for WriteBuildFileContents {
|
||||
build = self.build,
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
async fn resolve(self, args: &WriteArgs) -> serror::Result<Update> {
|
||||
let build = get_check_permissions::<Build>(
|
||||
&self.build,
|
||||
&args.user,
|
||||
@@ -225,7 +226,7 @@ async fn write_dockerfile_contents_git(
|
||||
args: &WriteArgs,
|
||||
build: Build,
|
||||
mut update: Update,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
let WriteBuildFileContents { build: _, contents } = req;
|
||||
|
||||
let mut repo_args: RepoExecutionArgs = if !build
|
||||
@@ -317,7 +318,7 @@ async fn write_dockerfile_contents_git(
|
||||
return Ok(update);
|
||||
}
|
||||
|
||||
if let Err(e) = mogh_secret_file::write_async(&full_path, &contents)
|
||||
if let Err(e) = secret_file::write_async(&full_path, &contents)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Failed to write dockerfile contents to {full_path:?}")
|
||||
@@ -369,7 +370,7 @@ impl Resolve<WriteArgs> for RefreshBuildCache {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<NoData> {
|
||||
) -> serror::Result<NoData> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// build should be able to do this.
|
||||
let build = get_check_permissions::<Build>(
|
||||
@@ -472,15 +473,48 @@ async fn get_on_host_periphery(
|
||||
.await
|
||||
.context("Failed to get builder")?;
|
||||
|
||||
if let BuilderConfig::Aws(_) = builder.config {
|
||||
return Err(anyhow!(
|
||||
"Files on host doesn't work with AWS builder"
|
||||
));
|
||||
match builder.config {
|
||||
BuilderConfig::Aws(_) => {
|
||||
Err(anyhow!("Files on host doesn't work with AWS builder"))
|
||||
}
|
||||
BuilderConfig::Url(config) => {
|
||||
// TODO: Ensure connection is actually established.
|
||||
// Builder id no good because it may be active for multiple connections.
|
||||
let periphery = PeripheryClient::new(
|
||||
PeripheryConnectionArgs::from_url_builder(
|
||||
&ObjectId::new().to_hex(),
|
||||
&config,
|
||||
),
|
||||
config.insecure_tls,
|
||||
)
|
||||
.await?;
|
||||
// Poll for connection to be estalished
|
||||
let mut err = None;
|
||||
for _ in 0..10 {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
match periphery.health_check().await {
|
||||
Ok(_) => return Ok(periphery),
|
||||
Err(e) => err = Some(e),
|
||||
};
|
||||
}
|
||||
Err(err.context("Missing error")?)
|
||||
}
|
||||
BuilderConfig::Server(config) => {
|
||||
if config.server_id.is_empty() {
|
||||
return Err(anyhow!(
|
||||
"Builder is type server, but has no server attached"
|
||||
));
|
||||
}
|
||||
let (server, state) =
|
||||
get_server_with_state(&config.server_id).await?;
|
||||
if state != ServerState::Ok {
|
||||
return Err(anyhow!(
|
||||
"Builder server is disabled or not reachable"
|
||||
));
|
||||
};
|
||||
periphery_client(&server).await
|
||||
}
|
||||
}
|
||||
|
||||
connect_builder_periphery(build.name.clone(), None, builder, None)
|
||||
.await
|
||||
.map(|(periphery, _)| periphery)
|
||||
}
|
||||
|
||||
/// The successful case will be included as Some(remote_contents).
|
||||
|
||||
@@ -4,7 +4,7 @@ use komodo_client::{
|
||||
builder::Builder, permission::PermissionLevel, update::Update,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Resolve<WriteArgs> for CreateBuilder {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Builder> {
|
||||
) -> serror::Result<Builder> {
|
||||
resource::create::<Builder>(&self.name, self.config, None, user)
|
||||
.await
|
||||
}
|
||||
@@ -42,7 +42,7 @@ impl Resolve<WriteArgs> for CopyBuilder {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Builder> {
|
||||
) -> serror::Result<Builder> {
|
||||
let Builder { config, .. } = get_check_permissions::<Builder>(
|
||||
&self.id,
|
||||
user,
|
||||
@@ -66,7 +66,7 @@ impl Resolve<WriteArgs> for DeleteBuilder {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Builder> {
|
||||
) -> serror::Result<Builder> {
|
||||
Ok(resource::delete::<Builder>(&self.id, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ impl Resolve<WriteArgs> for UpdateBuilder {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Builder> {
|
||||
) -> serror::Result<Builder> {
|
||||
Ok(
|
||||
resource::update::<Builder>(&self.id, self.config, user)
|
||||
.await?,
|
||||
@@ -105,7 +105,7 @@ impl Resolve<WriteArgs> for RenameBuilder {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
Ok(resource::rename::<Builder>(&self.id, &self.name, user).await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,33 @@
|
||||
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::*},
|
||||
api::write::*,
|
||||
entities::{
|
||||
Operation, ResourceTarget, SwarmOrServer,
|
||||
alert::{Alert, AlertData, SeverityLevel},
|
||||
Operation,
|
||||
deployment::{
|
||||
Deployment, DeploymentImage, DeploymentInfo, DeploymentState,
|
||||
PartialDeploymentConfig, RestartMode, extract_registry_domain,
|
||||
Deployment, DeploymentImage, DeploymentState,
|
||||
PartialDeploymentConfig, RestartMode,
|
||||
},
|
||||
docker::container::RestartPolicyNameEnum,
|
||||
komodo_timestamp, optional_string,
|
||||
komodo_timestamp,
|
||||
permission::PermissionLevel,
|
||||
server::{Server, ServerState},
|
||||
to_container_compatible_name,
|
||||
update::Update,
|
||||
user::{auto_redeploy_user, system_user},
|
||||
},
|
||||
};
|
||||
use mogh_cache::SetCache;
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api::{self, container::InspectContainer};
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
alert::send_alerts,
|
||||
api::execute::{self, ExecuteRequest, ExecutionResult},
|
||||
helpers::{
|
||||
periphery_client,
|
||||
query::{get_deployment_state, get_swarm_or_server},
|
||||
registry_token,
|
||||
update::{add_update, make_update, poll_update_until_complete},
|
||||
query::get_deployment_state,
|
||||
update::{add_update, make_update},
|
||||
},
|
||||
permission::get_check_permissions,
|
||||
resource::{
|
||||
self, list_full_for_user_using_pattern,
|
||||
setup_deployment_execution,
|
||||
},
|
||||
state::{
|
||||
action_states, db_client, deployment_status_cache,
|
||||
image_digest_cache, server_status_cache,
|
||||
},
|
||||
resource,
|
||||
state::{action_states, db_client, server_status_cache},
|
||||
};
|
||||
|
||||
use super::WriteArgs;
|
||||
@@ -60,7 +45,7 @@ impl Resolve<WriteArgs> for CreateDeployment {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Deployment> {
|
||||
) -> serror::Result<Deployment> {
|
||||
resource::create::<Deployment>(
|
||||
&self.name,
|
||||
self.config,
|
||||
@@ -84,7 +69,7 @@ impl Resolve<WriteArgs> for CopyDeployment {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Deployment> {
|
||||
) -> serror::Result<Deployment> {
|
||||
let Deployment { config, .. } =
|
||||
get_check_permissions::<Deployment>(
|
||||
&self.id,
|
||||
@@ -115,7 +100,7 @@ impl Resolve<WriteArgs> for CreateDeploymentFromContainer {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Deployment> {
|
||||
) -> serror::Result<Deployment> {
|
||||
let server = get_check_permissions::<Server>(
|
||||
&self.server,
|
||||
user,
|
||||
@@ -216,7 +201,7 @@ impl Resolve<WriteArgs> for DeleteDeployment {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Deployment> {
|
||||
) -> serror::Result<Deployment> {
|
||||
Ok(resource::delete::<Deployment>(&self.id, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -234,35 +219,11 @@ impl Resolve<WriteArgs> for UpdateDeployment {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Deployment> {
|
||||
// If the update changes image,
|
||||
// also update the stored latest image digest.
|
||||
let image_update = self
|
||||
.config
|
||||
.image
|
||||
.as_ref()
|
||||
.map(|image| image.as_image().is_some())
|
||||
.unwrap_or_default();
|
||||
|
||||
let deployment =
|
||||
) -> serror::Result<Deployment> {
|
||||
Ok(
|
||||
resource::update::<Deployment>(&self.id, self.config, user)
|
||||
.await?;
|
||||
|
||||
if image_update {
|
||||
tokio::spawn(async move {
|
||||
let _ = (CheckDeploymentForUpdate {
|
||||
deployment: self.id,
|
||||
skip_auto_update: false,
|
||||
wait_for_auto_update: false,
|
||||
})
|
||||
.resolve(&WriteArgs {
|
||||
user: system_user().to_owned(),
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
Ok(deployment)
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +240,7 @@ impl Resolve<WriteArgs> for RenameDeployment {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
let deployment = get_check_permissions::<Deployment>(
|
||||
&self.id,
|
||||
user,
|
||||
@@ -353,324 +314,3 @@ impl Resolve<WriteArgs> for RenameDeployment {
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
impl Resolve<WriteArgs> for CheckDeploymentForUpdate {
|
||||
#[instrument(
|
||||
"CheckDeploymentForUpdate",
|
||||
skip_all,
|
||||
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> {
|
||||
// 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(
|
||||
&self.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
swarm_or_server.verify_has_target()?;
|
||||
|
||||
check_deployment_for_update_inner(
|
||||
deployment,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
self.wait_for_auto_update,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// If it goes down the "update available" path,
|
||||
/// only send alert if deployment id is not in this cache.
|
||||
/// If alert is sent, add ID to cache.
|
||||
/// If later it goes down non "update available" path,
|
||||
/// remove the id from cache, so next time it does another alert
|
||||
/// will be sent.
|
||||
fn deployment_alert_sent_cache() -> &'static SetCache<String> {
|
||||
static CACHE: OnceLock<SetCache<String>> = OnceLock::new();
|
||||
CACHE.get_or_init(Default::default)
|
||||
}
|
||||
|
||||
/// Checks remote registry for latest image digest,
|
||||
/// and saves it to database associated with the deployment.
|
||||
///
|
||||
/// Returns true if update is available and auto deploy is false.
|
||||
/// If auto deploy is true, this will deploy.
|
||||
#[instrument(
|
||||
"CheckDeploymentForUpdateInner",
|
||||
skip_all,
|
||||
fields(
|
||||
deployment = deployment.id,
|
||||
skip_auto_update,
|
||||
wait_for_auto_update,
|
||||
)
|
||||
)]
|
||||
pub async fn check_deployment_for_update_inner(
|
||||
deployment: Deployment,
|
||||
swarm_or_server: &SwarmOrServer,
|
||||
skip_auto_update: bool,
|
||||
// Otherwise spawns task to run in background
|
||||
wait_for_auto_update: bool,
|
||||
) -> anyhow::Result<CheckDeploymentForUpdateResponse> {
|
||||
let alert_cache = deployment_alert_sent_cache();
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
let domain = extract_registry_domain(image)?;
|
||||
let account =
|
||||
optional_string(&deployment.config.image_registry_account);
|
||||
let token = if let Some(account) = &account {
|
||||
registry_token(&domain, account).await?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(image, account, token)
|
||||
}
|
||||
DeploymentImage::Build { .. } => {
|
||||
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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let latest_digest = image_digest_cache()
|
||||
.get(swarm_or_server, image, account, token)
|
||||
.await?;
|
||||
|
||||
resource::update_info::<Deployment>(
|
||||
&deployment.id,
|
||||
&DeploymentInfo {
|
||||
latest_image_digest: latest_digest.clone(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Some((state, Some(current_digests))) =
|
||||
deployment_status_cache()
|
||||
.get(&deployment.id)
|
||||
.await
|
||||
.map(|s| (s.curr.state, s.curr.image_digests.clone()))
|
||||
else {
|
||||
alert_cache.remove(&deployment.id).await;
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: false,
|
||||
});
|
||||
};
|
||||
|
||||
// If not running or latest digest matches current, early return
|
||||
if !matches!(state, DeploymentState::Running)
|
||||
|| !latest_digest.update_available(¤t_digests)
|
||||
{
|
||||
alert_cache.remove(&deployment.id).await;
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: 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();
|
||||
|
||||
let run = async move {
|
||||
match execute::inner_handler(
|
||||
ExecuteRequest::Deploy(Deploy {
|
||||
deployment: name.clone(),
|
||||
stop_signal: None,
|
||||
stop_time: None,
|
||||
}),
|
||||
auto_redeploy_user().to_owned(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(res) => {
|
||||
let ExecutionResult::Single(update) = res else {
|
||||
unreachable!()
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to auto update Deployment {name} | {e:#}",)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if wait_for_auto_update {
|
||||
run.await
|
||||
} else {
|
||||
tokio::spawn(run);
|
||||
}
|
||||
} else {
|
||||
// Avoid spamming alerts
|
||||
if alert_cache.contains(&deployment.id).await {
|
||||
return Ok(CheckDeploymentForUpdateResponse {
|
||||
deployment: deployment.id,
|
||||
update_available: true,
|
||||
});
|
||||
}
|
||||
alert_cache.insert(deployment.id.clone()).await;
|
||||
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::DeploymentImageUpdateAvailable {
|
||||
id: deployment.id.clone(),
|
||||
name: deployment.name.clone(),
|
||||
swarm_id: swarm_or_server.swarm_id().map(str::to_string),
|
||||
swarm_name: swarm_or_server.swarm_name().map(str::to_string),
|
||||
server_id: swarm_or_server.server_id().map(str::to_string),
|
||||
server_name: swarm_or_server
|
||||
.server_name()
|
||||
.map(str::to_string),
|
||||
image: image.clone(),
|
||||
},
|
||||
};
|
||||
let res = db_client().alerts.insert_one(&alert).await;
|
||||
if let Err(e) = res {
|
||||
error!(
|
||||
"Failed to record DeploymentImageUpdateAvailable to db | {e:#}"
|
||||
);
|
||||
}
|
||||
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(),
|
||||
None,
|
||||
None,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,18 @@ use anyhow::Context;
|
||||
use axum::{
|
||||
Extension, Router, extract::Path, middleware, routing::post,
|
||||
};
|
||||
use derive_variants::{EnumVariants, ExtractVariant};
|
||||
use komodo_client::{api::write::*, entities::user::User};
|
||||
use mogh_auth_server::middleware::authenticate_request;
|
||||
use mogh_error::Json;
|
||||
use mogh_error::Response;
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
use response::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use serror::Json;
|
||||
use strum::Display;
|
||||
use strum::EnumDiscriminants;
|
||||
use typeshare::typeshare;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::auth::KomodoAuthImpl;
|
||||
use crate::auth::auth_request;
|
||||
|
||||
use super::Variant;
|
||||
|
||||
@@ -41,23 +40,18 @@ mod user;
|
||||
mod user_group;
|
||||
mod variable;
|
||||
|
||||
pub use {
|
||||
deployment::check_deployment_for_update_inner,
|
||||
stack::check_stack_for_update_inner,
|
||||
};
|
||||
|
||||
pub struct WriteArgs {
|
||||
pub user: User,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
|
||||
Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants,
|
||||
)]
|
||||
#[strum_discriminants(name(WriteRequestMethod), derive(Display))]
|
||||
#[variant_derive(Debug, Display)]
|
||||
#[args(WriteArgs)]
|
||||
#[response(Response)]
|
||||
#[error(mogh_error::Error)]
|
||||
#[error(serror::Error)]
|
||||
#[serde(tag = "type", content = "params")]
|
||||
pub enum WriteRequest {
|
||||
// ==== RESOURCE ====
|
||||
@@ -94,8 +88,6 @@ pub enum WriteRequest {
|
||||
RenameStack(RenameStack),
|
||||
WriteStackFileContents(WriteStackFileContents),
|
||||
RefreshStackCache(RefreshStackCache),
|
||||
CheckStackForUpdate(CheckStackForUpdate),
|
||||
BatchCheckStackForUpdate(BatchCheckStackForUpdate),
|
||||
|
||||
// ==== DEPLOYMENT ====
|
||||
CreateDeployment(CreateDeployment),
|
||||
@@ -104,8 +96,6 @@ pub enum WriteRequest {
|
||||
DeleteDeployment(DeleteDeployment),
|
||||
UpdateDeployment(UpdateDeployment),
|
||||
RenameDeployment(RenameDeployment),
|
||||
CheckDeploymentForUpdate(CheckDeploymentForUpdate),
|
||||
BatchCheckDeploymentForUpdate(BatchCheckDeploymentForUpdate),
|
||||
|
||||
// ==== BUILD ====
|
||||
CreateBuild(CreateBuild),
|
||||
@@ -168,8 +158,6 @@ pub enum WriteRequest {
|
||||
DeleteOnboardingKey(DeleteOnboardingKey),
|
||||
|
||||
// ==== USER ====
|
||||
PushRecentlyViewed(PushRecentlyViewed),
|
||||
SetLastSeenUpdate(SetLastSeenUpdate),
|
||||
CreateLocalUser(CreateLocalUser),
|
||||
DeleteUser(DeleteUser),
|
||||
|
||||
@@ -211,12 +199,9 @@ pub enum WriteRequest {
|
||||
CreateGitProviderAccount(CreateGitProviderAccount),
|
||||
UpdateGitProviderAccount(UpdateGitProviderAccount),
|
||||
DeleteGitProviderAccount(DeleteGitProviderAccount),
|
||||
#[serde(alias = "CreateDockerRegistryAccount")]
|
||||
CreateImageRegistryAccount(CreateImageRegistryAccount),
|
||||
#[serde(alias = "UpdateDockerRegistryAccount")]
|
||||
UpdateImageRegistryAccount(UpdateImageRegistryAccount),
|
||||
#[serde(alias = "DeleteDockerRegistryAccount")]
|
||||
DeleteImageRegistryAccount(DeleteImageRegistryAccount),
|
||||
CreateDockerRegistryAccount(CreateDockerRegistryAccount),
|
||||
UpdateDockerRegistryAccount(UpdateDockerRegistryAccount),
|
||||
DeleteDockerRegistryAccount(DeleteDockerRegistryAccount),
|
||||
|
||||
// ==== ALERT ====
|
||||
CloseAlert(CloseAlert),
|
||||
@@ -226,16 +211,14 @@ pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", post(handler))
|
||||
.route("/{variant}", post(variant_handler))
|
||||
.layer(middleware::from_fn(
|
||||
authenticate_request::<KomodoAuthImpl, true>,
|
||||
))
|
||||
.layer(middleware::from_fn(auth_request))
|
||||
}
|
||||
|
||||
async fn variant_handler(
|
||||
user: Extension<User>,
|
||||
Path(Variant { variant }): Path<Variant>,
|
||||
Json(params): Json<serde_json::Value>,
|
||||
) -> mogh_error::Result<axum::response::Response> {
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let req: WriteRequest = serde_json::from_value(json!({
|
||||
"type": variant,
|
||||
"params": params,
|
||||
@@ -246,8 +229,10 @@ async fn variant_handler(
|
||||
async fn handler(
|
||||
Extension(user): Extension<User>,
|
||||
Json(request): Json<WriteRequest>,
|
||||
) -> mogh_error::Result<axum::response::Response> {
|
||||
let res = tokio::spawn(task(request, user))
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let req_id = Uuid::new_v4();
|
||||
|
||||
let res = tokio::spawn(task(req_id, request, user))
|
||||
.await
|
||||
.context("failure in spawned task");
|
||||
|
||||
@@ -255,38 +240,18 @@ async fn handler(
|
||||
}
|
||||
|
||||
async fn task(
|
||||
req_id: Uuid,
|
||||
request: WriteRequest,
|
||||
user: User,
|
||||
) -> mogh_error::Result<axum::response::Response> {
|
||||
let task_id = Uuid::new_v4();
|
||||
let method: WriteRequestMethod = (&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",
|
||||
);
|
||||
}
|
||||
) -> serror::Result<axum::response::Response> {
|
||||
let variant = request.extract_variant();
|
||||
info!("/write request | {variant} | user: {}", user.username);
|
||||
|
||||
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} | {variant} | error: {:#}",
|
||||
e.error
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,15 +10,12 @@ use komodo_client::{
|
||||
komodo_timestamp, onboarding_key::OnboardingKey, random_string,
|
||||
},
|
||||
};
|
||||
use mogh_error::{AddStatusCode, AddStatusCodeError};
|
||||
use mogh_pki::EncodedKeyPair;
|
||||
use mogh_resolver::Resolve;
|
||||
use noise::key::EncodedKeyPair;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::{AddStatusCode, AddStatusCodeError};
|
||||
|
||||
use crate::{
|
||||
api::write::WriteArgs, helpers::query::get_all_tags,
|
||||
state::db_client,
|
||||
};
|
||||
use crate::{api::write::WriteArgs, state::db_client};
|
||||
|
||||
//
|
||||
|
||||
@@ -38,7 +35,7 @@ impl Resolve<WriteArgs> for CreateOnboardingKey {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> mogh_error::Result<CreateOnboardingKeyResponse> {
|
||||
) -> serror::Result<CreateOnboardingKeyResponse> {
|
||||
if !admin.admin {
|
||||
return Err(
|
||||
anyhow!("This call is admin only")
|
||||
@@ -50,27 +47,9 @@ impl Resolve<WriteArgs> for CreateOnboardingKey {
|
||||
} else {
|
||||
format!("O_{}_O", random_string(28))
|
||||
};
|
||||
let public_key = EncodedKeyPair::from_private_key(
|
||||
mogh_pki::PkiKind::Mutual,
|
||||
&private_key,
|
||||
)?
|
||||
.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 public_key = EncodedKeyPair::from_private_key(&private_key)?
|
||||
.public
|
||||
.into_inner();
|
||||
let onboarding_key = OnboardingKey {
|
||||
public_key,
|
||||
name: self.name,
|
||||
@@ -78,8 +57,7 @@ impl Resolve<WriteArgs> for CreateOnboardingKey {
|
||||
onboarded: Default::default(),
|
||||
created_at: komodo_timestamp(),
|
||||
expires: self.expires,
|
||||
tags,
|
||||
privileged: self.privileged,
|
||||
tags: self.tags,
|
||||
copy_server: self.copy_server,
|
||||
create_builder: self.create_builder,
|
||||
};
|
||||
@@ -121,7 +99,7 @@ impl Resolve<WriteArgs> for UpdateOnboardingKey {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdateOnboardingKeyResponse> {
|
||||
) -> serror::Result<UpdateOnboardingKeyResponse> {
|
||||
if !admin.admin {
|
||||
return Err(
|
||||
anyhow!("This call is admin only")
|
||||
@@ -157,28 +135,9 @@ 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(copy_server) = self.copy_server {
|
||||
update.insert("copy_server", copy_server);
|
||||
}
|
||||
@@ -217,7 +176,7 @@ impl Resolve<WriteArgs> for DeleteOnboardingKey {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> mogh_error::Result<DeleteOnboardingKeyResponse> {
|
||||
) -> serror::Result<DeleteOnboardingKeyResponse> {
|
||||
if !admin.admin {
|
||||
return Err(
|
||||
anyhow!("This call is admin only")
|
||||
|
||||
@@ -8,6 +8,7 @@ use database::mungos::{
|
||||
options::UpdateOptions,
|
||||
},
|
||||
};
|
||||
use derive_variants::ExtractVariant as _;
|
||||
use komodo_client::{
|
||||
api::write::*,
|
||||
entities::{
|
||||
@@ -15,7 +16,7 @@ use komodo_client::{
|
||||
permission::{UserTarget, UserTargetVariant},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{helpers::query::get_user, state::db_client};
|
||||
|
||||
@@ -34,7 +35,7 @@ impl Resolve<WriteArgs> for UpdateUserAdmin {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: super_admin }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdateUserAdminResponse> {
|
||||
) -> serror::Result<UpdateUserAdminResponse> {
|
||||
if !super_admin.super_admin {
|
||||
return Err(
|
||||
anyhow!("Only super admins can call this method.").into(),
|
||||
@@ -82,7 +83,7 @@ impl Resolve<WriteArgs> for UpdateUserBasePermissions {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdateUserBasePermissionsResponse> {
|
||||
) -> serror::Result<UpdateUserBasePermissionsResponse> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
@@ -148,7 +149,7 @@ impl Resolve<WriteArgs> for UpdatePermissionOnResourceType {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdatePermissionOnResourceTypeResponse> {
|
||||
) -> serror::Result<UpdatePermissionOnResourceTypeResponse> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
@@ -226,7 +227,7 @@ impl Resolve<WriteArgs> for UpdatePermissionOnTarget {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user: admin }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdatePermissionOnTargetResponse> {
|
||||
) -> serror::Result<UpdatePermissionOnTargetResponse> {
|
||||
if !admin.admin {
|
||||
return Err(anyhow!("this method is admin only").into());
|
||||
}
|
||||
@@ -295,7 +296,7 @@ impl Resolve<WriteArgs> for UpdatePermissionOnTarget {
|
||||
/// checks if inner id is actually a `name`, and replaces it with id if so.
|
||||
async fn extract_user_target_with_validation(
|
||||
user_target: &UserTarget,
|
||||
) -> mogh_error::Result<(UserTargetVariant, String)> {
|
||||
) -> serror::Result<(UserTargetVariant, String)> {
|
||||
match user_target {
|
||||
UserTarget::User(ident) => {
|
||||
let filter = match ObjectId::from_str(ident) {
|
||||
@@ -331,7 +332,7 @@ async fn extract_user_target_with_validation(
|
||||
/// checks if inner id is actually a `name`, and replaces it with id if so.
|
||||
async fn extract_resource_target_with_validation(
|
||||
resource_target: &ResourceTarget,
|
||||
) -> mogh_error::Result<(ResourceTargetVariant, String)> {
|
||||
) -> serror::Result<(ResourceTargetVariant, String)> {
|
||||
match resource_target {
|
||||
ResourceTarget::System(_) => {
|
||||
let res = resource_target.extract_variant_id();
|
||||
@@ -347,9 +348,9 @@ async fn extract_resource_target_with_validation(
|
||||
.find_one(filter)
|
||||
.await
|
||||
.context("Failed to query db for swarms")?
|
||||
.context("No matching swarm found")?
|
||||
.context("No matching server found")?
|
||||
.id;
|
||||
Ok((ResourceTargetVariant::Swarm, id))
|
||||
Ok((ResourceTargetVariant::Server, id))
|
||||
}
|
||||
ResourceTarget::Server(ident) => {
|
||||
let filter = match ObjectId::from_str(ident) {
|
||||
|
||||
@@ -4,7 +4,7 @@ use komodo_client::{
|
||||
permission::PermissionLevel, procedure::Procedure, update::Update,
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{permission::get_check_permissions, resource};
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Resolve<WriteArgs> for CreateProcedure {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<CreateProcedureResponse> {
|
||||
) -> serror::Result<CreateProcedureResponse> {
|
||||
resource::create::<Procedure>(&self.name, self.config, None, user)
|
||||
.await
|
||||
}
|
||||
@@ -42,7 +42,7 @@ impl Resolve<WriteArgs> for CopyProcedure {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<CopyProcedureResponse> {
|
||||
) -> serror::Result<CopyProcedureResponse> {
|
||||
let Procedure { config, .. } =
|
||||
get_check_permissions::<Procedure>(
|
||||
&self.id,
|
||||
@@ -73,7 +73,7 @@ impl Resolve<WriteArgs> for UpdateProcedure {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdateProcedureResponse> {
|
||||
) -> serror::Result<UpdateProcedureResponse> {
|
||||
Ok(
|
||||
resource::update::<Procedure>(&self.id, self.config, user)
|
||||
.await?,
|
||||
@@ -94,7 +94,7 @@ impl Resolve<WriteArgs> for RenameProcedure {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
Ok(
|
||||
resource::rename::<Procedure>(&self.id, &self.name, user)
|
||||
.await?,
|
||||
@@ -114,7 +114,7 @@ impl Resolve<WriteArgs> for DeleteProcedure {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<DeleteProcedureResponse> {
|
||||
) -> serror::Result<DeleteProcedureResponse> {
|
||||
Ok(resource::delete::<Procedure>(&self.id, user).await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ use komodo_client::{
|
||||
api::write::*,
|
||||
entities::{
|
||||
Operation, ResourceTarget,
|
||||
provider::{GitProviderAccount, ImageRegistryAccount},
|
||||
provider::{DockerRegistryAccount, GitProviderAccount},
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
|
||||
use crate::{
|
||||
helpers::update::{add_update, make_update},
|
||||
@@ -35,7 +35,7 @@ impl Resolve<WriteArgs> for CreateGitProviderAccount {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<CreateGitProviderAccountResponse> {
|
||||
) -> serror::Result<CreateGitProviderAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can create git provider accounts")
|
||||
@@ -111,7 +111,7 @@ impl Resolve<WriteArgs> for UpdateGitProviderAccount {
|
||||
async fn resolve(
|
||||
mut self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdateGitProviderAccountResponse> {
|
||||
) -> serror::Result<UpdateGitProviderAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can update git provider accounts")
|
||||
@@ -199,7 +199,7 @@ impl Resolve<WriteArgs> for DeleteGitProviderAccount {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<DeleteGitProviderAccountResponse> {
|
||||
) -> serror::Result<DeleteGitProviderAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can delete git provider accounts")
|
||||
@@ -248,7 +248,7 @@ impl Resolve<WriteArgs> for DeleteGitProviderAccount {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for CreateImageRegistryAccount {
|
||||
impl Resolve<WriteArgs> for CreateDockerRegistryAccount {
|
||||
#[instrument(
|
||||
"CreateDockerRegistryAccount",
|
||||
skip_all,
|
||||
@@ -261,7 +261,7 @@ impl Resolve<WriteArgs> for CreateImageRegistryAccount {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<CreateImageRegistryAccountResponse> {
|
||||
) -> serror::Result<CreateDockerRegistryAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!(
|
||||
@@ -271,7 +271,7 @@ impl Resolve<WriteArgs> for CreateImageRegistryAccount {
|
||||
);
|
||||
}
|
||||
|
||||
let mut account: ImageRegistryAccount = self.account.into();
|
||||
let mut account: DockerRegistryAccount = self.account.into();
|
||||
|
||||
if account.domain.is_empty() {
|
||||
return Err(
|
||||
@@ -326,7 +326,7 @@ impl Resolve<WriteArgs> for CreateImageRegistryAccount {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for UpdateImageRegistryAccount {
|
||||
impl Resolve<WriteArgs> for UpdateDockerRegistryAccount {
|
||||
#[instrument(
|
||||
"UpdateDockerRegistryAccount",
|
||||
skip_all,
|
||||
@@ -340,7 +340,7 @@ impl Resolve<WriteArgs> for UpdateImageRegistryAccount {
|
||||
async fn resolve(
|
||||
mut self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdateImageRegistryAccountResponse> {
|
||||
) -> serror::Result<UpdateDockerRegistryAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can update docker registry accounts")
|
||||
@@ -423,7 +423,7 @@ impl Resolve<WriteArgs> for UpdateImageRegistryAccount {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<WriteArgs> for DeleteImageRegistryAccount {
|
||||
impl Resolve<WriteArgs> for DeleteDockerRegistryAccount {
|
||||
#[instrument(
|
||||
"DeleteDockerRegistryAccount",
|
||||
skip_all,
|
||||
@@ -435,7 +435,7 @@ impl Resolve<WriteArgs> for DeleteImageRegistryAccount {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<DeleteImageRegistryAccountResponse> {
|
||||
) -> serror::Result<DeleteDockerRegistryAccountResponse> {
|
||||
if !user.admin {
|
||||
return Err(
|
||||
anyhow!("Only admins can delete docker registry accounts")
|
||||
|
||||
@@ -15,8 +15,8 @@ use komodo_client::{
|
||||
update::{Log, Update},
|
||||
},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
use periphery_client::api;
|
||||
use resolver_api::Resolve;
|
||||
|
||||
use crate::{
|
||||
config::core_config,
|
||||
@@ -44,7 +44,7 @@ impl Resolve<WriteArgs> for CreateRepo {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Repo> {
|
||||
) -> serror::Result<Repo> {
|
||||
resource::create::<Repo>(&self.name, self.config, None, user)
|
||||
.await
|
||||
}
|
||||
@@ -63,7 +63,7 @@ impl Resolve<WriteArgs> for CopyRepo {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Repo> {
|
||||
) -> serror::Result<Repo> {
|
||||
let Repo { config, .. } = get_check_permissions::<Repo>(
|
||||
&self.id,
|
||||
user,
|
||||
@@ -87,7 +87,7 @@ impl Resolve<WriteArgs> for DeleteRepo {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Repo> {
|
||||
) -> serror::Result<Repo> {
|
||||
Ok(resource::delete::<Repo>(&self.id, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ impl Resolve<WriteArgs> for UpdateRepo {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Repo> {
|
||||
) -> serror::Result<Repo> {
|
||||
Ok(resource::update::<Repo>(&self.id, self.config, user).await?)
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ impl Resolve<WriteArgs> for RenameRepo {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<Update> {
|
||||
) -> serror::Result<Update> {
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
&self.id,
|
||||
user,
|
||||
@@ -199,7 +199,7 @@ impl Resolve<WriteArgs> for RefreshRepoCache {
|
||||
async fn resolve(
|
||||
self,
|
||||
WriteArgs { user }: &WriteArgs,
|
||||
) -> mogh_error::Result<NoData> {
|
||||
) -> serror::Result<NoData> {
|
||||
// Even though this is a write request, this doesn't change any config. Anyone that can execute the
|
||||
// repo should be able to do this.
|
||||
let repo = get_check_permissions::<Repo>(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use anyhow::anyhow;
|
||||
use derive_variants::ExtractVariant as _;
|
||||
use komodo_client::{
|
||||
api::write::{UpdateResourceMeta, UpdateResourceMetaResponse},
|
||||
entities::{
|
||||
@@ -8,9 +9,9 @@ use komodo_client::{
|
||||
sync::ResourceSync,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
use mogh_resolver::Resolve;
|
||||
use reqwest::StatusCode;
|
||||
use resolver_api::Resolve;
|
||||
use serror::AddStatusCodeError;
|
||||
|
||||
use crate::resource::{self, ResourceMetaUpdate};
|
||||
|
||||
@@ -32,7 +33,7 @@ impl Resolve<WriteArgs> for UpdateResourceMeta {
|
||||
async fn resolve(
|
||||
self,
|
||||
args: &WriteArgs,
|
||||
) -> mogh_error::Result<UpdateResourceMetaResponse> {
|
||||
) -> serror::Result<UpdateResourceMetaResponse> {
|
||||
let meta = ResourceMetaUpdate {
|
||||
description: self.description,
|
||||
template: self.template,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user