From af9fbf9667d757a0d167987ffc80d0d83d96fdb7 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Wed, 4 Dec 2024 11:51:25 -0500 Subject: [PATCH] resolver v3 add new ec2 instance types clean up testing config document the libraries a bit clean up main update sysinfo and otel update client resolver 3.0 resolver v3 prog clean up gitignore implement periphery resolver v3 clean up core read api v3 more prog execute api missing apis compiling 1.16.13 work on more granular traits prog on crud --- .dockerignore | 14 - .gitignore | 7 +- Cargo.lock | 182 +-- Cargo.toml | 35 +- bin/cli/Cargo.toml | 1 + bin/core/Cargo.toml | 1 + bin/core/multi-arch.Dockerfile | 2 +- bin/core/single-arch.Dockerfile | 2 +- bin/core/src/api/auth.rs | 82 +- bin/core/src/api/execute/action.rs | 70 +- bin/core/src/api/execute/build.rs | 104 +- bin/core/src/api/execute/deployment.rs | 186 +-- bin/core/src/api/execute/mod.rs | 149 +-- bin/core/src/api/execute/procedure.rs | 36 +- bin/core/src/api/execute/repo.rs | 143 +-- bin/core/src/api/execute/server.rs | 427 +++---- bin/core/src/api/execute/server_template.rs | 102 +- bin/core/src/api/execute/stack.rs | 291 +++-- bin/core/src/api/execute/sync.rs | 44 +- bin/core/src/api/read/action.rs | 81 +- bin/core/src/api/read/alert.rs | 51 +- bin/core/src/api/read/alerter.rs | 72 +- bin/core/src/api/read/build.rs | 143 +-- bin/core/src/api/read/builder.rs | 72 +- bin/core/src/api/read/deployment.rs | 174 +-- bin/core/src/api/read/mod.rs | 145 +-- bin/core/src/api/read/permission.rs | 50 +- bin/core/src/api/read/procedure.rs | 88 +- bin/core/src/api/read/provider.rs | 115 +- bin/core/src/api/read/repo.rs | 98 +- bin/core/src/api/read/search.rs | 82 -- bin/core/src/api/read/server.rs | 513 ++++---- bin/core/src/api/read/server_template.rs | 75 +- bin/core/src/api/read/stack.rs | 205 ++-- bin/core/src/api/read/sync.rs | 103 +- bin/core/src/api/read/tag.rs | 34 +- bin/core/src/api/read/toml.rs | 109 +- bin/core/src/api/read/update.rs | 42 +- bin/core/src/api/read/user.rs | 72 +- bin/core/src/api/read/user_group.rs | 44 +- bin/core/src/api/read/variable.rs | 35 +- bin/core/src/api/user.rs | 106 +- bin/core/src/api/write/action.rs | 90 +- bin/core/src/api/write/alerter.rs | 96 +- bin/core/src/api/write/build.rs | 204 ++-- bin/core/src/api/write/builder.rs | 99 +- bin/core/src/api/write/deployment.rs | 138 ++- bin/core/src/api/write/description.rs | 53 +- bin/core/src/api/write/mod.rs | 40 +- bin/core/src/api/write/permissions.rs | 129 ++- bin/core/src/api/write/procedure.rs | 86 +- bin/core/src/api/write/provider.rs | 205 ++-- bin/core/src/api/write/repo.rs | 201 ++-- bin/core/src/api/write/server.rs | 78 +- bin/core/src/api/write/server_template.rs | 94 +- bin/core/src/api/write/service_user.rs | 134 +-- bin/core/src/api/write/stack.rs | 237 ++-- bin/core/src/api/write/sync.rs | 318 ++--- bin/core/src/api/write/tag.rs | 144 +-- bin/core/src/api/write/user.rs | 82 +- bin/core/src/api/write/user_group.rs | 138 +-- bin/core/src/api/write/variable.rs | 107 +- bin/core/src/auth/local.rs | 63 +- bin/core/src/cloud/aws/ec2.rs | 85 +- bin/core/src/helpers/mod.rs | 59 +- bin/core/src/helpers/procedure.rs | 247 ++-- bin/core/src/listener/resources.rs | 77 +- bin/core/src/main.rs | 20 +- bin/core/src/monitor/resources.rs | 2 +- bin/core/src/resource/mod.rs | 46 +- bin/core/src/resource/refresh.rs | 32 +- bin/core/src/resource/stack.rs | 21 +- bin/core/src/resource/sync.rs | 22 +- bin/core/src/resource2/crud.rs | 233 ++++ bin/core/src/resource2/mod.rs | 102 ++ bin/core/src/resource2/query.rs | 1027 +++++++++++++++++ bin/core/src/state.rs | 2 - bin/core/src/sync/deploy.rs | 51 +- bin/core/src/sync/execute.rs | 51 +- bin/core/src/sync/resources.rs | 10 +- bin/core/src/sync/user_groups.rs | 497 ++++---- bin/core/src/sync/variables.rs | 119 +- bin/periphery/Cargo.toml | 2 +- bin/periphery/src/api/build.rs | 35 +- bin/periphery/src/api/compose.rs | 157 ++- bin/periphery/src/api/container.rs | 232 ++-- bin/periphery/src/api/deploy.rs | 36 +- bin/periphery/src/api/git.rs | 103 +- bin/periphery/src/api/image.rs | 69 +- bin/periphery/src/api/mod.rs | 157 ++- bin/periphery/src/api/network.rs | 41 +- bin/periphery/src/api/stats.rs | 63 +- bin/periphery/src/api/volume.rs | 36 +- bin/periphery/src/compose.rs | 57 +- bin/periphery/src/config.rs | 27 - bin/periphery/src/helpers.rs | 30 +- bin/periphery/src/main.rs | 4 +- bin/periphery/src/router.rs | 27 +- bin/periphery/src/stats.rs | 26 +- client/core/rs/src/api/auth.rs | 17 +- client/core/rs/src/api/execute/action.rs | 8 +- client/core/rs/src/api/execute/build.rs | 11 +- client/core/rs/src/api/execute/deployment.rs | 32 +- client/core/rs/src/api/execute/procedure.rs | 8 +- client/core/rs/src/api/execute/repo.rs | 23 +- client/core/rs/src/api/execute/server.rs | 65 +- .../rs/src/api/execute/server_template.rs | 5 +- client/core/rs/src/api/execute/stack.rs | 38 +- client/core/rs/src/api/execute/sync.rs | 5 +- client/core/rs/src/api/read/action.rs | 17 +- client/core/rs/src/api/read/alert.rs | 8 +- client/core/rs/src/api/read/alerter.rs | 14 +- client/core/rs/src/api/read/build.rs | 29 +- client/core/rs/src/api/read/builder.rs | 14 +- client/core/rs/src/api/read/deployment.rs | 32 +- client/core/rs/src/api/read/mod.rs | 19 +- client/core/rs/src/api/read/permission.rs | 11 +- client/core/rs/src/api/read/procedure.rs | 17 +- client/core/rs/src/api/read/provider.rs | 14 +- client/core/rs/src/api/read/repo.rs | 20 +- client/core/rs/src/api/read/search.rs | 46 - client/core/rs/src/api/read/server.rs | 77 +- .../core/rs/src/api/read/server_template.rs | 14 +- client/core/rs/src/api/read/stack.rs | 35 +- client/core/rs/src/api/read/sync.rs | 20 +- client/core/rs/src/api/read/tag.rs | 8 +- client/core/rs/src/api/read/toml.rs | 8 +- client/core/rs/src/api/read/update.rs | 8 +- client/core/rs/src/api/read/user.rs | 17 +- client/core/rs/src/api/read/user_group.rs | 8 +- client/core/rs/src/api/read/variable.rs | 8 +- client/core/rs/src/api/user.rs | 14 +- client/core/rs/src/api/write/action.rs | 24 +- client/core/rs/src/api/write/alerter.rs | 18 +- client/core/rs/src/api/write/api_key.rs | 8 +- client/core/rs/src/api/write/build.rs | 27 +- client/core/rs/src/api/write/builder.rs | 18 +- client/core/rs/src/api/write/deployment.rs | 21 +- client/core/rs/src/api/write/description.rs | 5 +- client/core/rs/src/api/write/permissions.rs | 14 +- client/core/rs/src/api/write/procedure.rs | 18 +- client/core/rs/src/api/write/provider.rs | 20 +- client/core/rs/src/api/write/repo.rs | 27 +- client/core/rs/src/api/write/server.rs | 19 +- .../core/rs/src/api/write/server_template.rs | 18 +- client/core/rs/src/api/write/stack.rs | 31 +- client/core/rs/src/api/write/sync.rs | 35 +- client/core/rs/src/api/write/tags.rs | 14 +- client/core/rs/src/api/write/user.rs | 17 +- client/core/rs/src/api/write/user_group.rs | 20 +- client/core/rs/src/api/write/variable.rs | 17 +- client/core/rs/src/request.rs | 85 +- client/core/ts/package.json | 2 +- client/core/ts/src/responses.ts | 3 - client/core/ts/src/types.ts | 23 - client/periphery/rs/src/api/build.rs | 11 +- client/periphery/rs/src/api/compose.rs | 29 +- client/periphery/rs/src/api/container.rs | 59 +- client/periphery/rs/src/api/git.rs | 20 +- client/periphery/rs/src/api/image.rs | 17 +- client/periphery/rs/src/api/mod.rs | 26 +- client/periphery/rs/src/api/network.rs | 14 +- client/periphery/rs/src/api/stats.rs | 15 +- client/periphery/rs/src/api/volume.rs | 11 +- client/periphery/rs/src/lib.rs | 21 +- frontend/public/client/responses.d.ts | 1 - frontend/public/client/types.d.ts | 23 - lib/cache/README.md | 5 + lib/command/README.md | 3 + lib/environment_file/README.md | 8 + lib/formatting/README.md | 3 + lib/git/README.md | 3 + lib/logger/README.md | 3 + lib/logger/src/otel.rs | 7 +- lib/response/Cargo.toml | 15 + lib/response/src/lib.rs | 76 ++ runfile.toml | 8 +- test.core.config.toml | 43 - test.periphery.config.toml | 24 - 179 files changed, 7141 insertions(+), 5386 deletions(-) delete mode 100644 .dockerignore delete mode 100644 bin/core/src/api/read/search.rs create mode 100644 bin/core/src/resource2/crud.rs create mode 100644 bin/core/src/resource2/mod.rs create mode 100644 bin/core/src/resource2/query.rs delete mode 100644 client/core/rs/src/api/read/search.rs create mode 100644 lib/cache/README.md create mode 100644 lib/command/README.md create mode 100644 lib/environment_file/README.md create mode 100644 lib/formatting/README.md create mode 100644 lib/git/README.md create mode 100644 lib/logger/README.md create mode 100644 lib/response/Cargo.toml create mode 100644 lib/response/src/lib.rs delete mode 100644 test.core.config.toml delete mode 100644 test.periphery.config.toml diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index c4899da37..000000000 --- a/.dockerignore +++ /dev/null @@ -1,14 +0,0 @@ -/target -readme.md -typeshare.toml -LICENSE -*.code-workspace - -*/node_modules -*/dist - -creds.toml -.core-repos -.repos -.stacks -.ssl \ No newline at end of file diff --git a/.gitignore b/.gitignore index 80333d821..2b3a40717 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,12 @@ target -/frontend/build -/lib/ts_client/build node_modules dist .env .env.development .DS_Store +/frontend/build +/lib/ts_client/build + creds.toml -.komodo \ No newline at end of file +.dev \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ff5ef93ac..28f056033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arc-swap" @@ -197,7 +197,7 @@ dependencies = [ "aws-sdk-sts", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.60.7", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -254,9 +254,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" +checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -279,15 +279,15 @@ dependencies = [ [[package]] name = "aws-sdk-ec2" -version = "1.93.0" +version = "1.95.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9987ec31fe6dd2d81f2a534de8a752ac9dfc4b87305502807424296e6371af" +checksum = "facb3c68b989b2a8f4b26c4c0d8c5558b3a5d5335839bf2963b3932997a166d0" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -311,7 +311,7 @@ dependencies = [ "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.60.7", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -333,7 +333,7 @@ dependencies = [ "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.60.7", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -355,7 +355,7 @@ dependencies = [ "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.60.7", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -370,9 +370,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5619742a0d8f253be760bfbb8e8e8368c69e3587e4637af5754e488a611499b1" +checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -431,6 +431,15 @@ dependencies = [ "aws-smithy-types", ] +[[package]] +name = "aws-smithy-json" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095" +dependencies = [ + "aws-smithy-types", +] + [[package]] name = "aws-smithy-query" version = "0.60.7" @@ -443,9 +452,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.3" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be28bd063fa91fd871d131fc8b68d7cd4c5fa0869bea68daca50dcb1cbd76be2" +checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -643,7 +652,7 @@ dependencies = [ "hyper 1.5.1", "hyper-util", "pin-project-lite", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pemfile 2.2.0", "rustls-pki-types", "tokio", @@ -811,7 +820,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.3", + "thiserror 2.0.6", "tokio", "tokio-util", "tower-service", @@ -884,7 +893,7 @@ dependencies = [ [[package]] name = "cache" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "tokio", @@ -960,9 +969,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -970,9 +979,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", @@ -994,9 +1003,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cmake" @@ -1025,7 +1034,7 @@ dependencies = [ [[package]] name = "command" -version = "1.16.12" +version = "1.16.13" dependencies = [ "komodo_client", "run_command", @@ -1464,9 +1473,9 @@ dependencies = [ [[package]] name = "environment_file" -version = "1.16.12" +version = "1.16.13" dependencies = [ - "thiserror 2.0.3", + "thiserror 2.0.6", ] [[package]] @@ -1533,7 +1542,7 @@ dependencies = [ [[package]] name = "formatting" -version = "1.16.12" +version = "1.16.13" dependencies = [ "serror", ] @@ -1671,7 +1680,7 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "cache", @@ -2051,7 +2060,7 @@ dependencies = [ "http 1.1.0", "hyper 1.5.1", "hyper-util", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", @@ -2434,7 +2443,7 @@ dependencies = [ [[package]] name = "komodo_cli" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "clap", @@ -2450,7 +2459,7 @@ dependencies = [ [[package]] name = "komodo_client" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "async_timing_util", @@ -2470,7 +2479,7 @@ dependencies = [ "serde_json", "serror", "strum", - "thiserror 2.0.3", + "thiserror 2.0.6", "tokio", "tokio-tungstenite", "tokio-util", @@ -2481,7 +2490,7 @@ dependencies = [ [[package]] name = "komodo_core" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "async_timing_util", @@ -2520,7 +2529,8 @@ dependencies = [ "regex", "reqwest 0.12.9", "resolver_api", - "rustls 0.23.18", + "response", + "rustls 0.23.19", "serde", "serde_json", "serde_yaml", @@ -2542,12 +2552,11 @@ dependencies = [ [[package]] name = "komodo_periphery" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "async_timing_util", "axum", - "axum-extra", "axum-server", "bollard", "cache", @@ -2565,8 +2574,9 @@ dependencies = [ "merge_config_files", "periphery_client", "resolver_api", + "response", "run_command", - "rustls 0.23.18", + "rustls 0.23.19", "serde", "serde_json", "serror", @@ -2653,7 +2663,7 @@ dependencies = [ [[package]] name = "logger" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "komodo_client", @@ -3090,16 +3100,16 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "opentelemetry" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3cebff57f7dbd1255b44d8bddc2cebeb0ea677dbaa2e25a3070a91b318f660" +checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7" dependencies = [ "futures-core", "futures-sink", "js-sys", - "once_cell", "pin-project-lite", "thiserror 1.0.69", + "tracing", ] [[package]] @@ -3156,16 +3166,15 @@ checksum = "bc1b6902ff63b32ef6c489e8048c5e253e2e4a803ea3ea7e783914536eb15c52" [[package]] name = "opentelemetry_sdk" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b742c1cae4693792cc564e58d75a2a0ba29421a34a85b50da92efa89ecb2bc" +checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8" dependencies = [ "async-trait", "futures-channel", "futures-executor", "futures-util", "glob", - "once_cell", "opentelemetry", "percent-encoding", "rand", @@ -3351,7 +3360,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "periphery_client" -version = "1.16.12" +version = "1.16.13" dependencies = [ "anyhow", "komodo_client", @@ -3499,9 +3508,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.18", + "rustls 0.23.19", "socket2", - "thiserror 2.0.3", + "thiserror 2.0.6", "tokio", "tracing", ] @@ -3517,10 +3526,10 @@ dependencies = [ "rand", "ring 0.17.8", "rustc-hash 2.0.0", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.6", "tinyvec", "tracing", "web-time", @@ -3727,7 +3736,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.18", + "rustls 0.23.19", "rustls-native-certs 0.8.1", "rustls-pemfile 2.2.0", "rustls-pki-types", @@ -3825,28 +3834,35 @@ dependencies = [ [[package]] name = "resolver_api" -version = "1.1.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86d2e3a6e01f92f8f964bc16a5e579028c02fa625612e5e1f159a6474e47969" +checksum = "3faf1143e798058d8287e5a019fc8a46542c1ef152a29bc3b3d9bf886363016b" dependencies = [ - "anyhow", "resolver_api_derive", - "serde", - "serde_json", - "thiserror 1.0.69", ] [[package]] name = "resolver_api_derive" -version = "1.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35ef0d79735ffcdf944895605c107b7f04ea097ce5a39d3191f9b2d82f73f7b" +checksum = "e45ff368040e7f3787e97e6f3a97718ca08e0553587747bd4d0bf3ca2b899963" dependencies = [ "proc-macro2", "quote", "syn 2.0.89", ] +[[package]] +name = "response" +version = "1.16.13" +dependencies = [ + "anyhow", + "axum", + "serde", + "serde_json", + "serror", +] + [[package]] name = "retry-policies" version = "0.1.2" @@ -3991,9 +4007,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.18" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9cc1d47e243d655ace55ed38201c19ae02c148ae56412ab8750e8f0166ab7f" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "aws-lc-rs", "log", @@ -4358,9 +4374,9 @@ dependencies = [ [[package]] name = "serror" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715a997753611604c722411afbe11f83a89e00e39323dc9016db96a86cc04fc8" +checksum = "47348b77230bc82d8c6f6452df44988f73e42a3bd68098c6236fcd656605b5e6" dependencies = [ "anyhow", "axum", @@ -4619,9 +4635,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.32.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af" +checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46" dependencies = [ "core-foundation-sys", "libc", @@ -4705,11 +4721,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.6", ] [[package]] @@ -4725,9 +4741,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" dependencies = [ "proc-macro2", "quote", @@ -4802,9 +4818,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.41.1" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -4845,7 +4861,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.18", + "rustls 0.23.19", "rustls-pki-types", "tokio", ] @@ -4875,9 +4891,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -5041,9 +5057,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -5103,9 +5119,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -5113,9 +5129,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "nu-ansi-term", "serde", @@ -5475,7 +5491,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9b0540e91e49de3817c314da0dd3bc518093ceacc6ea5327cb0e1eb073e5189" dependencies = [ - "thiserror 2.0.3", + "thiserror 2.0.6", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 91e888963..43e9bc168 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,23 +8,20 @@ members = [ ] [workspace.package] -version = "1.16.12" +version = "1.16.13" edition = "2021" authors = ["mbecker20 "] license = "GPL-3.0-or-later" repository = "https://github.com/mbecker20/komodo" homepage = "https://komo.do" -[patch.crates-io] -# komodo_client = { path = "client/core/rs" } - [workspace.dependencies] # LOCAL -# komodo_client = "1.15.6" komodo_client = { path = "client/core/rs" } periphery_client = { path = "client/periphery/rs" } environment_file = { path = "lib/environment_file" } formatting = { path = "lib/formatting" } +response = { path = "lib/response" } command = { path = "lib/command" } logger = { path = "lib/logger" } cache = { path = "lib/cache" } @@ -32,7 +29,7 @@ git = { path = "lib/git" } # MOGH run_command = { version = "0.0.6", features = ["async_tokio"] } -serror = { version = "0.4.7", default-features = false } +serror = { version = "0.4.8", default-features = false } slack = { version = "0.3.0", package = "slack_client_rs", default-features = false, features = ["rustls"] } derive_default_builder = "0.1.8" derive_empty_traits = "0.1.0" @@ -41,15 +38,15 @@ async_timing_util = "1.0.0" partial_derive2 = "0.4.3" derive_variants = "1.0.0" mongo_indexed = "2.0.1" -resolver_api = "1.1.1" +resolver_api = "3.0.0" toml_pretty = "1.1.2" mungos = "1.1.0" svi = "1.0.1" # ASYNC reqwest = { version = "0.12.9", default-features = false, features = ["json", "rustls-tls"] } -tokio = { version = "1.41.1", features = ["full"] } -tokio-util = "0.7.12" +tokio = { version = "1.42.0", features = ["full"] } +tokio-util = "0.7.13" futures = "0.3.31" futures-util = "0.3.31" @@ -69,20 +66,20 @@ serde_yaml = "0.9.34" toml = "0.8.19" # ERROR -anyhow = "1.0.93" -thiserror = "2.0.3" +anyhow = "1.0.94" +thiserror = "2.0.6" # LOGGING opentelemetry-otlp = { version = "0.27.0", features = ["tls-roots", "reqwest-rustls"] } -opentelemetry_sdk = { version = "0.27.0", features = ["rt-tokio"] } -tracing-subscriber = { version = "0.3.18", features = ["json"] } +opentelemetry_sdk = { version = "0.27.1", features = ["rt-tokio"] } +tracing-subscriber = { version = "0.3.19", features = ["json"] } opentelemetry-semantic-conventions = "0.27.0" tracing-opentelemetry = "0.28.0" -opentelemetry = "0.27.0" -tracing = "0.1.40" +opentelemetry = "0.27.1" +tracing = "0.1.41" # CONFIG -clap = { version = "4.5.21", features = ["derive"] } +clap = { version = "4.5.23", features = ["derive"] } dotenvy = "0.15.7" envy = "0.4.2" @@ -93,7 +90,7 @@ urlencoding = "2.1.3" nom_pem = "4.0.0" bcrypt = "0.16.0" base64 = "0.22.1" -rustls = "0.23.18" +rustls = "0.23.19" hmac = "0.12.1" sha2 = "0.10.8" rand = "0.8.5" @@ -102,11 +99,11 @@ hex = "0.4.3" # SYSTEM bollard = "0.18.1" -sysinfo = "0.32.0" +sysinfo = "0.33.0" # CLOUD aws-config = "1.5.10" -aws-sdk-ec2 = "1.91.0" +aws-sdk-ec2 = "1.95.0" # MISC derive_builder = "0.20.2" diff --git a/bin/cli/Cargo.toml b/bin/cli/Cargo.toml index f3f3dd67e..7ab8f3c06 100644 --- a/bin/cli/Cargo.toml +++ b/bin/cli/Cargo.toml @@ -16,6 +16,7 @@ path = "src/main.rs" [dependencies] # local +# komodo_client = "1.16.12" komodo_client.workspace = true # external tracing-subscriber.workspace = true diff --git a/bin/core/Cargo.toml b/bin/core/Cargo.toml index cec4c2bfa..180bd91bc 100644 --- a/bin/core/Cargo.toml +++ b/bin/core/Cargo.toml @@ -19,6 +19,7 @@ komodo_client = { workspace = true, features = ["mongo"] } periphery_client.workspace = true environment_file.workspace = true formatting.workspace = true +response.workspace = true command.workspace = true logger.workspace = true cache.workspace = true diff --git a/bin/core/multi-arch.Dockerfile b/bin/core/multi-arch.Dockerfile index 411cf1538..326ab35c5 100644 --- a/bin/core/multi-arch.Dockerfile +++ b/bin/core/multi-arch.Dockerfile @@ -47,4 +47,4 @@ LABEL org.opencontainers.image.source=https://github.com/mbecker20/komodo LABEL org.opencontainers.image.description="Komodo Core" LABEL org.opencontainers.image.licenses=GPL-3.0 -ENTRYPOINT [ "core" ] \ No newline at end of file +CMD [ "core" ] \ No newline at end of file diff --git a/bin/core/single-arch.Dockerfile b/bin/core/single-arch.Dockerfile index 8a3a22ba2..4db635437 100644 --- a/bin/core/single-arch.Dockerfile +++ b/bin/core/single-arch.Dockerfile @@ -41,4 +41,4 @@ LABEL org.opencontainers.image.source=https://github.com/mbecker20/komodo LABEL org.opencontainers.image.description="Komodo Core" LABEL org.opencontainers.image.licenses=GPL-3.0 -ENTRYPOINT [ "core" ] \ No newline at end of file +CMD [ "core" ] \ No newline at end of file diff --git a/bin/core/src/api/auth.rs b/bin/core/src/api/auth.rs index 91ca0ef4e..97a03654e 100644 --- a/bin/core/src/api/auth.rs +++ b/bin/core/src/api/auth.rs @@ -1,13 +1,12 @@ use std::{sync::OnceLock, time::Instant}; -use anyhow::anyhow; use axum::{http::HeaderMap, routing::post, Router}; -use axum_extra::{headers::ContentType, TypedHeader}; +use derive_variants::{EnumVariants, ExtractVariant}; use komodo_client::{api::auth::*, entities::user::User}; -use reqwest::StatusCode; -use resolver_api::{derive::Resolver, Resolve, Resolver}; +use resolver_api::Resolve; +use response::Response; use serde::{Deserialize, Serialize}; -use serror::{AddStatusCode, Json}; +use serror::Json; use typeshare::typeshare; use uuid::Uuid; @@ -20,13 +19,21 @@ use crate::{ }, config::core_config, helpers::query::get_user, - state::{jwt_client, State}, + state::jwt_client, }; +pub struct AuthArgs { + pub headers: HeaderMap, +} + #[typeshare] -#[derive(Serialize, Deserialize, Debug, Clone, Resolver)] -#[resolver_target(State)] -#[resolver_args(HeaderMap)] +#[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 { @@ -66,27 +73,20 @@ pub fn router() -> Router { async fn handler( headers: HeaderMap, Json(request): Json, -) -> serror::Result<(TypedHeader, String)> { +) -> serror::Result { let timer = Instant::now(); let req_id = Uuid::new_v4(); - debug!("/auth request {req_id} | METHOD: {}", request.req_type()); - let res = State.resolve_request(request, headers).await.map_err( - |e| match e { - resolver_api::Error::Serialization(e) => { - anyhow!("{e:?}").context("response serialization error") - } - resolver_api::Error::Inner(e) => e, - }, + debug!( + "/auth request {req_id} | METHOD: {:?}", + request.extract_variant() ); + let res = request.resolve(&AuthArgs { headers }).await; if let Err(e) = &res { - debug!("/auth request {req_id} | error: {e:#}"); + debug!("/auth request {req_id} | error: {:#}", e.error); } let elapsed = timer.elapsed(); debug!("/auth request {req_id} | resolve time: {elapsed:?}"); - Ok(( - TypedHeader(ContentType::json()), - res.status_code(StatusCode::UNAUTHORIZED)?, - )) + res.map(|res| res.0) } fn login_options_reponse() -> &'static GetLoginOptionsResponse { @@ -112,38 +112,34 @@ fn login_options_reponse() -> &'static GetLoginOptionsResponse { }) } -impl Resolve for State { +impl Resolve for GetLoginOptions { #[instrument(name = "GetLoginOptions", level = "debug", skip(self))] async fn resolve( - &self, - _: GetLoginOptions, - _: HeaderMap, - ) -> anyhow::Result { + self, + _: &AuthArgs, + ) -> serror::Result { Ok(*login_options_reponse()) } } -impl Resolve for State { +impl Resolve for ExchangeForJwt { #[instrument(name = "ExchangeForJwt", level = "debug", skip(self))] async fn resolve( - &self, - ExchangeForJwt { token }: ExchangeForJwt, - _: HeaderMap, - ) -> anyhow::Result { - let jwt = jwt_client().redeem_exchange_token(&token).await?; - let res = ExchangeForJwtResponse { jwt }; - Ok(res) + self, + _: &AuthArgs, + ) -> serror::Result { + let jwt = jwt_client().redeem_exchange_token(&self.token).await?; + Ok(ExchangeForJwtResponse { jwt }) } } -impl Resolve for State { +impl Resolve for GetUser { #[instrument(name = "GetUser", level = "debug", skip(self))] async fn resolve( - &self, - GetUser {}: GetUser, - headers: HeaderMap, - ) -> anyhow::Result { - let user_id = get_user_id_from_headers(&headers).await?; - get_user(&user_id).await + self, + AuthArgs { headers }: &AuthArgs, + ) -> serror::Result { + let user_id = get_user_id_from_headers(headers).await?; + Ok(get_user(&user_id).await?) } } diff --git a/bin/core/src/api/execute/action.rs b/bin/core/src/api/execute/action.rs index 05af563cc..1a9cab3b9 100644 --- a/bin/core/src/api/execute/action.rs +++ b/bin/core/src/api/execute/action.rs @@ -13,11 +13,8 @@ use komodo_client::{ user::{CreateApiKey, CreateApiKeyResponse, DeleteApiKey}, }, entities::{ - action::Action, - config::core::CoreConfig, - permission::PermissionLevel, - update::Update, - user::{action_user, User}, + action::Action, config::core::CoreConfig, + permission::PermissionLevel, update::Update, user::action_user, }, }; use mungos::{by_id::update_one_by_id, mongodb::bson::to_document}; @@ -25,7 +22,7 @@ use resolver_api::Resolve; use tokio::fs; use crate::{ - api::execute::ExecuteRequest, + api::{execute::ExecuteRequest, user::UserArgs}, config::core_config, helpers::{ interpolate::{ @@ -37,9 +34,11 @@ use crate::{ update::update_update, }, resource::{self, refresh_action_state_cache}, - state::{action_states, db_client, State}, + state::{action_states, db_client}, }; +use super::ExecuteArgs; + impl super::BatchExecute for BatchRunAction { type Resource = Action; fn single_request(action: String) -> ExecuteRequest { @@ -47,26 +46,27 @@ impl super::BatchExecute for BatchRunAction { } } -impl Resolve for State { +impl Resolve for BatchRunAction { #[instrument(name = "BatchRunAction", skip(self, user), fields(user_id = user.id))] async fn resolve( - &self, - BatchRunAction { pattern }: BatchRunAction, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RunAction", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for RunAction { + #[instrument(name = "RunAction", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - RunAction { action }: RunAction, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let mut action = resource::get_check_permissions::( - &action, + &self.action, &user, PermissionLevel::Execute, ) @@ -83,17 +83,18 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.running = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; - let CreateApiKeyResponse { key, secret } = State - .resolve( - CreateApiKey { - name: update.id.clone(), - expires: 0, - }, - action_user().to_owned(), - ) - .await?; + let CreateApiKeyResponse { key, secret } = CreateApiKey { + name: update.id.clone(), + expires: 0, + } + .resolve(&UserArgs { + user: action_user().to_owned(), + }) + .await?; let contents = &mut action.config.file_contents; @@ -133,12 +134,15 @@ impl Resolve for State { cleanup_run(file + ".js", &path).await; - if let Err(e) = State - .resolve(DeleteApiKey { key }, action_user().to_owned()) + if let Err(e) = (DeleteApiKey { key }) + .resolve(&UserArgs { + user: action_user().to_owned(), + }) .await { warn!( - "Failed to delete API key after action execution | {e:#}" + "Failed to delete API key after action execution | {:#}", + e.error ); }; @@ -171,7 +175,7 @@ async fn interpolate( update: &mut Update, key: String, secret: String, -) -> anyhow::Result> { +) -> serror::Result> { let mut vars_and_secrets = get_variables_and_secrets().await?; vars_and_secrets diff --git a/bin/core/src/api/execute/build.rs b/bin/core/src/api/execute/build.rs index 6f63ce243..6cc97ee86 100644 --- a/bin/core/src/api/execute/build.rs +++ b/bin/core/src/api/execute/build.rs @@ -17,7 +17,7 @@ use komodo_client::{ komodo_timestamp, permission::PermissionLevel, update::{Log, Update}, - user::{auto_redeploy_user, User}, + user::auto_redeploy_user, }, }; use mungos::{ @@ -49,10 +49,10 @@ use crate::{ update::{init_execution_update, update_update}, }, resource::{self, refresh_build_state_cache}, - state::{action_states, db_client, State}, + state::{action_states, db_client}, }; -use super::ExecuteRequest; +use super::{ExecuteArgs, ExecuteRequest}; impl super::BatchExecute for BatchRunBuild { type Resource = Build; @@ -61,26 +61,27 @@ impl super::BatchExecute for BatchRunBuild { } } -impl Resolve for State { - #[instrument(name = "BatchRunBuild", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchRunBuild { + #[instrument(name = "BatchRunBuild", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchRunBuild { pattern }: BatchRunBuild, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RunBuild", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for RunBuild { + #[instrument(name = "RunBuild", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - RunBuild { build }: RunBuild, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let mut build = resource::get_check_permissions::( - &build, + &self.build, &user, PermissionLevel::Execute, ) @@ -88,7 +89,7 @@ impl Resolve for State { let mut vars_and_secrets = get_variables_and_secrets().await?; if build.config.builder_id.is_empty() { - return Err(anyhow!("Must attach builder to RunBuild")); + return Err(anyhow!("Must attach builder to RunBuild").into()); } // get the action state for the build (or insert default). @@ -103,6 +104,9 @@ impl Resolve for State { if build.config.auto_increment_version { build.config.version.increment(); } + + let mut update = update.clone(); + update.version = build.config.version; update_update(update.clone()).await?; @@ -408,7 +412,7 @@ impl Resolve for State { }); } - Ok(update) + Ok(update.clone()) } } @@ -418,7 +422,7 @@ async fn handle_early_return( build_id: String, build_name: String, is_cancel: bool, -) -> anyhow::Result { +) -> serror::Result { update.finalize(); // Need to manually update the update before cache refresh, // and before broadcast with add_update. @@ -456,7 +460,7 @@ async fn handle_early_return( send_alerts(&[alert]).await }); } - Ok(update) + Ok(update.clone()) } pub async fn validate_cancel_build( @@ -495,25 +499,28 @@ 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")); + return Err( + anyhow!("Build has already been cancelled"), + ); } } - (None, _) => return Err(anyhow!("No build in progress")), + (None, _) => { + return Err(anyhow!("No build in progress")) + } _ => {} }; } Ok(()) } -impl Resolve for State { - #[instrument(name = "CancelBuild", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for CancelBuild { + #[instrument(name = "CancelBuild", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - CancelBuild { build }: CancelBuild, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let build = resource::get_check_permissions::( - &build, + &self.build, &user, PermissionLevel::Execute, ) @@ -527,9 +534,11 @@ impl Resolve for State { .and_then(|s| s.get().ok().map(|s| s.building)) .unwrap_or_default() { - return Err(anyhow!("Build is not building.")); + return Err(anyhow!("Build is not building.").into()); } + let mut update = update.clone(); + update.push_simple_log( "cancel triggered", "the build cancel has been triggered", @@ -593,16 +602,13 @@ async fn handle_post_build_redeploy(build_id: &str) { let user = auto_redeploy_user().to_owned(); let res = async { let update = init_execution_update(&req, &user).await?; - State - .resolve( - Deploy { - deployment: deployment.id.clone(), - stop_signal: None, - stop_time: None, - }, - (user, update), - ) - .await + Deploy { + deployment: deployment.id.clone(), + stop_signal: None, + stop_time: None, + } + .resolve(&ExecuteArgs { user, update }) + .await } .await; Some((deployment.id.clone(), res)) @@ -616,7 +622,10 @@ async fn handle_post_build_redeploy(build_id: &str) { continue; }; if let Err(e) = res { - warn!("failed post build redeploy for deployment {id}: {e:#}"); + warn!( + "failed post build redeploy for deployment {id}: {:#}", + e.error + ); } } } @@ -636,14 +645,17 @@ async fn validate_account_extract_registry_token( }, .. }: &Build, -) -> anyhow::Result> { +) -> serror::Result> { if domain.is_empty() { return Ok(None); } if account.is_empty() { - return Err(anyhow!( - "Must attach account to use registry provider {domain}" - )); + return Err( + anyhow!( + "Must attach account to use registry provider {domain}" + ) + .into(), + ); } let registry_token = registry_token(domain, account).await.with_context( diff --git a/bin/core/src/api/execute/deployment.rs b/bin/core/src/api/execute/deployment.rs index cbf8d9a89..d320aafd3 100644 --- a/bin/core/src/api/execute/deployment.rs +++ b/bin/core/src/api/execute/deployment.rs @@ -35,10 +35,10 @@ use crate::{ }, monitor::update_cache_for_server, resource, - state::{action_states, State}, + state::action_states, }; -use super::ExecuteRequest; +use super::{ExecuteArgs, ExecuteRequest}; impl super::BatchExecute for BatchDeploy { type Resource = Deployment; @@ -51,14 +51,16 @@ impl super::BatchExecute for BatchDeploy { } } -impl Resolve for State { - #[instrument(name = "BatchDeploy", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchDeploy { + #[instrument(name = "BatchDeploy", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchDeploy { pattern }: BatchDeploy, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, user) + .await?, + ) } } @@ -87,19 +89,14 @@ async fn setup_deployment_execution( Ok((deployment, server)) } -impl Resolve for State { - #[instrument(name = "Deploy", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for Deploy { + #[instrument(name = "Deploy", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - Deploy { - deployment, - stop_signal, - stop_time, - }: Deploy, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (mut deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -112,6 +109,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.deploying = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -237,8 +236,8 @@ impl Resolve for State { match periphery_client(&server)? .request(api::container::Deploy { deployment, - stop_signal, - stop_time, + stop_signal: self.stop_signal, + stop_time: self.stop_time, registry_token, replacers: secret_replacers.into_iter().collect(), }) @@ -378,14 +377,14 @@ pub async fn pull_deployment_inner( res } -impl Resolve for State { +impl Resolve for PullDeployment { + #[instrument(name = "PullDeployment", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PullDeployment { deployment }: PullDeployment, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -398,6 +397,7 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pulling = true)?; + let mut update = update.clone(); // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -411,15 +411,14 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "StartDeployment", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for StartDeployment { + #[instrument(name = "StartDeployment", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - StartDeployment { deployment }: StartDeployment, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -432,6 +431,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.starting = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -457,15 +458,14 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "RestartDeployment", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for RestartDeployment { + #[instrument(name = "RestartDeployment", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - RestartDeployment { deployment }: RestartDeployment, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -478,6 +478,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.restarting = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -505,15 +507,14 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PauseDeployment", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PauseDeployment { + #[instrument(name = "PauseDeployment", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PauseDeployment { deployment }: PauseDeployment, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -526,6 +527,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pausing = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -551,15 +554,14 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "UnpauseDeployment", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for UnpauseDeployment { + #[instrument(name = "UnpauseDeployment", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - UnpauseDeployment { deployment }: UnpauseDeployment, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, &user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -572,6 +574,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.unpausing = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -599,19 +603,14 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "StopDeployment", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for StopDeployment { + #[instrument(name = "StopDeployment", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - StopDeployment { - deployment, - signal, - time, - }: StopDeployment, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, &user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -624,16 +623,20 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.stopping = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; let log = match periphery_client(&server)? .request(api::container::StopContainer { name: deployment.name, - signal: signal + signal: self + .signal .unwrap_or(deployment.config.termination_signal) .into(), - time: time + time: self + .time .unwrap_or(deployment.config.termination_timeout) .into(), }) @@ -666,31 +669,30 @@ impl super::BatchExecute for BatchDestroyDeployment { } } -impl Resolve for State { - #[instrument(name = "BatchDestroyDeployment", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchDestroyDeployment { + #[instrument(name = "BatchDestroyDeployment", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchDestroyDeployment { pattern }: BatchDestroyDeployment, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user) - .await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::( + &self.pattern, + user, + ) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DestroyDeployment", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DestroyDeployment { + #[instrument(name = "DestroyDeployment", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - DestroyDeployment { - deployment, - signal, - time, - }: DestroyDeployment, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (deployment, server) = - setup_deployment_execution(&deployment, &user).await?; + setup_deployment_execution(&self.deployment, user).await?; // get the action state for the deployment (or insert default). let action_state = action_states() @@ -703,16 +705,20 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.destroying = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; let log = match periphery_client(&server)? .request(api::container::RemoveContainer { name: deployment.name, - signal: signal + signal: self + .signal .unwrap_or(deployment.config.termination_signal) .into(), - time: time + time: self + .time .unwrap_or(deployment.config.termination_timeout) .into(), }) diff --git a/bin/core/src/api/execute/mod.rs b/bin/core/src/api/execute/mod.rs index 714dd11f2..eb31882bd 100644 --- a/bin/core/src/api/execute/mod.rs +++ b/bin/core/src/api/execute/mod.rs @@ -1,6 +1,6 @@ -use std::time::Instant; +use std::{pin::Pin, time::Instant}; -use anyhow::{anyhow, Context}; +use anyhow::Context; use axum::{middleware, routing::post, Extension, Router}; use axum_extra::{headers::ContentType, TypedHeader}; use derive_variants::{EnumVariants, ExtractVariant}; @@ -15,7 +15,8 @@ use komodo_client::{ }, }; use mungos::by_id::find_one_by_id; -use resolver_api::{derive::Resolver, Resolver}; +use resolver_api::Resolve; +use response::JsonString; use serde::{Deserialize, Serialize}; use serror::Json; use typeshare::typeshare; @@ -25,7 +26,7 @@ use crate::{ auth::auth_request, helpers::update::{init_execution_update, update_update}, resource::{list_full_for_user_using_pattern, KomodoResource}, - state::{db_client, State}, + state::db_client, }; mod action; @@ -42,13 +43,19 @@ pub use { deployment::pull_deployment_inner, stack::pull_stack_inner, }; +pub struct ExecuteArgs { + pub user: User, + pub update: Update, +} + #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Resolver, EnumVariants, + Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants, )] #[variant_derive(Debug)] -#[resolver_target(State)] -#[resolver_args((User, Update))] +#[args(ExecuteArgs)] +#[response(JsonString)] +#[error(serror::Error)] #[serde(tag = "type", content = "params")] pub enum ExecuteRequest { // ==== SERVER ==== @@ -153,64 +160,73 @@ pub enum ExecutionResult { Batch(String), } -pub async fn inner_handler( +pub fn inner_handler( request: ExecuteRequest, user: User, -) -> anyhow::Result { - let req_id = Uuid::new_v4(); +) -> Pin< + Box< + dyn std::future::Future> + + Send, + >, +> { + Box::pin(async move { + let req_id = Uuid::new_v4(); - // need to validate no cancel is active before any update is created. - build::validate_cancel_build(&request).await?; + // need to validate no cancel is active before any update is created. + build::validate_cancel_build(&request).await?; - let update = init_execution_update(&request, &user).await?; + let update = init_execution_update(&request, &user).await?; - // This will be the case for the Batch exections, - // they don't have their own updates. - // The batch calls also call "inner_handler" themselves, - // and in their case will spawn tasks, so that isn't necessary - // here either. - if update.operation == Operation::None { - return Ok(ExecutionResult::Batch( - task(req_id, request, user, update).await?, - )); - } - - let handle = - tokio::spawn(task(req_id, request, user, update.clone())); - - tokio::spawn({ - let update_id = update.id.clone(); - async move { - let log = match handle.await { - Ok(Err(e)) => { - warn!("/execute request {req_id} task error: {e:#}",); - Log::error("task error", format_serror(&e.into())) - } - Err(e) => { - warn!("/execute request {req_id} spawn error: {e:?}",); - Log::error("spawn error", format!("{e:#?}")) - } - _ => return, - }; - let res = async { - 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")?; - update.logs.push(log); - update.finalize(); - update_update(update).await - } - .await; - - if let Err(e) = res { - warn!("failed to update update with task error log | {e:#}"); - } + // This will be the case for the Batch exections, + // they don't have their own updates. + // The batch calls also call "inner_handler" themselves, + // and in their case will spawn tasks, so that isn't necessary + // here either. + if update.operation == Operation::None { + return Ok(ExecutionResult::Batch( + task(req_id, request, user, update).await?, + )); } - }); - Ok(ExecutionResult::Single(update)) + let handle = + tokio::spawn(task(req_id, request, user, update.clone())); + + tokio::spawn({ + let update_id = update.id.clone(); + async move { + let log = match handle.await { + Ok(Err(e)) => { + warn!("/execute request {req_id} task error: {e:#}",); + Log::error("task error", format_serror(&e.into())) + } + Err(e) => { + warn!("/execute request {req_id} spawn error: {e:?}",); + Log::error("spawn error", format!("{e:#?}")) + } + _ => return, + }; + let res = async { + 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")?; + update.logs.push(log); + update.finalize(); + update_update(update).await + } + .await; + + if let Err(e) = res { + warn!( + "failed to update update with task error log | {e:#}" + ); + } + } + }); + + Ok(ExecutionResult::Single(update)) + }) } #[instrument( @@ -231,15 +247,14 @@ async fn task( info!("/execute request {req_id} | user: {}", user.username); let timer = Instant::now(); - let res = State - .resolve_request(request, (user, update)) - .await - .map_err(|e| match e { - resolver_api::Error::Serialization(e) => { - anyhow!("{e:?}").context("response serialization error") - } - resolver_api::Error::Inner(e) => e, - }); + let res = match request.resolve(&ExecuteArgs { user, update }).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!("/execute request {req_id} error: {e:#}"); diff --git a/bin/core/src/api/execute/procedure.rs b/bin/core/src/api/execute/procedure.rs index 28a7d5db5..a0f5d3478 100644 --- a/bin/core/src/api/execute/procedure.rs +++ b/bin/core/src/api/execute/procedure.rs @@ -17,10 +17,10 @@ use tokio::sync::Mutex; use crate::{ helpers::{procedure::execute_procedure, update::update_update}, resource::{self, refresh_procedure_state_cache}, - state::{action_states, db_client, State}, + state::{action_states, db_client}, }; -use super::ExecuteRequest; +use super::{ExecuteArgs, ExecuteRequest}; impl super::BatchExecute for BatchRunProcedure { type Resource = Procedure; @@ -29,25 +29,29 @@ impl super::BatchExecute for BatchRunProcedure { } } -impl Resolve for State { - #[instrument(name = "BatchRunProcedure", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchRunProcedure { + #[instrument(name = "BatchRunProcedure", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchRunProcedure { pattern }: BatchRunProcedure, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RunProcedure", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for RunProcedure { + #[instrument(name = "RunProcedure", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - RunProcedure { procedure }: RunProcedure, - (user, update): (User, Update), - ) -> anyhow::Result { - resolve_inner(procedure, user, update).await + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { + Ok( + resolve_inner(self.procedure, user.clone(), update.clone()) + .await?, + ) } } diff --git a/bin/core/src/api/execute/repo.rs b/bin/core/src/api/execute/repo.rs index fd924adce..1eeb4781e 100644 --- a/bin/core/src/api/execute/repo.rs +++ b/bin/core/src/api/execute/repo.rs @@ -12,7 +12,6 @@ use komodo_client::{ repo::Repo, server::Server, update::{Log, Update}, - user::User, }, }; use mungos::{ @@ -28,6 +27,7 @@ use tokio_util::sync::CancellationToken; use crate::{ alert::send_alerts, + api::write::WriteArgs, helpers::{ builder::{cleanup_builder_instance, get_builder_periphery}, channel::repo_cancel_channel, @@ -42,10 +42,10 @@ use crate::{ update::update_update, }, resource::{self, refresh_repo_state_cache}, - state::{action_states, db_client, State}, + state::{action_states, db_client}, }; -use super::ExecuteRequest; +use super::{ExecuteArgs, ExecuteRequest}; impl super::BatchExecute for BatchCloneRepo { type Resource = Repo; @@ -54,27 +54,28 @@ impl super::BatchExecute for BatchCloneRepo { } } -impl Resolve for State { - #[instrument(name = "BatchCloneRepo", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchCloneRepo { + #[instrument(name = "BatchCloneRepo", skip( user), fields(user_id = user.id))] async fn resolve( - &self, - BatchCloneRepo { pattern }: BatchCloneRepo, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "CloneRepo", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for CloneRepo { + #[instrument(name = "CloneRepo", skip( user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - CloneRepo { repo }: CloneRepo, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let mut repo = resource::get_check_permissions::( - &repo, - &user, + &self.repo, + user, PermissionLevel::Execute, ) .await?; @@ -88,10 +89,11 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.cloning = true)?; + let mut update = update.clone(); update_update(update.clone()).await?; if repo.config.server_id.is_empty() { - return Err(anyhow!("repo has no server attached")); + return Err(anyhow!("repo has no server attached").into()); } let git_token = git_token( @@ -141,9 +143,10 @@ impl Resolve for State { update_last_pulled_time(&repo.name).await; } - if let Err(e) = State - .resolve(RefreshRepoCache { repo: repo.id }, user) + if let Err(e) = (RefreshRepoCache { repo: repo.id }) + .resolve(&WriteArgs { user: user.clone() }) .await + .map_err(|e| e.error) .context("Failed to refresh repo cache") { update.push_error_log( @@ -163,26 +166,27 @@ impl super::BatchExecute for BatchPullRepo { } } -impl Resolve for State { - #[instrument(name = "BatchPullRepo", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchPullRepo { + #[instrument(name = "BatchPullRepo", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchPullRepo { pattern }: BatchPullRepo, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, &user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "PullRepo", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PullRepo { + #[instrument(name = "PullRepo", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PullRepo { repo }: PullRepo, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let mut repo = resource::get_check_permissions::( - &repo, + &self.repo, &user, PermissionLevel::Execute, ) @@ -197,10 +201,12 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pulling = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; if repo.config.server_id.is_empty() { - return Err(anyhow!("repo has no server attached")); + return Err(anyhow!("repo has no server attached").into()); } let git_token = git_token( @@ -254,9 +260,10 @@ impl Resolve for State { update_last_pulled_time(&repo.name).await; } - if let Err(e) = State - .resolve(RefreshRepoCache { repo: repo.id }, user) + if let Err(e) = (RefreshRepoCache { repo: repo.id }) + .resolve(&WriteArgs { user: user.clone() }) .await + .map_err(|e| e.error) .context("Failed to refresh repo cache") { update.push_error_log( @@ -272,7 +279,7 @@ impl Resolve for State { #[instrument(skip_all, fields(update_id = update.id))] async fn handle_server_update_return( update: Update, -) -> anyhow::Result { +) -> serror::Result { // Need to manually update the update before cache refresh, // and before broadcast with add_update. // The Err case of to_document should be unreachable, @@ -314,33 +321,34 @@ impl super::BatchExecute for BatchBuildRepo { } } -impl Resolve for State { - #[instrument(name = "BatchBuildRepo", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchBuildRepo { + #[instrument(name = "BatchBuildRepo", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchBuildRepo { pattern }: BatchBuildRepo, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "BuildRepo", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for BuildRepo { + #[instrument(name = "BuildRepo", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - BuildRepo { repo }: BuildRepo, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let mut repo = resource::get_check_permissions::( - &repo, - &user, + &self.repo, + user, PermissionLevel::Execute, ) .await?; if repo.config.builder_id.is_empty() { - return Err(anyhow!("Must attach builder to BuildRepo")); + return Err(anyhow!("Must attach builder to BuildRepo").into()); } // get the action state for the repo (or insert default). @@ -352,6 +360,7 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.building = true)?; + let mut update = update.clone(); update_update(update.clone()).await?; let git_token = git_token( @@ -429,7 +438,8 @@ impl Resolve for State { return handle_builder_early_return( update, repo.id, repo.name, false, ) - .await; + .await + .map_err(Into::into); } }; @@ -547,7 +557,7 @@ async fn handle_builder_early_return( repo_id: String, repo_name: String, is_cancel: bool, -) -> anyhow::Result { +) -> serror::Result { update.finalize(); // Need to manually update the update before cache refresh, // and before broadcast with add_update. @@ -635,16 +645,15 @@ pub async fn validate_cancel_repo_build( Ok(()) } -impl Resolve for State { - #[instrument(name = "CancelRepoBuild", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for CancelRepoBuild { + #[instrument(name = "CancelRepoBuild", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - CancelRepoBuild { repo }: CancelRepoBuild, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let repo = resource::get_check_permissions::( - &repo, - &user, + &self.repo, + user, PermissionLevel::Execute, ) .await?; @@ -657,9 +666,11 @@ impl Resolve for State { .and_then(|s| s.get().ok().map(|s| s.building)) .unwrap_or_default() { - return Err(anyhow!("Repo is not building.")); + return Err(anyhow!("Repo is not building.").into()); } + let mut update = update.clone(); + update.push_simple_log( "cancel triggered", "the repo build cancel has been triggered", diff --git a/bin/core/src/api/execute/server.rs b/bin/core/src/api/execute/server.rs index c5642400d..2e29c71d1 100644 --- a/bin/core/src/api/execute/server.rs +++ b/bin/core/src/api/execute/server.rs @@ -7,7 +7,6 @@ use komodo_client::{ permission::PermissionLevel, server::Server, update::{Log, Update}, - user::User, }, }; use periphery_client::api; @@ -17,19 +16,20 @@ use crate::{ helpers::{periphery_client, update::update_update}, monitor::update_cache_for_server, resource, - state::{action_states, State}, + state::action_states, }; -impl Resolve for State { +use super::ExecuteArgs; + +impl Resolve for StartContainer { #[instrument(name = "StartContainer", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - StartContainer { server, container }: StartContainer, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -45,13 +45,17 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.starting_containers = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; let periphery = periphery_client(&server)?; let log = match periphery - .request(api::container::StartContainer { name: container }) + .request(api::container::StartContainer { + name: self.container, + }) .await { Ok(log) => log, @@ -71,16 +75,15 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for RestartContainer { #[instrument(name = "RestartContainer", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - RestartContainer { server, container }: RestartContainer, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -96,13 +99,17 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.restarting_containers = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; let periphery = periphery_client(&server)?; let log = match periphery - .request(api::container::RestartContainer { name: container }) + .request(api::container::RestartContainer { + name: self.container, + }) .await { Ok(log) => log, @@ -124,16 +131,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PauseContainer", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PauseContainer { + #[instrument(name = "PauseContainer", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PauseContainer { server, container }: PauseContainer, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -149,13 +155,17 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pausing_containers = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; let periphery = periphery_client(&server)?; let log = match periphery - .request(api::container::PauseContainer { name: container }) + .request(api::container::PauseContainer { + name: self.container, + }) .await { Ok(log) => log, @@ -175,16 +185,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "UnpauseContainer", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for UnpauseContainer { + #[instrument(name = "UnpauseContainer", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - UnpauseContainer { server, container }: UnpauseContainer, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -200,13 +209,17 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.unpausing_containers = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; let periphery = periphery_client(&server)?; let log = match periphery - .request(api::container::UnpauseContainer { name: container }) + .request(api::container::UnpauseContainer { + name: self.container, + }) .await { Ok(log) => log, @@ -228,21 +241,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "StopContainer", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for StopContainer { + #[instrument(name = "StopContainer", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - StopContainer { - server, - container, - signal, - time, - }: StopContainer, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -258,6 +265,8 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.stopping_containers = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -265,9 +274,9 @@ impl Resolve for State { let log = match periphery .request(api::container::StopContainer { - name: container, - signal, - time, + name: self.container, + signal: self.signal, + time: self.time, }) .await { @@ -288,21 +297,21 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "DestroyContainer", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DestroyContainer { + #[instrument(name = "DestroyContainer", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - DestroyContainer { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { + let DestroyContainer { server, container, signal, time, - }: DestroyContainer, - (user, mut update): (User, Update), - ) -> anyhow::Result { + } = self; let server = resource::get_check_permissions::( &server, - &user, + user, PermissionLevel::Execute, ) .await?; @@ -318,6 +327,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_containers = true)?; + let mut update = update.clone(); + // Send update after setting action state, this way frontend gets correct state. update_update(update.clone()).await?; @@ -348,16 +359,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "StartAllContainers", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for StartAllContainers { + #[instrument(name = "StartAllContainers", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - StartAllContainers { server }: StartAllContainers, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -373,6 +383,8 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.starting_containers = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let logs = periphery_client(&server)? @@ -397,16 +409,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "RestartAllContainers", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for RestartAllContainers { + #[instrument(name = "RestartAllContainers", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - RestartAllContainers { server }: RestartAllContainers, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -422,6 +433,8 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.restarting_containers = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let logs = periphery_client(&server)? @@ -448,16 +461,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PauseAllContainers", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PauseAllContainers { + #[instrument(name = "PauseAllContainers", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PauseAllContainers { server }: PauseAllContainers, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -473,6 +485,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pausing_containers = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let logs = periphery_client(&server)? @@ -497,16 +511,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "UnpauseAllContainers", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for UnpauseAllContainers { + #[instrument(name = "UnpauseAllContainers", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - UnpauseAllContainers { server }: UnpauseAllContainers, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -522,6 +535,8 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.unpausing_containers = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let logs = periphery_client(&server)? @@ -548,16 +563,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "StopAllContainers", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for StopAllContainers { + #[instrument(name = "StopAllContainers", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - StopAllContainers { server }: StopAllContainers, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -573,6 +587,8 @@ impl Resolve for State { let _action_guard = action_state .update(|state| state.stopping_containers = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let logs = periphery_client(&server)? @@ -597,16 +613,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PruneContainers", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PruneContainers { + #[instrument(name = "PruneContainers", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PruneContainers { server }: PruneContainers, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -622,6 +637,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_containers = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; @@ -652,37 +669,43 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "DeleteNetwork", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DeleteNetwork { + #[instrument(name = "DeleteNetwork", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - DeleteNetwork { server, name }: DeleteNetwork, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; let log = match periphery - .request(api::network::DeleteNetwork { name: name.clone() }) + .request(api::network::DeleteNetwork { + name: self.name.clone(), + }) .await .context(format!( - "failed to delete network {name} on server {}", - server.name + "failed to delete network {} on server {}", + self.name, server.name )) { Ok(log) => log, Err(e) => Log::error( "delete network", format_serror( - &e.context(format!("failed to delete network {name}")) - .into(), + &e.context(format!( + "failed to delete network {}", + self.name + )) + .into(), ), ), }; @@ -697,16 +720,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PruneNetworks", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PruneNetworks { + #[instrument(name = "PruneNetworks", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PruneNetworks { server }: PruneNetworks, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -722,6 +744,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_networks = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; @@ -750,36 +774,40 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "DeleteImage", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DeleteImage { + #[instrument(name = "DeleteImage", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - DeleteImage { server, name }: DeleteImage, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; let log = match periphery - .request(api::image::DeleteImage { name: name.clone() }) + .request(api::image::DeleteImage { + name: self.name.clone(), + }) .await .context(format!( - "failed to delete image {name} on server {}", - server.name + "failed to delete image {} on server {}", + self.name, server.name )) { Ok(log) => log, Err(e) => Log::error( "delete image", format_serror( - &e.context(format!("failed to delete image {name}")).into(), + &e.context(format!("failed to delete image {}", self.name)) + .into(), ), ), }; @@ -794,16 +822,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PruneImages", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PruneImages { + #[instrument(name = "PruneImages", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PruneImages { server }: PruneImages, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -819,6 +846,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_images = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; @@ -845,37 +874,43 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "DeleteVolume", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DeleteVolume { + #[instrument(name = "DeleteVolume", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - DeleteVolume { server, name }: DeleteVolume, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; let log = match periphery - .request(api::volume::DeleteVolume { name: name.clone() }) + .request(api::volume::DeleteVolume { + name: self.name.clone(), + }) .await .context(format!( - "failed to delete volume {name} on server {}", - server.name + "failed to delete volume {} on server {}", + self.name, server.name )) { Ok(log) => log, Err(e) => Log::error( "delete volume", format_serror( - &e.context(format!("failed to delete volume {name}")) - .into(), + &e.context(format!( + "failed to delete volume {}", + self.name + )) + .into(), ), ), }; @@ -890,16 +925,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PruneVolumes", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PruneVolumes { + #[instrument(name = "PruneVolumes", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PruneVolumes { server }: PruneVolumes, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -915,6 +949,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_volumes = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; @@ -941,16 +977,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PruneDockerBuilders", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PruneDockerBuilders { + #[instrument(name = "PruneDockerBuilders", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PruneDockerBuilders { server }: PruneDockerBuilders, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -966,6 +1001,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_builders = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; @@ -992,16 +1029,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PruneBuildx", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PruneBuildx { + #[instrument(name = "PruneBuildx", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PruneBuildx { server }: PruneBuildx, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -1017,6 +1053,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_buildx = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; @@ -1043,16 +1081,15 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "PruneSystem", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PruneSystem { + #[instrument(name = "PruneSystem", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PruneSystem { server }: PruneSystem, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Execute, ) .await?; @@ -1068,6 +1105,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pruning_system = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; let periphery = periphery_client(&server)?; diff --git a/bin/core/src/api/execute/server_template.rs b/bin/core/src/api/execute/server_template.rs index 5ef9fadbe..b82d047e5 100644 --- a/bin/core/src/api/execute/server_template.rs +++ b/bin/core/src/api/execute/server_template.rs @@ -7,51 +7,51 @@ use komodo_client::{ server::PartialServerConfig, server_template::{ServerTemplate, ServerTemplateConfig}, update::Update, - user::User, }, }; use mungos::mongodb::bson::doc; use resolver_api::Resolve; use crate::{ + api::write::WriteArgs, cloud::{ aws::ec2::launch_ec2_instance, hetzner::launch_hetzner_server, }, helpers::update::update_update, resource, - state::{db_client, State}, + state::db_client, }; -impl Resolve for State { - #[instrument(name = "LaunchServer", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +use super::ExecuteArgs; + +impl Resolve for LaunchServer { + #[instrument(name = "LaunchServer", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - LaunchServer { - name, - server_template, - }: LaunchServer, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { // validate name isn't already taken by another server if db_client() .servers .find_one(doc! { - "name": &name + "name": &self.name }) .await .context("failed to query db for servers")? .is_some() { - return Err(anyhow!("name is already taken")); + return Err(anyhow!("name is already taken").into()); } let template = resource::get_check_permissions::( - &server_template, - &user, + &self.server_template, + user, PermissionLevel::Execute, ) .await?; + let mut update = update.clone(); + update.push_simple_log( "launching server", format!("{:#?}", template.config), @@ -63,24 +63,24 @@ impl Resolve for State { let region = config.region.clone(); let use_https = config.use_https; let port = config.port; - let instance = match launch_ec2_instance(&name, config).await - { - Ok(instance) => instance, - Err(e) => { - update.push_error_log( - "launch server", - format!("failed to launch aws instance\n\n{e:#?}"), - ); - update.finalize(); - update_update(update.clone()).await?; - return Ok(update); - } - }; + let instance = + match launch_ec2_instance(&self.name, config).await { + Ok(instance) => instance, + Err(e) => { + update.push_error_log( + "launch server", + format!("failed to launch aws instance\n\n{e:#?}"), + ); + update.finalize(); + update_update(update.clone()).await?; + return Ok(update); + } + }; update.push_simple_log( "launch server", format!( - "successfully launched server {name} on ip {}", - instance.ip + "successfully launched server {} on ip {}", + self.name, instance.ip ), ); let protocol = if use_https { "https" } else { "http" }; @@ -95,24 +95,24 @@ impl Resolve for State { let datacenter = config.datacenter; let use_https = config.use_https; let port = config.port; - let server = match launch_hetzner_server(&name, config).await - { - Ok(server) => server, - Err(e) => { - update.push_error_log( - "launch server", - format!("failed to launch hetzner server\n\n{e:#?}"), - ); - update.finalize(); - update_update(update.clone()).await?; - return Ok(update); - } - }; + let server = + match launch_hetzner_server(&self.name, config).await { + Ok(server) => server, + Err(e) => { + update.push_error_log( + "launch server", + format!("failed to launch hetzner server\n\n{e:#?}"), + ); + update.finalize(); + update_update(update.clone()).await?; + return Ok(update); + } + }; update.push_simple_log( "launch server", format!( - "successfully launched server {name} on ip {}", - server.ip + "successfully launched server {} on ip {}", + self.name, server.ip ), ); let protocol = if use_https { "https" } else { "http" }; @@ -125,7 +125,13 @@ impl Resolve for State { } }; - match self.resolve(CreateServer { name, config }, user).await { + match (CreateServer { + name: self.name, + config, + }) + .resolve(&WriteArgs { user: user.clone() }) + .await + { Ok(server) => { update.push_simple_log( "create server", @@ -136,7 +142,9 @@ impl Resolve for State { Err(e) => { update.push_error_log( "create server", - format_serror(&e.context("failed to create server").into()), + format_serror( + &e.error.context("failed to create server").into(), + ), ); } }; diff --git a/bin/core/src/api/execute/stack.rs b/bin/core/src/api/execute/stack.rs index 01c1a118b..51f93c5e0 100644 --- a/bin/core/src/api/execute/stack.rs +++ b/bin/core/src/api/execute/stack.rs @@ -9,7 +9,6 @@ use komodo_client::{ server::Server, stack::{Stack, StackInfo}, update::{Log, Update}, - user::User, }, }; use mungos::mongodb::bson::{doc, to_document}; @@ -17,6 +16,7 @@ use periphery_client::api::compose::*; use resolver_api::Resolve; use crate::{ + api::write::WriteArgs, helpers::{ interpolate::{ add_interp_update_log, @@ -31,10 +31,10 @@ use crate::{ monitor::update_cache_for_server, resource, stack::{execute::execute_compose, get_stack_and_server}, - state::{action_states, db_client, State}, + state::{action_states, db_client}, }; -use super::ExecuteRequest; +use super::{ExecuteArgs, ExecuteRequest}; impl super::BatchExecute for BatchDeployStack { type Resource = Stack; @@ -47,31 +47,28 @@ impl super::BatchExecute for BatchDeployStack { } } -impl Resolve for State { - #[instrument(name = "BatchDeployStack", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchDeployStack { + #[instrument(name = "BatchDeployStack", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchDeployStack { pattern }: BatchDeployStack, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::(&self.pattern, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeployStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DeployStack { + #[instrument(name = "DeployStack", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - DeployStack { - stack, - service, - stop_time, - }: DeployStack, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (mut stack, server) = get_stack_and_server( - &stack, - &user, + &self.stack, + user, PermissionLevel::Execute, true, ) @@ -86,9 +83,11 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.deploying = true)?; + let mut update = update.clone(); + update_update(update.clone()).await?; - if let Some(service) = &service { + if let Some(service) = &self.service { update.logs.push(Log::simple( &format!("Service: {service}"), format!("Execution requested for Stack service {service}"), @@ -176,7 +175,7 @@ impl Resolve for State { } = periphery_client(&server)? .request(ComposeUp { stack: stack.clone(), - service, + service: self.service, git_token, registry_token, replacers: secret_replacers.into_iter().collect(), @@ -284,39 +283,39 @@ impl super::BatchExecute for BatchDeployStackIfChanged { } } -impl Resolve for State { - #[instrument(name = "BatchDeployStackIfChanged", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchDeployStackIfChanged { + #[instrument(name = "BatchDeployStackIfChanged", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchDeployStackIfChanged { pattern }: BatchDeployStackIfChanged, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user) - .await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + Ok( + super::batch_execute::( + &self.pattern, + user, + ) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeployStackIfChanged", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DeployStackIfChanged { + #[instrument(name = "DeployStackIfChanged", skip(user, update), fields(user_id = user.id))] async fn resolve( - &self, - DeployStackIfChanged { stack, stop_time }: DeployStackIfChanged, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let stack = resource::get_check_permissions::( - &stack, - &user, + &self.stack, + user, PermissionLevel::Execute, ) .await?; - State - .resolve( - RefreshStackCache { - stack: stack.id.clone(), - }, - user.clone(), - ) - .await?; + RefreshStackCache { + stack: stack.id.clone(), + } + .resolve(&WriteArgs { user: user.clone() }) + .await?; let stack = resource::get::(&stack.id).await?; let changed = match ( &stack.info.deployed_contents, @@ -343,6 +342,8 @@ impl Resolve for State { _ => false, }; + let mut update = update.clone(); + if !changed { update.push_simple_log( "Diff compose files", @@ -356,16 +357,16 @@ impl Resolve for State { // This is usually done in crate::helpers::update::init_execution_update. update.id = add_update_without_send(&update).await?; - State - .resolve( - DeployStack { - stack: stack.name, - service: None, - stop_time, - }, - (user, update), - ) - .await + DeployStack { + stack: stack.name, + service: None, + stop_time: self.stop_time, + } + .resolve(&ExecuteArgs { + user: user.clone(), + update, + }) + .await } } @@ -412,16 +413,15 @@ pub async fn pull_stack_inner( Ok(res) } -impl Resolve for State { - #[instrument(name = "PullStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PullStack { + #[instrument(name = "PullStack", skip(user, update), fields(user_id = user.id))] async fn resolve( - &self, - PullStack { stack, service }: PullStack, - (user, mut update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { let (stack, server) = get_stack_and_server( - &stack, - &user, + &self.stack, + user, PermissionLevel::Execute, true, ) @@ -436,11 +436,16 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.pulling = true)?; + let mut update = update.clone(); update_update(update.clone()).await?; - let res = - pull_stack_inner(stack, service, &server, Some(&mut update)) - .await?; + let res = pull_stack_inner( + stack, + self.service, + &server, + Some(&mut update), + ) + .await?; update.logs.extend(res.logs); update.finalize(); @@ -450,104 +455,100 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "StartStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for StartStack { + #[instrument(name = "StartStack", skip(user, update), fields(user_id = user.id))] async fn resolve( - &self, - StartStack { stack, service }: StartStack, - (user, update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { execute_compose::( - &stack, - service, - &user, + &self.stack, + self.service, + user, |state| state.starting = true, - update, + update.clone(), (), ) .await + .map_err(Into::into) } } -impl Resolve for State { - #[instrument(name = "RestartStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for RestartStack { + #[instrument(name = "RestartStack", skip(user, update), fields(user_id = user.id))] async fn resolve( - &self, - RestartStack { stack, service }: RestartStack, - (user, update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { execute_compose::( - &stack, - service, - &user, + &self.stack, + self.service, + user, |state| { state.restarting = true; }, - update, + update.clone(), (), ) .await + .map_err(Into::into) } } -impl Resolve for State { - #[instrument(name = "PauseStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for PauseStack { + #[instrument(name = "PauseStack", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - PauseStack { stack, service }: PauseStack, - (user, update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { execute_compose::( - &stack, - service, - &user, + &self.stack, + self.service, + user, |state| state.pausing = true, - update, + update.clone(), (), ) .await + .map_err(Into::into) } } -impl Resolve for State { - #[instrument(name = "UnpauseStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for UnpauseStack { + #[instrument(name = "UnpauseStack", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - UnpauseStack { stack, service }: UnpauseStack, - (user, update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { execute_compose::( - &stack, - service, - &user, + &self.stack, + self.service, + user, |state| state.unpausing = true, - update, + update.clone(), (), ) .await + .map_err(Into::into) } } -impl Resolve for State { - #[instrument(name = "StopStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for StopStack { + #[instrument(name = "StopStack", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - StopStack { - stack, - stop_time, - service, - }: StopStack, - (user, update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { execute_compose::( - &stack, - service, - &user, + &self.stack, + self.service, + user, |state| state.stopping = true, - update, - stop_time, + update.clone(), + self.stop_time, ) .await + .map_err(Into::into) } } @@ -563,37 +564,33 @@ impl super::BatchExecute for BatchDestroyStack { } } -impl Resolve for State { - #[instrument(name = "BatchDestroyStack", skip(self, user), fields(user_id = user.id))] +impl Resolve for BatchDestroyStack { + #[instrument(name = "BatchDestroyStack", skip(user), fields(user_id = user.id))] async fn resolve( - &self, - BatchDestroyStack { pattern }: BatchDestroyStack, - (user, _): (User, Update), - ) -> anyhow::Result { - super::batch_execute::(&pattern, &user).await + self, + ExecuteArgs { user, .. }: &ExecuteArgs, + ) -> serror::Result { + super::batch_execute::(&self.pattern, user) + .await + .map_err(Into::into) } } -impl Resolve for State { - #[instrument(name = "DestroyStack", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +impl Resolve for DestroyStack { + #[instrument(name = "DestroyStack", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - DestroyStack { - stack, - service, - remove_orphans, - stop_time, - }: DestroyStack, - (user, update): (User, Update), - ) -> anyhow::Result { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { execute_compose::( - &stack, - service, - &user, + &self.stack, + self.service, + user, |state| state.destroying = true, - update, - (stop_time, remove_orphans), + update.clone(), + (self.stop_time, self.remove_orphans), ) .await + .map_err(Into::into) } } diff --git a/bin/core/src/api/execute/sync.rs b/bin/core/src/api/execute/sync.rs index 60f6176f2..6c353902a 100644 --- a/bin/core/src/api/execute/sync.rs +++ b/bin/core/src/api/execute/sync.rs @@ -20,7 +20,7 @@ use komodo_client::{ stack::Stack, sync::ResourceSync, update::{Log, Update}, - user::{sync_user, User}, + user::sync_user, ResourceTargetVariant, }, }; @@ -32,9 +32,10 @@ use mungos::{ use resolver_api::Resolve; use crate::{ + api::write::WriteArgs, helpers::{query::get_id_to_tags, update::update_update}, resource::{self, refresh_resource_sync_state_cache}, - state::{action_states, db_client, State}, + state::{action_states, db_client}, sync::{ deploy::{ build_deploy_cache, deploy_from_cache, SyncDeployParams, @@ -45,17 +46,19 @@ use crate::{ }, }; -impl Resolve for State { - #[instrument(name = "RunSync", skip(self, user, update), fields(user_id = user.id, update_id = update.id))] +use super::ExecuteArgs; + +impl Resolve for RunSync { + #[instrument(name = "RunSync", skip(user, update), fields(user_id = user.id, update_id = update.id))] async fn resolve( - &self, - RunSync { + self, + ExecuteArgs { user, update }: &ExecuteArgs, + ) -> serror::Result { + let RunSync { sync, resource_type: match_resource_type, resources: match_resources, - }: RunSync, - (user, mut update): (User, Update), - ) -> anyhow::Result { + } = self; let sync = resource::get_check_permissions::< entities::sync::ResourceSync, >(&sync, &user, PermissionLevel::Execute) @@ -72,6 +75,8 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.syncing = true)?; + let mut update = update.clone(); + // Send update here for FE to recheck action state update_update(update.clone()).await?; @@ -90,7 +95,9 @@ impl Resolve for State { update_update(update.clone()).await?; if !file_errors.is_empty() { - return Err(anyhow!("Found file errors. Cannot execute sync.")); + return Err( + anyhow!("Found file errors. Cannot execute sync.").into(), + ); } let resources = resources?; @@ -581,18 +588,21 @@ impl Resolve for State { ) } - if let Err(e) = State - .resolve( - RefreshResourceSyncPending { sync: sync.id }, - sync_user().to_owned(), - ) + if let Err(e) = (RefreshResourceSyncPending { sync: sync.id }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) .await { - warn!("failed to refresh sync {} after run | {e:#}", sync.name); + warn!( + "failed to refresh sync {} after run | {:#}", + sync.name, e.error + ); update.push_error_log( "refresh sync", format_serror( - &e.context("failed to refresh sync pending after run") + &e.error + .context("failed to refresh sync pending after run") .into(), ), ); diff --git a/bin/core/src/api/read/action.rs b/bin/core/src/api/read/action.rs index 3501333a3..7c049eea9 100644 --- a/bin/core/src/api/read/action.rs +++ b/bin/core/src/api/read/action.rs @@ -6,7 +6,6 @@ use komodo_client::{ Action, ActionActionState, ActionListItem, ActionState, }, permission::PermissionLevel, - user::User, }, }; use resolver_api::Resolve; @@ -14,63 +13,70 @@ use resolver_api::Resolve; use crate::{ helpers::query::get_all_tags, resource, - state::{action_state_cache, action_states, State}, + state::{action_state_cache, action_states}, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetAction { async fn resolve( - &self, - GetAction { action }: GetAction, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &action, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.action, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListActions { async fn resolve( - &self, - ListActions { query }: ListActions, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags).await + Ok( + resource::list_for_user::(self.query, &user, &all_tags) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullActions { async fn resolve( - &self, - ListFullActions { query }: ListFullActions, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetActionActionState { async fn resolve( - &self, - GetActionActionState { action }: GetActionActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let action = resource::get_check_permissions::( - &action, + &self.action, &user, PermissionLevel::Read, ) @@ -85,12 +91,11 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetActionsSummary { async fn resolve( - &self, - GetActionsSummary {}: GetActionsSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let actions = resource::list_full_for_user::( Default::default(), &user, diff --git a/bin/core/src/api/read/alert.rs b/bin/core/src/api/read/alert.rs index 2d789545e..574869430 100644 --- a/bin/core/src/api/read/alert.rs +++ b/bin/core/src/api/read/alert.rs @@ -5,7 +5,7 @@ use komodo_client::{ }, entities::{ deployment::Deployment, server::Server, stack::Stack, - sync::ResourceSync, user::User, + sync::ResourceSync, }, }; use mungos::{ @@ -16,29 +16,29 @@ use mungos::{ use resolver_api::Resolve; use crate::{ - config::core_config, - resource::get_resource_ids_for_user, - state::{db_client, State}, + config::core_config, resource::get_resource_ids_for_user, + state::db_client, }; +use super::ReadArgs; + const NUM_ALERTS_PER_PAGE: u64 = 100; -impl Resolve for State { +impl Resolve for ListAlerts { async fn resolve( - &self, - ListAlerts { query, page }: ListAlerts, - user: User, - ) -> anyhow::Result { - let mut query = query.unwrap_or_default(); + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let mut query = self.query.unwrap_or_default(); if !user.admin && !core_config().transparent_mode { let server_ids = - get_resource_ids_for_user::(&user).await?; + get_resource_ids_for_user::(user).await?; let stack_ids = - get_resource_ids_for_user::(&user).await?; + get_resource_ids_for_user::(user).await?; let deployment_ids = - get_resource_ids_for_user::(&user).await?; + get_resource_ids_for_user::(user).await?; let sync_ids = - get_resource_ids_for_user::(&user).await?; + get_resource_ids_for_user::(user).await?; query.extend(doc! { "$or": [ { "target.type": "Server", "target.id": { "$in": &server_ids } }, @@ -55,7 +55,7 @@ impl Resolve for State { FindOptions::builder() .sort(doc! { "ts": -1 }) .limit(NUM_ALERTS_PER_PAGE as i64) - .skip(page * NUM_ALERTS_PER_PAGE) + .skip(self.page * NUM_ALERTS_PER_PAGE) .build(), ) .await @@ -64,7 +64,7 @@ impl Resolve for State { let next_page = if alerts.len() < NUM_ALERTS_PER_PAGE as usize { None } else { - Some((page + 1) as i64) + Some((self.page + 1) as i64) }; let res = ListAlertsResponse { next_page, alerts }; @@ -73,15 +73,16 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetAlert { async fn resolve( - &self, - GetAlert { id }: GetAlert, - _: User, - ) -> anyhow::Result { - find_one_by_id(&db_client().alerts, &id) - .await - .context("failed to query db for alert")? - .context("no alert found with given id") + self, + _: &ReadArgs, + ) -> serror::Result { + Ok( + find_one_by_id(&db_client().alerts, &self.id) + .await + .context("failed to query db for alert")? + .context("no alert found with given id")?, + ) } } diff --git a/bin/core/src/api/read/alerter.rs b/bin/core/src/api/read/alerter.rs index 654d37bc3..8b158a280 100644 --- a/bin/core/src/api/read/alerter.rs +++ b/bin/core/src/api/read/alerter.rs @@ -4,7 +4,6 @@ use komodo_client::{ entities::{ alerter::{Alerter, AlerterListItem}, permission::PermissionLevel, - user::User, }, }; use mongo_indexed::Document; @@ -12,63 +11,68 @@ use mungos::mongodb::bson::doc; use resolver_api::Resolve; use crate::{ - helpers::query::get_all_tags, - resource, - state::{db_client, State}, + helpers::query::get_all_tags, resource, state::db_client, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetAlerter { async fn resolve( - &self, - GetAlerter { alerter }: GetAlerter, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &alerter, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.alerter, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListAlerters { async fn resolve( - &self, - ListAlerters { query }: ListAlerters, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags).await + Ok( + resource::list_for_user::(self.query, user, &all_tags) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullAlerters { async fn resolve( - &self, - ListFullAlerters { query }: ListFullAlerters, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetAlertersSummary { async fn resolve( - &self, - GetAlertersSummary {}: GetAlertersSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let query = match resource::get_resource_object_ids_for_user::< Alerter, >(&user) diff --git a/bin/core/src/api/read/build.rs b/bin/core/src/api/read/build.rs index 06a64aa02..331e5a55c 100644 --- a/bin/core/src/api/read/build.rs +++ b/bin/core/src/api/read/build.rs @@ -10,7 +10,6 @@ use komodo_client::{ config::core::CoreConfig, permission::PermissionLevel, update::UpdateStatus, - user::User, Operation, }, }; @@ -25,65 +24,72 @@ use crate::{ helpers::query::get_all_tags, resource, state::{ - action_states, build_state_cache, db_client, github_client, State, + action_states, build_state_cache, db_client, github_client, }, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetBuild { async fn resolve( - &self, - GetBuild { build }: GetBuild, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &build, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.build, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListBuilds { async fn resolve( - &self, - ListBuilds { query }: ListBuilds, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags).await + Ok( + resource::list_for_user::(self.query, user, &all_tags) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullBuilds { async fn resolve( - &self, - ListFullBuilds { query }: ListFullBuilds, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetBuildActionState { async fn resolve( - &self, - GetBuildActionState { build }: GetBuildActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let build = resource::get_check_permissions::( - &build, - &user, + &self.build, + user, PermissionLevel::Read, ) .await?; @@ -97,15 +103,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetBuildsSummary { async fn resolve( - &self, - GetBuildsSummary {}: GetBuildsSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let builds = resource::list_full_for_user::( Default::default(), - &user, + user, &[], ) .await @@ -145,16 +150,15 @@ impl Resolve for State { const ONE_DAY_MS: i64 = 86400000; -impl Resolve for State { +impl Resolve for GetBuildMonthlyStats { async fn resolve( - &self, - GetBuildMonthlyStats { page }: GetBuildMonthlyStats, - _: User, - ) -> anyhow::Result { + self, + _: &ReadArgs, + ) -> serror::Result { 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 - page as i64 * 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() @@ -202,21 +206,21 @@ fn ms_to_hour(duration: i64) -> f64 { duration as f64 / MS_TO_HOUR_DIVISOR } -impl Resolve for State { +impl Resolve for ListBuildVersions { async fn resolve( - &self, - ListBuildVersions { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let ListBuildVersions { build, major, minor, patch, limit, - }: ListBuildVersions, - user: User, - ) -> anyhow::Result> { + } = self; let build = resource::get_check_permissions::( &build, - &user, + user, PermissionLevel::Read, ) .await?; @@ -259,21 +263,21 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListCommonBuildExtraArgs { async fn resolve( - &self, - ListCommonBuildExtraArgs { query }: ListCommonBuildExtraArgs, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - let builds = - resource::list_full_for_user::(query, &user, &all_tags) - .await - .context("failed to get resources matching query")?; + let builds = resource::list_full_for_user::( + self.query, user, &all_tags, + ) + .await + .context("failed to get resources matching query")?; // first collect with guaranteed uniqueness let mut res = HashSet::::new(); @@ -290,12 +294,11 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetBuildWebhookEnabled { async fn resolve( - &self, - GetBuildWebhookEnabled { build }: GetBuildWebhookEnabled, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let Some(github) = github_client() else { return Ok(GetBuildWebhookEnabledResponse { managed: false, @@ -304,8 +307,8 @@ impl Resolve for State { }; let build = resource::get_check_permissions::( - &build, - &user, + &self.build, + user, PermissionLevel::Read, ) .await?; diff --git a/bin/core/src/api/read/builder.rs b/bin/core/src/api/read/builder.rs index 469866d37..eecca91a6 100644 --- a/bin/core/src/api/read/builder.rs +++ b/bin/core/src/api/read/builder.rs @@ -4,7 +4,6 @@ use komodo_client::{ entities::{ builder::{Builder, BuilderListItem}, permission::PermissionLevel, - user::User, }, }; use mongo_indexed::Document; @@ -12,63 +11,68 @@ use mungos::mongodb::bson::doc; use resolver_api::Resolve; use crate::{ - helpers::query::get_all_tags, - resource, - state::{db_client, State}, + helpers::query::get_all_tags, resource, state::db_client, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetBuilder { async fn resolve( - &self, - GetBuilder { builder }: GetBuilder, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &builder, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.builder, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListBuilders { async fn resolve( - &self, - ListBuilders { query }: ListBuilders, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags).await + Ok( + resource::list_for_user::(self.query, user, &all_tags) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullBuilders { async fn resolve( - &self, - ListFullBuilders { query }: ListFullBuilders, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetBuildersSummary { async fn resolve( - &self, - GetBuildersSummary {}: GetBuildersSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let query = match resource::get_resource_object_ids_for_user::< Builder, >(&user) diff --git a/bin/core/src/api/read/deployment.rs b/bin/core/src/api/read/deployment.rs index 697da171f..b147e0ff6 100644 --- a/bin/core/src/api/read/deployment.rs +++ b/bin/core/src/api/read/deployment.rs @@ -12,7 +12,6 @@ use komodo_client::{ permission::PermissionLevel, server::Server, update::Log, - user::User, }, }; use periphery_client::api; @@ -21,67 +20,73 @@ use resolver_api::Resolve; use crate::{ helpers::{periphery_client, query::get_all_tags}, resource, - state::{action_states, deployment_status_cache, State}, + state::{action_states, deployment_status_cache}, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetDeployment { async fn resolve( - &self, - GetDeployment { deployment }: GetDeployment, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &deployment, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.deployment, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListDeployments { async fn resolve( - &self, - ListDeployments { query }: ListDeployments, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_for_user::( + self.query, user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullDeployments { async fn resolve( - &self, - ListFullDeployments { query }: ListFullDeployments, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::( - query, &user, &all_tags, + Ok( + resource::list_full_for_user::( + self.query, user, &all_tags, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for GetDeploymentContainer { async fn resolve( - &self, - GetDeploymentContainer { deployment }: GetDeploymentContainer, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let deployment = resource::get_check_permissions::( - &deployment, - &user, + &self.deployment, + user, PermissionLevel::Read, ) .await?; @@ -99,23 +104,23 @@ impl Resolve for State { const MAX_LOG_LENGTH: u64 = 5000; -impl Resolve for State { +impl Resolve for GetDeploymentLog { async fn resolve( - &self, - GetDeploymentLog { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let GetDeploymentLog { deployment, tail, timestamps, - }: GetDeploymentLog, - user: User, - ) -> anyhow::Result { + } = self; let Deployment { name, config: DeploymentConfig { server_id, .. }, .. } = resource::get_check_permissions::( &deployment, - &user, + user, PermissionLevel::Read, ) .await?; @@ -123,36 +128,37 @@ impl Resolve for State { return Ok(Log::default()); } let server = resource::get::(&server_id).await?; - periphery_client(&server)? + let res = periphery_client(&server)? .request(api::container::GetContainerLog { name, tail: cmp::min(tail, MAX_LOG_LENGTH), timestamps, }) .await - .context("failed at call to periphery") + .context("failed at call to periphery")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for SearchDeploymentLog { async fn resolve( - &self, - SearchDeploymentLog { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let SearchDeploymentLog { deployment, terms, combinator, invert, timestamps, - }: SearchDeploymentLog, - user: User, - ) -> anyhow::Result { + } = self; let Deployment { name, config: DeploymentConfig { server_id, .. }, .. } = resource::get_check_permissions::( &deployment, - &user, + user, PermissionLevel::Read, ) .await?; @@ -160,7 +166,7 @@ impl Resolve for State { return Ok(Log::default()); } let server = resource::get::(&server_id).await?; - periphery_client(&server)? + let res = periphery_client(&server)? .request(api::container::GetContainerLogSearch { name, terms, @@ -169,46 +175,48 @@ impl Resolve for State { timestamps, }) .await - .context("failed at call to periphery") + .context("failed at call to periphery")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for GetDeploymentStats { async fn resolve( - &self, - GetDeploymentStats { deployment }: GetDeploymentStats, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let Deployment { name, config: DeploymentConfig { server_id, .. }, .. } = resource::get_check_permissions::( - &deployment, - &user, + &self.deployment, + user, PermissionLevel::Read, ) .await?; if server_id.is_empty() { - return Err(anyhow!("deployment has no server attached")); + return Err( + anyhow!("deployment has no server attached").into(), + ); } let server = resource::get::(&server_id).await?; - periphery_client(&server)? + let res = periphery_client(&server)? .request(api::container::GetContainerStats { name }) .await - .context("failed to get stats from periphery") + .context("failed to get stats from periphery")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for GetDeploymentActionState { async fn resolve( - &self, - GetDeploymentActionState { deployment }: GetDeploymentActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let deployment = resource::get_check_permissions::( - &deployment, - &user, + &self.deployment, + user, PermissionLevel::Read, ) .await?; @@ -222,15 +230,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetDeploymentsSummary { async fn resolve( - &self, - GetDeploymentsSummary {}: GetDeploymentsSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let deployments = resource::list_full_for_user::( Default::default(), - &user, + user, &[], ) .await @@ -263,19 +270,18 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListCommonDeploymentExtraArgs { async fn resolve( - &self, - ListCommonDeploymentExtraArgs { query }: ListCommonDeploymentExtraArgs, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; let deployments = resource::list_full_for_user::( - query, &user, &all_tags, + self.query, &user, &all_tags, ) .await .context("failed to get resources matching query")?; diff --git a/bin/core/src/api/read/mod.rs b/bin/core/src/api/read/mod.rs index 324c3153c..54b85936b 100644 --- a/bin/core/src/api/read/mod.rs +++ b/bin/core/src/api/read/mod.rs @@ -2,7 +2,6 @@ use std::{collections::HashSet, sync::OnceLock, time::Instant}; use anyhow::{anyhow, Context}; use axum::{middleware, routing::post, Extension, Router}; -use axum_extra::{headers::ContentType, TypedHeader}; use komodo_client::{ api::read::*, entities::{ @@ -16,9 +15,8 @@ use komodo_client::{ ResourceTarget, }, }; -use resolver_api::{ - derive::Resolver, Resolve, ResolveToString, Resolver, -}; +use resolver_api::Resolve; +use response::Response; use serde::{Deserialize, Serialize}; use serror::Json; use typeshare::typeshare; @@ -26,7 +24,7 @@ use uuid::Uuid; use crate::{ auth::auth_request, config::core_config, helpers::periphery_client, - resource, state::State, + resource, }; mod action; @@ -39,7 +37,6 @@ mod permission; mod procedure; mod provider; mod repo; -mod search; mod server; mod server_template; mod stack; @@ -51,15 +48,18 @@ mod user; mod user_group; mod variable; +pub struct ReadArgs { + pub user: User, +} + #[typeshare] -#[derive(Serialize, Deserialize, Debug, Clone, Resolver)] -#[resolver_target(State)] -#[resolver_args(User)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] +#[args(ReadArgs)] +#[response(Response)] +#[error(serror::Error)] #[serde(tag = "type", content = "params")] enum ReadRequest { - #[to_string_resolver] GetVersion(GetVersion), - #[to_string_resolver] GetCoreInfo(GetCoreInfo), ListSecrets(ListSecrets), ListGitProvidersFromConfig(ListGitProvidersFromConfig), @@ -79,9 +79,6 @@ enum ReadRequest { GetUserGroup(GetUserGroup), ListUserGroups(ListUserGroups), - // ==== SEARCH ==== - FindResources(FindResources), - // ==== PROCEDURE ==== GetProceduresSummary(GetProceduresSummary), GetProcedure(GetProcedure), @@ -120,15 +117,10 @@ enum ReadRequest { ListDockerImageHistory(ListDockerImageHistory), InspectDockerVolume(InspectDockerVolume), ListAllDockerContainers(ListAllDockerContainers), - #[to_string_resolver] ListDockerContainers(ListDockerContainers), - #[to_string_resolver] ListDockerNetworks(ListDockerNetworks), - #[to_string_resolver] ListDockerImages(ListDockerImages), - #[to_string_resolver] ListDockerVolumes(ListDockerVolumes), - #[to_string_resolver] ListComposeProjects(ListComposeProjects), // ==== DEPLOYMENT ==== @@ -212,11 +204,8 @@ enum ReadRequest { GetAlert(GetAlert), // ==== SERVER STATS ==== - #[to_string_resolver] GetSystemInformation(GetSystemInformation), - #[to_string_resolver] GetSystemStats(GetSystemStats), - #[to_string_resolver] ListSystemProcesses(ListSystemProcesses), // ==== VARIABLE ==== @@ -240,54 +229,35 @@ pub fn router() -> Router { async fn handler( Extension(user): Extension, Json(request): Json, -) -> serror::Result<(TypedHeader, String)> { +) -> serror::Result { let timer = Instant::now(); let req_id = Uuid::new_v4(); debug!("/read request | user: {}", user.username); - let res = - State - .resolve_request(request, user) - .await - .map_err(|e| match e { - resolver_api::Error::Serialization(e) => { - anyhow!("{e:?}").context("response serialization error") - } - resolver_api::Error::Inner(e) => e, - }); + let res = request.resolve(&ReadArgs { user }).await; if let Err(e) = &res { - debug!("/read request {req_id} error: {e:#}"); + debug!("/read request {req_id} error: {:#}", e.error); } let elapsed = timer.elapsed(); debug!("/read request {req_id} | resolve time: {elapsed:?}"); - Ok((TypedHeader(ContentType::json()), res?)) + res.map(|res| res.0) } -fn version() -> &'static String { - static VERSION: OnceLock = OnceLock::new(); - VERSION.get_or_init(|| { - serde_json::to_string(&GetVersionResponse { +impl Resolve for GetVersion { + async fn resolve( + self, + _: &ReadArgs, + ) -> serror::Result { + Ok(GetVersionResponse { version: env!("CARGO_PKG_VERSION").to_string(), }) - .context("failed to serialize GetVersionResponse") - .unwrap() - }) -} - -impl ResolveToString for State { - async fn resolve_to_string( - &self, - GetVersion {}: GetVersion, - _: User, - ) -> anyhow::Result { - Ok(version().to_string()) } } -fn core_info() -> &'static String { - static CORE_INFO: OnceLock = OnceLock::new(); +fn core_info() -> &'static GetCoreInfoResponse { + static CORE_INFO: OnceLock = OnceLock::new(); CORE_INFO.get_or_init(|| { let config = core_config(); - let info = GetCoreInfoResponse { + GetCoreInfoResponse { title: config.title.clone(), monitoring_interval: config.monitoring_interval, webhook_base_url: if config.webhook_base_url.is_empty() { @@ -305,36 +275,31 @@ fn core_info() -> &'static String { .iter() .map(|i| i.namespace.to_string()) .collect(), - }; - serde_json::to_string(&info) - .context("failed to serialize GetCoreInfoResponse") - .unwrap() + } }) } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - GetCoreInfo {}: GetCoreInfo, - _: User, - ) -> anyhow::Result { - Ok(core_info().to_string()) +impl Resolve for GetCoreInfo { + async fn resolve( + self, + _: &ReadArgs, + ) -> serror::Result { + Ok(core_info().clone()) } } -impl Resolve for State { +impl Resolve for ListSecrets { async fn resolve( - &self, - ListSecrets { target }: ListSecrets, - _: User, - ) -> anyhow::Result { + self, + _: &ReadArgs, + ) -> serror::Result { let mut secrets = core_config() .secrets .keys() .cloned() .collect::>(); - if let Some(target) = target { + if let Some(target) = self.target { let server_id = match target { ResourceTarget::Server(id) => Some(id), ResourceTarget::Builder(id) => { @@ -348,7 +313,9 @@ impl Resolve for State { } } _ => { - return Err(anyhow!("target must be `Server` or `Builder`")) + return Err( + anyhow!("target must be `Server` or `Builder`").into(), + ) } }; if let Some(id) = server_id { @@ -373,15 +340,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListGitProvidersFromConfig { async fn resolve( - &self, - ListGitProvidersFromConfig { target }: ListGitProvidersFromConfig, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let mut providers = core_config().git_providers.clone(); - if let Some(target) = target { + if let Some(target) = self.target { match target { ResourceTarget::Server(id) => { merge_git_providers_for_server(&mut providers, &id).await?; @@ -405,7 +371,9 @@ impl Resolve for State { } } _ => { - return Err(anyhow!("target must be `Server` or `Builder`")) + return Err( + anyhow!("target must be `Server` or `Builder`").into(), + ) } } } @@ -471,15 +439,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListDockerRegistriesFromConfig { async fn resolve( - &self, - ListDockerRegistriesFromConfig { target }: ListDockerRegistriesFromConfig, - _: User, - ) -> anyhow::Result { + self, + _: &ReadArgs, + ) -> serror::Result { let mut registries = core_config().docker_registries.clone(); - if let Some(target) = target { + if let Some(target) = self.target { match target { ResourceTarget::Server(id) => { merge_docker_registries_for_server(&mut registries, &id) @@ -504,7 +471,9 @@ impl Resolve for State { } } _ => { - return Err(anyhow!("target must be `Server` or `Builder`")) + return Err( + anyhow!("target must be `Server` or `Builder`").into(), + ) } } } @@ -518,7 +487,7 @@ impl Resolve for State { async fn merge_git_providers_for_server( providers: &mut Vec, server_id: &str, -) -> anyhow::Result<()> { +) -> serror::Result<()> { let server = resource::get::(server_id).await?; let more = periphery_client(&server)? .request(periphery_client::api::ListGitProviders {}) @@ -556,7 +525,7 @@ fn merge_git_providers( async fn merge_docker_registries_for_server( registries: &mut Vec, server_id: &str, -) -> anyhow::Result<()> { +) -> serror::Result<()> { let server = resource::get::(server_id).await?; let more = periphery_client(&server)? .request(periphery_client::api::ListDockerRegistries {}) diff --git a/bin/core/src/api/read/permission.rs b/bin/core/src/api/read/permission.rs index e3e493e28..3a6cfc7c0 100644 --- a/bin/core/src/api/read/permission.rs +++ b/bin/core/src/api/read/permission.rs @@ -5,23 +5,23 @@ use komodo_client::{ ListPermissionsResponse, ListUserTargetPermissions, ListUserTargetPermissionsResponse, }, - entities::{permission::PermissionLevel, user::User}, + entities::permission::PermissionLevel, }; use mungos::{find::find_collect, mongodb::bson::doc}; use resolver_api::Resolve; use crate::{ - helpers::query::get_user_permission_on_target, - state::{db_client, State}, + helpers::query::get_user_permission_on_target, state::db_client, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for ListPermissions { async fn resolve( - &self, - ListPermissions {}: ListPermissions, - user: User, - ) -> anyhow::Result { - find_collect( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let res = find_collect( &db_client().permissions, doc! { "user_target.type": "User", @@ -30,34 +30,33 @@ impl Resolve for State { None, ) .await - .context("failed to query db for permissions") + .context("failed to query db for permissions")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for GetPermissionLevel { async fn resolve( - &self, - GetPermissionLevel { target }: GetPermissionLevel, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { if user.admin { return Ok(PermissionLevel::Write); } - get_user_permission_on_target(&user, &target).await + Ok(get_user_permission_on_target(user, &self.target).await?) } } -impl Resolve for State { +impl Resolve for ListUserTargetPermissions { async fn resolve( - &self, - ListUserTargetPermissions { user_target }: ListUserTargetPermissions, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("this method is admin only")); + return Err(anyhow!("this method is admin only").into()); } - let (variant, id) = user_target.extract_variant_id(); - find_collect( + let (variant, id) = self.user_target.extract_variant_id(); + let res = find_collect( &db_client().permissions, doc! { "user_target.type": variant.as_ref(), @@ -66,6 +65,7 @@ impl Resolve for State { None, ) .await - .context("failed to query db for permissions") + .context("failed to query db for permissions")?; + Ok(res) } } diff --git a/bin/core/src/api/read/procedure.rs b/bin/core/src/api/read/procedure.rs index ec1e4961d..d3507a8f5 100644 --- a/bin/core/src/api/read/procedure.rs +++ b/bin/core/src/api/read/procedure.rs @@ -4,7 +4,6 @@ use komodo_client::{ entities::{ permission::PermissionLevel, procedure::{Procedure, ProcedureState}, - user::User, }, }; use resolver_api::Resolve; @@ -12,65 +11,73 @@ use resolver_api::Resolve; use crate::{ helpers::query::get_all_tags, resource, - state::{action_states, procedure_state_cache, State}, + state::{action_states, procedure_state_cache}, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetProcedure { async fn resolve( - &self, - GetProcedure { procedure }: GetProcedure, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &procedure, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.procedure, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListProcedures { async fn resolve( - &self, - ListProcedures { query }: ListProcedures, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_for_user::( + self.query, user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullProcedures { async fn resolve( - &self, - ListFullProcedures { query }: ListFullProcedures, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetProceduresSummary { async fn resolve( - &self, - GetProceduresSummary {}: GetProceduresSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let procedures = resource::list_full_for_user::( Default::default(), - &user, + user, &[], ) .await @@ -108,15 +115,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetProcedureActionState { async fn resolve( - &self, - GetProcedureActionState { procedure }: GetProcedureActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let procedure = resource::get_check_permissions::( - &procedure, - &user, + &self.procedure, + user, PermissionLevel::Read, ) .await?; diff --git a/bin/core/src/api/read/provider.rs b/bin/core/src/api/read/provider.rs index 28d3735df..f787e5d29 100644 --- a/bin/core/src/api/read/provider.rs +++ b/bin/core/src/api/read/provider.rs @@ -1,13 +1,5 @@ use anyhow::{anyhow, Context}; -use komodo_client::{ - api::read::{ - GetDockerRegistryAccount, GetDockerRegistryAccountResponse, - GetGitProviderAccount, GetGitProviderAccountResponse, - ListDockerRegistryAccounts, ListDockerRegistryAccountsResponse, - ListGitProviderAccounts, ListGitProviderAccountsResponse, - }, - entities::user::User, -}; +use komodo_client::api::read::*; use mongo_indexed::{doc, Document}; use mungos::{ by_id::find_one_by_id, find::find_collect, @@ -15,45 +7,48 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::state::{db_client, State}; +use crate::state::db_client; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetGitProviderAccount { async fn resolve( - &self, - GetGitProviderAccount { id }: GetGitProviderAccount, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "Only admins can read git provider accounts" - )); + return Err( + anyhow!("Only admins can read git provider accounts").into(), + ); } - find_one_by_id(&db_client().git_accounts, &id) + let res = find_one_by_id(&db_client().git_accounts, &self.id) .await .context("failed to query db for git provider accounts")? - .context("did not find git provider account with the given id") + .context( + "did not find git provider account with the given id", + )?; + Ok(res) } } -impl Resolve for State { +impl Resolve for ListGitProviderAccounts { async fn resolve( - &self, - ListGitProviderAccounts { domain, username }: ListGitProviderAccounts, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "Only admins can read git provider accounts" - )); + return Err( + anyhow!("Only admins can read git provider accounts").into(), + ); } let mut filter = Document::new(); - if let Some(domain) = domain { + if let Some(domain) = self.domain { filter.insert("domain", domain); } - if let Some(username) = username { + if let Some(username) = self.username { filter.insert("username", username); } - find_collect( + let res = find_collect( &db_client().git_accounts, filter, FindOptions::builder() @@ -61,49 +56,52 @@ impl Resolve for State { .build(), ) .await - .context("failed to query db for git provider accounts") + .context("failed to query db for git provider accounts")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for GetDockerRegistryAccount { async fn resolve( - &self, - GetDockerRegistryAccount { id }: GetDockerRegistryAccount, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "Only admins can read docker registry accounts" - )); + return Err( + anyhow!("Only admins can read docker registry accounts") + .into(), + ); } - find_one_by_id(&db_client().registry_accounts, &id) - .await - .context("failed to query db for docker registry accounts")? - .context( - "did not find docker registry account with the given id", - ) + let res = + find_one_by_id(&db_client().registry_accounts, &self.id) + .await + .context("failed to query db for docker registry accounts")? + .context( + "did not find docker registry account with the given id", + )?; + Ok(res) } } -impl Resolve for State { +impl Resolve for ListDockerRegistryAccounts { async fn resolve( - &self, - ListDockerRegistryAccounts { domain, username }: ListDockerRegistryAccounts, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "Only admins can read docker registry accounts" - )); + return Err( + anyhow!("Only admins can read docker registry accounts") + .into(), + ); } let mut filter = Document::new(); - if let Some(domain) = domain { + if let Some(domain) = self.domain { filter.insert("domain", domain); } - if let Some(username) = username { + if let Some(username) = self.username { filter.insert("username", username); } - find_collect( + let res = find_collect( &db_client().registry_accounts, filter, FindOptions::builder() @@ -111,6 +109,7 @@ impl Resolve for State { .build(), ) .await - .context("failed to query db for docker registry accounts") + .context("failed to query db for docker registry accounts")?; + Ok(res) } } diff --git a/bin/core/src/api/read/repo.rs b/bin/core/src/api/read/repo.rs index 5b8f8c976..4da0c052a 100644 --- a/bin/core/src/api/read/repo.rs +++ b/bin/core/src/api/read/repo.rs @@ -5,7 +5,6 @@ use komodo_client::{ config::core::CoreConfig, permission::PermissionLevel, repo::{Repo, RepoActionState, RepoListItem, RepoState}, - user::User, }, }; use resolver_api::Resolve; @@ -14,64 +13,71 @@ use crate::{ config::core_config, helpers::query::get_all_tags, resource, - state::{action_states, github_client, repo_state_cache, State}, + state::{action_states, github_client, repo_state_cache}, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetRepo { async fn resolve( - &self, - GetRepo { repo }: GetRepo, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &repo, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.repo, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListRepos { async fn resolve( - &self, - ListRepos { query }: ListRepos, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags).await + Ok( + resource::list_for_user::(self.query, &user, &all_tags) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullRepos { async fn resolve( - &self, - ListFullRepos { query }: ListFullRepos, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetRepoActionState { async fn resolve( - &self, - GetRepoActionState { repo }: GetRepoActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let repo = resource::get_check_permissions::( - &repo, - &user, + &self.repo, + user, PermissionLevel::Read, ) .await?; @@ -85,15 +91,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetReposSummary { async fn resolve( - &self, - GetReposSummary {}: GetReposSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let repos = resource::list_full_for_user::( Default::default(), - &user, + user, &[], ) .await @@ -141,12 +146,11 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetRepoWebhooksEnabled { async fn resolve( - &self, - GetRepoWebhooksEnabled { repo }: GetRepoWebhooksEnabled, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let Some(github) = github_client() else { return Ok(GetRepoWebhooksEnabledResponse { managed: false, @@ -157,8 +161,8 @@ impl Resolve for State { }; let repo = resource::get_check_permissions::( - &repo, - &user, + &self.repo, + user, PermissionLevel::Read, ) .await?; diff --git a/bin/core/src/api/read/search.rs b/bin/core/src/api/read/search.rs deleted file mode 100644 index 3568ad8ab..000000000 --- a/bin/core/src/api/read/search.rs +++ /dev/null @@ -1,82 +0,0 @@ -use komodo_client::{ - api::read::{FindResources, FindResourcesResponse}, - entities::{ - build::Build, deployment::Deployment, procedure::Procedure, - repo::Repo, server::Server, user::User, ResourceTargetVariant, - }, -}; -use resolver_api::Resolve; - -use crate::{resource, state::State}; - -const FIND_RESOURCE_TYPES: [ResourceTargetVariant; 5] = [ - ResourceTargetVariant::Server, - ResourceTargetVariant::Build, - ResourceTargetVariant::Deployment, - ResourceTargetVariant::Repo, - ResourceTargetVariant::Procedure, -]; - -impl Resolve for State { - async fn resolve( - &self, - FindResources { query, resources }: FindResources, - user: User, - ) -> anyhow::Result { - let mut res = FindResourcesResponse::default(); - let resource_types = if resources.is_empty() { - FIND_RESOURCE_TYPES.to_vec() - } else { - resources - .into_iter() - .filter(|r| { - !matches!( - r, - ResourceTargetVariant::System - | ResourceTargetVariant::Builder - | ResourceTargetVariant::Alerter - ) - }) - .collect() - }; - for resource_type in resource_types { - match resource_type { - ResourceTargetVariant::Server => { - res.servers = resource::list_for_user_using_document::< - Server, - >(query.clone(), &user) - .await?; - } - ResourceTargetVariant::Deployment => { - res.deployments = resource::list_for_user_using_document::< - Deployment, - >(query.clone(), &user) - .await?; - } - ResourceTargetVariant::Build => { - res.builds = - resource::list_for_user_using_document::( - query.clone(), - &user, - ) - .await?; - } - ResourceTargetVariant::Repo => { - res.repos = resource::list_for_user_using_document::( - query.clone(), - &user, - ) - .await?; - } - ResourceTargetVariant::Procedure => { - res.procedures = resource::list_for_user_using_document::< - Procedure, - >(query.clone(), &user) - .await?; - } - _ => {} - } - } - Ok(res) - } -} diff --git a/bin/core/src/api/read/server.rs b/bin/core/src/api/read/server.rs index 1761aca15..3e532ac64 100644 --- a/bin/core/src/api/read/server.rs +++ b/bin/core/src/api/read/server.rs @@ -23,8 +23,8 @@ use komodo_client::{ Server, ServerActionState, ServerListItem, ServerState, }, stack::{Stack, StackServiceNames}, + stats::{SystemInformation, SystemProcess}, update::Log, - user::User, ResourceTarget, }, }; @@ -39,25 +39,26 @@ use periphery_client::api::{ network::InspectNetwork, volume::InspectVolume, }; -use resolver_api::{Resolve, ResolveToString}; +use resolver_api::Resolve; use tokio::sync::Mutex; use crate::{ helpers::{periphery_client, query::get_all_tags}, resource, stack::compose_container_match_regex, - state::{action_states, db_client, server_status_cache, State}, + state::{action_states, db_client, server_status_cache}, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetServersSummary { async fn resolve( - &self, - GetServersSummary {}: GetServersSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let servers = resource::list_for_user::( Default::default(), - &user, + user, &[], ) .await?; @@ -80,15 +81,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetPeripheryVersion { async fn resolve( - &self, - req: GetPeripheryVersion, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &req.server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -101,61 +101,66 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetServer { async fn resolve( - &self, - req: GetServer, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &req.server, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.server, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListServers { async fn resolve( - &self, - ListServers { query }: ListServers, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags).await + Ok( + resource::list_for_user::(self.query, &user, &all_tags) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullServers { async fn resolve( - &self, - ListFullServers { query }: ListFullServers, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetServerState { async fn resolve( - &self, - GetServerState { server }: GetServerState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -170,15 +175,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetServerActionState { async fn resolve( - &self, - GetServerActionState { server }: GetServerActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -194,22 +198,22 @@ impl Resolve for State { // This protects the peripheries from spam requests const SYSTEM_INFO_EXPIRY: u128 = FIFTEEN_SECONDS_MS; -type SystemInfoCache = Mutex>>; +type SystemInfoCache = + Mutex>>; fn system_info_cache() -> &'static SystemInfoCache { static SYSTEM_INFO_CACHE: OnceLock = OnceLock::new(); SYSTEM_INFO_CACHE.get_or_init(Default::default) } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - GetSystemInformation { server }: GetSystemInformation, - user: User, - ) -> anyhow::Result { +impl Resolve for GetSystemInformation { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -223,28 +227,26 @@ impl ResolveToString for State { let stats = periphery_client(&server)? .request(periphery::stats::GetSystemInformation {}) .await?; - let res = serde_json::to_string(&stats)?; lock.insert( server.id, - (res.clone(), unix_timestamp_ms() + SYSTEM_INFO_EXPIRY) + (stats.clone(), unix_timestamp_ms() + SYSTEM_INFO_EXPIRY) .into(), ); - res + stats } }; Ok(res) } } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - GetSystemStats { server }: GetSystemStats, - user: User, - ) -> anyhow::Result { +impl Resolve for GetSystemStats { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -256,28 +258,27 @@ impl ResolveToString for State { .stats .as_ref() .context("server stats not available")?; - let stats = serde_json::to_string(&stats)?; - Ok(stats) + Ok(stats.clone()) } } // This protects the peripheries from spam requests const PROCESSES_EXPIRY: u128 = FIFTEEN_SECONDS_MS; -type ProcessesCache = Mutex>>; +type ProcessesCache = + Mutex, u128)>>>; fn processes_cache() -> &'static ProcessesCache { static PROCESSES_CACHE: OnceLock = OnceLock::new(); PROCESSES_CACHE.get_or_init(Default::default) } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - ListSystemProcesses { server }: ListSystemProcesses, - user: User, - ) -> anyhow::Result { +impl Resolve for ListSystemProcesses { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -290,13 +291,12 @@ impl ResolveToString for State { let stats = periphery_client(&server)? .request(periphery::stats::GetSystemProcesses {}) .await?; - let res = serde_json::to_string(&stats)?; lock.insert( server.id, - (res.clone(), unix_timestamp_ms() + PROCESSES_EXPIRY) + (stats.clone(), unix_timestamp_ms() + PROCESSES_EXPIRY) .into(), ); - res + stats } }; Ok(res) @@ -305,19 +305,19 @@ impl ResolveToString for State { const STATS_PER_PAGE: i64 = 200; -impl Resolve for State { +impl Resolve for GetHistoricalServerStats { async fn resolve( - &self, - GetHistoricalServerStats { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let GetHistoricalServerStats { server, granularity, page, - }: GetHistoricalServerStats, - user: User, - ) -> anyhow::Result { + } = self; let server = resource::get_check_permissions::( &server, - &user, + user, PermissionLevel::Read, ) .await?; @@ -358,15 +358,14 @@ impl Resolve for State { } } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - ListDockerContainers { server }: ListDockerContainers, - user: User, - ) -> anyhow::Result { +impl Resolve for ListDockerContainers { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -374,20 +373,18 @@ impl ResolveToString for State { .get_or_insert_default(&server.id) .await; if let Some(containers) = &cache.containers { - serde_json::to_string(containers) - .context("failed to serialize response") + Ok(containers.clone()) } else { - Ok(String::from("[]")) + Ok(Vec::new()) } } } -impl Resolve for State { +impl Resolve for ListAllDockerContainers { async fn resolve( - &self, - ListAllDockerContainers { servers }: ListAllDockerContainers, - user: User, - ) -> anyhow::Result> { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let servers = resource::list_for_user::( Default::default(), &user, @@ -396,9 +393,9 @@ impl Resolve for State { .await? .into_iter() .filter(|server| { - servers.is_empty() - || servers.contains(&server.id) - || servers.contains(&server.name) + self.servers.is_empty() + || self.servers.contains(&server.id) + || self.servers.contains(&server.name) }); let mut containers = Vec::::new(); @@ -416,15 +413,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for InspectDockerContainer { async fn resolve( - &self, - InspectDockerContainer { server, container }: InspectDockerContainer, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -432,67 +428,74 @@ impl Resolve for State { .get_or_insert_default(&server.id) .await; if cache.state != ServerState::Ok { - return Err(anyhow!( - "Cannot inspect container: server is {:?}", - cache.state - )); + return Err( + anyhow!( + "Cannot inspect container: server is {:?}", + cache.state + ) + .into(), + ); } - periphery_client(&server)? - .request(InspectContainer { name: container }) - .await + let res = periphery_client(&server)? + .request(InspectContainer { + name: self.container, + }) + .await?; + Ok(res) } } const MAX_LOG_LENGTH: u64 = 5000; -impl Resolve for State { +impl Resolve for GetContainerLog { async fn resolve( - &self, - GetContainerLog { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let GetContainerLog { server, container, tail, timestamps, - }: GetContainerLog, - user: User, - ) -> anyhow::Result { + } = self; let server = resource::get_check_permissions::( &server, - &user, + user, PermissionLevel::Read, ) .await?; - periphery_client(&server)? + let res = periphery_client(&server)? .request(periphery::container::GetContainerLog { name: container, tail: cmp::min(tail, MAX_LOG_LENGTH), timestamps, }) .await - .context("failed at call to periphery") + .context("failed at call to periphery")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for SearchContainerLog { async fn resolve( - &self, - SearchContainerLog { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let SearchContainerLog { server, container, terms, combinator, invert, timestamps, - }: SearchContainerLog, - user: User, - ) -> anyhow::Result { + } = self; let server = resource::get_check_permissions::( &server, - &user, + user, PermissionLevel::Read, ) .await?; - periphery_client(&server)? + let res = periphery_client(&server)? .request(periphery::container::GetContainerLogSearch { name: container, terms, @@ -501,25 +504,25 @@ impl Resolve for State { timestamps, }) .await - .context("failed at call to periphery") + .context("failed at call to periphery")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for GetResourceMatchingContainer { async fn resolve( - &self, - GetResourceMatchingContainer { server, container }: GetResourceMatchingContainer, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; // first check deployments if let Ok(deployment) = - resource::get::(&container).await + resource::get::(&self.container).await { return Ok(GetResourceMatchingContainerResponse { resource: ResourceTarget::Deployment(deployment.id).into(), @@ -553,7 +556,7 @@ impl Resolve for State { warn!("{e:#}"); continue; } - }.is_match(&container); + }.is_match(&self.container); if is_match { return Ok(GetResourceMatchingContainerResponse { @@ -567,15 +570,14 @@ impl Resolve for State { } } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - ListDockerNetworks { server }: ListDockerNetworks, - user: User, - ) -> anyhow::Result { +impl Resolve for ListDockerNetworks { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -583,23 +585,21 @@ impl ResolveToString for State { .get_or_insert_default(&server.id) .await; if let Some(networks) = &cache.networks { - serde_json::to_string(networks) - .context("failed to serialize response") + Ok(networks.clone()) } else { - Ok(String::from("[]")) + Ok(Vec::new()) } } } -impl Resolve for State { +impl Resolve for InspectDockerNetwork { async fn resolve( - &self, - InspectDockerNetwork { server, network }: InspectDockerNetwork, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -607,26 +607,29 @@ impl Resolve for State { .get_or_insert_default(&server.id) .await; if cache.state != ServerState::Ok { - return Err(anyhow!( - "Cannot inspect network: server is {:?}", - cache.state - )); + return Err( + anyhow!( + "Cannot inspect network: server is {:?}", + cache.state + ) + .into(), + ); } - periphery_client(&server)? - .request(InspectNetwork { name: network }) - .await + let res = periphery_client(&server)? + .request(InspectNetwork { name: self.network }) + .await?; + Ok(res) } } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - ListDockerImages { server }: ListDockerImages, - user: User, - ) -> anyhow::Result { +impl Resolve for ListDockerImages { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -634,23 +637,21 @@ impl ResolveToString for State { .get_or_insert_default(&server.id) .await; if let Some(images) = &cache.images { - serde_json::to_string(images) - .context("failed to serialize response") + Ok(images.clone()) } else { - Ok(String::from("[]")) + Ok(Vec::new()) } } } -impl Resolve for State { +impl Resolve for InspectDockerImage { async fn resolve( - &self, - InspectDockerImage { server, image }: InspectDockerImage, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -658,26 +659,26 @@ impl Resolve for State { .get_or_insert_default(&server.id) .await; if cache.state != ServerState::Ok { - return Err(anyhow!( - "Cannot inspect image: server is {:?}", - cache.state - )); + return Err( + anyhow!("Cannot inspect image: server is {:?}", cache.state) + .into(), + ); } - periphery_client(&server)? - .request(InspectImage { name: image }) - .await + let res = periphery_client(&server)? + .request(InspectImage { name: self.image }) + .await?; + Ok(res) } } -impl Resolve for State { +impl Resolve for ListDockerImageHistory { async fn resolve( - &self, - ListDockerImageHistory { server, image }: ListDockerImageHistory, - user: User, - ) -> anyhow::Result> { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -685,26 +686,29 @@ impl Resolve for State { .get_or_insert_default(&server.id) .await; if cache.state != ServerState::Ok { - return Err(anyhow!( - "Cannot get image history: server is {:?}", - cache.state - )); + return Err( + anyhow!( + "Cannot get image history: server is {:?}", + cache.state + ) + .into(), + ); } - periphery_client(&server)? - .request(ImageHistory { name: image }) - .await + let res = periphery_client(&server)? + .request(ImageHistory { name: self.image }) + .await?; + Ok(res) } } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - ListDockerVolumes { server }: ListDockerVolumes, - user: User, - ) -> anyhow::Result { +impl Resolve for ListDockerVolumes { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -712,23 +716,21 @@ impl ResolveToString for State { .get_or_insert_default(&server.id) .await; if let Some(volumes) = &cache.volumes { - serde_json::to_string(volumes) - .context("failed to serialize response") + Ok(volumes.clone()) } else { - Ok(String::from("[]")) + Ok(Vec::new()) } } } -impl Resolve for State { +impl Resolve for InspectDockerVolume { async fn resolve( - &self, - InspectDockerVolume { server, volume }: InspectDockerVolume, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -736,26 +738,26 @@ impl Resolve for State { .get_or_insert_default(&server.id) .await; if cache.state != ServerState::Ok { - return Err(anyhow!( - "Cannot inspect volume: server is {:?}", - cache.state - )); + return Err( + anyhow!("Cannot inspect volume: server is {:?}", cache.state) + .into(), + ); } - periphery_client(&server)? - .request(InspectVolume { name: volume }) - .await + let res = periphery_client(&server)? + .request(InspectVolume { name: self.volume }) + .await?; + Ok(res) } } -impl ResolveToString for State { - async fn resolve_to_string( - &self, - ListComposeProjects { server }: ListComposeProjects, - user: User, - ) -> anyhow::Result { +impl Resolve for ListComposeProjects { + async fn resolve( + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Read, ) .await?; @@ -763,10 +765,9 @@ impl ResolveToString for State { .get_or_insert_default(&server.id) .await; if let Some(projects) = &cache.projects { - serde_json::to_string(projects) - .context("failed to serialize response") + Ok(projects.clone()) } else { - Ok(String::from("[]")) + Ok(Vec::new()) } } } diff --git a/bin/core/src/api/read/server_template.rs b/bin/core/src/api/read/server_template.rs index 503f92540..39dc325cc 100644 --- a/bin/core/src/api/read/server_template.rs +++ b/bin/core/src/api/read/server_template.rs @@ -3,7 +3,6 @@ use komodo_client::{ api::read::*, entities::{ permission::PermissionLevel, server_template::ServerTemplate, - user::User, }, }; use mongo_indexed::Document; @@ -11,66 +10,70 @@ use mungos::mongodb::bson::doc; use resolver_api::Resolve; use crate::{ - helpers::query::get_all_tags, - resource, - state::{db_client, State}, + helpers::query::get_all_tags, resource, state::db_client, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetServerTemplate { async fn resolve( - &self, - GetServerTemplate { server_template }: GetServerTemplate, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &server_template, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.server_template, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListServerTemplates { async fn resolve( - &self, - ListServerTemplates { query }: ListServerTemplates, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_for_user::( + self.query, user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullServerTemplates { async fn resolve( - &self, - ListFullServerTemplates { query }: ListFullServerTemplates, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::( - query, &user, &all_tags, + Ok( + resource::list_full_for_user::( + self.query, user, &all_tags, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for GetServerTemplatesSummary { async fn resolve( - &self, - GetServerTemplatesSummary {}: GetServerTemplatesSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let query = match resource::get_resource_object_ids_for_user::< ServerTemplate, >(&user) diff --git a/bin/core/src/api/read/stack.rs b/bin/core/src/api/read/stack.rs index 9122236f4..e1e467b77 100644 --- a/bin/core/src/api/read/stack.rs +++ b/bin/core/src/api/read/stack.rs @@ -7,7 +7,6 @@ use komodo_client::{ config::core::CoreConfig, permission::PermissionLevel, stack::{Stack, StackActionState, StackListItem, StackState}, - user::User, }, }; use periphery_client::api::compose::{ @@ -20,33 +19,35 @@ use crate::{ helpers::{periphery_client, query::get_all_tags}, resource, stack::get_stack_and_server, - state::{action_states, github_client, stack_status_cache, State}, + state::{action_states, github_client, stack_status_cache}, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetStack { async fn resolve( - &self, - GetStack { stack }: GetStack, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &stack, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.stack, + user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListStackServices { async fn resolve( - &self, - ListStackServices { stack }: ListStackServices, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let stack = resource::get_check_permissions::( - &stack, - &user, + &self.stack, + user, PermissionLevel::Read, ) .await?; @@ -63,25 +64,21 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetStackServiceLog { async fn resolve( - &self, - GetStackServiceLog { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let GetStackServiceLog { stack, service, tail, timestamps, - }: GetStackServiceLog, - user: User, - ) -> anyhow::Result { - let (stack, server) = get_stack_and_server( - &stack, - &user, - PermissionLevel::Read, - true, - ) - .await?; - periphery_client(&server)? + } = self; + let (stack, server) = + get_stack_and_server(&stack, user, PermissionLevel::Read, true) + .await?; + let res = periphery_client(&server)? .request(GetComposeServiceLog { project: stack.project_name(false), service, @@ -89,31 +86,28 @@ impl Resolve for State { timestamps, }) .await - .context("failed to get stack service log from periphery") + .context("failed to get stack service log from periphery")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for SearchStackServiceLog { async fn resolve( - &self, - SearchStackServiceLog { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let SearchStackServiceLog { stack, service, terms, combinator, invert, timestamps, - }: SearchStackServiceLog, - user: User, - ) -> anyhow::Result { - let (stack, server) = get_stack_and_server( - &stack, - &user, - PermissionLevel::Read, - true, - ) - .await?; - periphery_client(&server)? + } = self; + let (stack, server) = + get_stack_and_server(&stack, user, PermissionLevel::Read, true) + .await?; + let res = periphery_client(&server)? .request(GetComposeServiceLogSearch { project: stack.project_name(false), service, @@ -123,25 +117,26 @@ impl Resolve for State { timestamps, }) .await - .context("failed to get stack service log from periphery") + .context("failed to get stack service log from periphery")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for ListCommonStackExtraArgs { async fn resolve( - &self, - ListCommonStackExtraArgs { query }: ListCommonStackExtraArgs, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - let stacks = - resource::list_full_for_user::(query, &user, &all_tags) - .await - .context("failed to get resources matching query")?; + let stacks = resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await + .context("failed to get resources matching query")?; // first collect with guaranteed uniqueness let mut res = HashSet::::new(); @@ -158,21 +153,21 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListCommonStackBuildExtraArgs { async fn resolve( - &self, - ListCommonStackBuildExtraArgs { query }: ListCommonStackBuildExtraArgs, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - let stacks = - resource::list_full_for_user::(query, &user, &all_tags) - .await - .context("failed to get resources matching query")?; + let stacks = resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await + .context("failed to get resources matching query")?; // first collect with guaranteed uniqueness let mut res = HashSet::::new(); @@ -189,46 +184,50 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListStacks { async fn resolve( - &self, - ListStacks { query }: ListStacks, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags).await + Ok( + resource::list_for_user::(self.query, user, &all_tags) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullStacks { async fn resolve( - &self, - ListFullStacks { query }: ListFullStacks, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_full_for_user::( + self.query, user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for GetStackActionState { async fn resolve( - &self, - GetStackActionState { stack }: GetStackActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let stack = resource::get_check_permissions::( - &stack, - &user, + &self.stack, + user, PermissionLevel::Read, ) .await?; @@ -242,15 +241,14 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetStacksSummary { async fn resolve( - &self, - GetStacksSummary {}: GetStacksSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let stacks = resource::list_full_for_user::( Default::default(), - &user, + user, &[], ) .await @@ -276,12 +274,11 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetStackWebhooksEnabled { async fn resolve( - &self, - GetStackWebhooksEnabled { stack }: GetStackWebhooksEnabled, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let Some(github) = github_client() else { return Ok(GetStackWebhooksEnabledResponse { managed: false, @@ -291,8 +288,8 @@ impl Resolve for State { }; let stack = resource::get_check_permissions::( - &stack, - &user, + &self.stack, + user, PermissionLevel::Read, ) .await?; diff --git a/bin/core/src/api/read/sync.rs b/bin/core/src/api/read/sync.rs index 2606890fa..085f89ba3 100644 --- a/bin/core/src/api/read/sync.rs +++ b/bin/core/src/api/read/sync.rs @@ -8,7 +8,6 @@ use komodo_client::{ ResourceSync, ResourceSyncActionState, ResourceSyncListItem, ResourceSyncState, }, - user::User, }, }; use resolver_api::Resolve; @@ -17,69 +16,73 @@ use crate::{ config::core_config, helpers::query::get_all_tags, resource, - state::{ - action_states, github_client, resource_sync_state_cache, State, - }, + state::{action_states, github_client, resource_sync_state_cache}, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetResourceSync { async fn resolve( - &self, - GetResourceSync { sync }: GetResourceSync, - user: User, - ) -> anyhow::Result { - resource::get_check_permissions::( - &sync, - &user, - PermissionLevel::Read, + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + Ok( + resource::get_check_permissions::( + &self.sync, + &user, + PermissionLevel::Read, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for ListResourceSyncs { async fn resolve( - &self, - ListResourceSyncs { query }: ListResourceSyncs, - user: User, - ) -> anyhow::Result> { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result> { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_for_user::(query, &user, &all_tags) - .await + Ok( + resource::list_for_user::( + self.query, &user, &all_tags, + ) + .await?, + ) } } -impl Resolve for State { +impl Resolve for ListFullResourceSyncs { async fn resolve( - &self, - ListFullResourceSyncs { query }: ListFullResourceSyncs, - user: User, - ) -> anyhow::Result { - let all_tags = if query.tags.is_empty() { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let all_tags = if self.query.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; - resource::list_full_for_user::( - query, &user, &all_tags, + Ok( + resource::list_full_for_user::( + self.query, &user, &all_tags, + ) + .await?, ) - .await } } -impl Resolve for State { +impl Resolve for GetResourceSyncActionState { async fn resolve( - &self, - GetResourceSyncActionState { sync }: GetResourceSyncActionState, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let sync = resource::get_check_permissions::( - &sync, - &user, + &self.sync, + user, PermissionLevel::Read, ) .await?; @@ -93,16 +96,15 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetResourceSyncsSummary { async fn resolve( - &self, - GetResourceSyncsSummary {}: GetResourceSyncsSummary, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let resource_syncs = resource::list_full_for_user::( Default::default(), - &user, + user, &[], ) .await @@ -159,12 +161,11 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetSyncWebhooksEnabled { async fn resolve( - &self, - GetSyncWebhooksEnabled { sync }: GetSyncWebhooksEnabled, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let Some(github) = github_client() else { return Ok(GetSyncWebhooksEnabledResponse { managed: false, @@ -174,8 +175,8 @@ impl Resolve for State { }; let sync = resource::get_check_permissions::( - &sync, - &user, + &self.sync, + user, PermissionLevel::Read, ) .await?; diff --git a/bin/core/src/api/read/tag.rs b/bin/core/src/api/read/tag.rs index be1d032da..6bb849181 100644 --- a/bin/core/src/api/read/tag.rs +++ b/bin/core/src/api/read/tag.rs @@ -1,39 +1,31 @@ use anyhow::Context; use komodo_client::{ api::read::{GetTag, ListTags}, - entities::{tag::Tag, user::User}, + entities::tag::Tag, }; use mongo_indexed::doc; use mungos::{find::find_collect, mongodb::options::FindOptions}; use resolver_api::Resolve; -use crate::{ - helpers::query::get_tag, - state::{db_client, State}, -}; +use crate::{helpers::query::get_tag, state::db_client}; -impl Resolve for State { - async fn resolve( - &self, - GetTag { tag }: GetTag, - _: User, - ) -> anyhow::Result { - get_tag(&tag).await +use super::ReadArgs; + +impl Resolve for GetTag { + async fn resolve(self, _: &ReadArgs) -> serror::Result { + Ok(get_tag(&self.tag).await?) } } -impl Resolve for State { - async fn resolve( - &self, - ListTags { query }: ListTags, - _: User, - ) -> anyhow::Result> { - find_collect( +impl Resolve for ListTags { + async fn resolve(self, _: &ReadArgs) -> serror::Result> { + let res = find_collect( &db_client().tags, - query, + self.query, FindOptions::builder().sort(doc! { "name": 1 }).build(), ) .await - .context("failed to get tags from db") + .context("failed to get tags from db")?; + Ok(res) } } diff --git a/bin/core/src/api/read/toml.rs b/bin/core/src/api/read/toml.rs index 5186e6196..376a41e89 100644 --- a/bin/core/src/api/read/toml.rs +++ b/bin/core/src/api/read/toml.rs @@ -10,8 +10,7 @@ use komodo_client::{ deployment::Deployment, permission::PermissionLevel, procedure::Procedure, repo::Repo, resource::ResourceQuery, server::Server, server_template::ServerTemplate, stack::Stack, - sync::ResourceSync, toml::ResourcesToml, user::User, - ResourceTarget, + sync::ResourceSync, toml::ResourcesToml, ResourceTarget, }, }; use mungos::find::find_collect; @@ -22,7 +21,7 @@ use crate::{ get_all_tags, get_id_to_tags, get_user_user_group_ids, }, resource, - state::{db_client, State}, + state::db_client, sync::{ toml::{convert_resource, ToToml, TOML_PRETTY_OPTIONS}, user_groups::convert_user_groups, @@ -30,23 +29,26 @@ use crate::{ }, }; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for ExportAllResourcesToToml { async fn resolve( - &self, - ExportAllResourcesToToml { tags }: ExportAllResourcesToToml, - user: User, - ) -> anyhow::Result { + self, + args: &ReadArgs, + ) -> serror::Result { let mut targets = Vec::::new(); - let all_tags = if tags.is_empty() { + let all_tags = if self.tags.is_empty() { vec![] } else { get_all_tags(None).await? }; + let ReadArgs { user } = args; + targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -56,7 +58,7 @@ impl Resolve for State { ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -66,7 +68,7 @@ impl Resolve for State { ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -74,19 +76,9 @@ impl Resolve for State { .into_iter() .map(|resource| ResourceTarget::Server(resource.id)), ); - targets.extend( - resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), - &user, - &all_tags, - ) - .await? - .into_iter() - .map(|resource| ResourceTarget::Deployment(resource.id)), - ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -94,9 +86,19 @@ impl Resolve for State { .into_iter() .map(|resource| ResourceTarget::Stack(resource.id)), ); + targets.extend( + resource::list_for_user::( + ResourceQuery::builder().tags(self.tags.clone()).build(), + &user, + &all_tags, + ) + .await? + .into_iter() + .map(|resource| ResourceTarget::Deployment(resource.id)), + ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -106,7 +108,7 @@ impl Resolve for State { ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -116,7 +118,7 @@ impl Resolve for State { ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -126,7 +128,7 @@ impl Resolve for State { ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -136,7 +138,7 @@ impl Resolve for State { ); targets.extend( resource::list_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -146,7 +148,7 @@ impl Resolve for State { ); targets.extend( resource::list_full_for_user::( - ResourceQuery::builder().tags(tags.clone()).build(), + ResourceQuery::builder().tags(self.tags.clone()).build(), &user, &all_tags, ) @@ -156,7 +158,7 @@ impl Resolve for State { .map(|resource| ResourceTarget::ResourceSync(resource.id)), ); - let user_groups = if user.admin && tags.is_empty() { + let user_groups = if user.admin && self.tags.is_empty() { find_collect(&db_client().user_groups, None, None) .await .context("failed to query db for user groups")? @@ -167,32 +169,30 @@ impl Resolve for State { get_user_user_group_ids(&user.id).await? }; - self - .resolve( - ExportResourcesToToml { - targets, - user_groups, - include_variables: tags.is_empty(), - }, - user, - ) - .await - } -} - -impl Resolve for State { - async fn resolve( - &self, ExportResourcesToToml { + targets, + user_groups, + include_variables: self.tags.is_empty(), + } + .resolve(args) + .await + } +} + +impl Resolve for ExportResourcesToToml { + async fn resolve( + self, + args: &ReadArgs, + ) -> serror::Result { + let ExportResourcesToToml { targets, user_groups, include_variables, - }: ExportResourcesToToml, - user: User, - ) -> anyhow::Result { + } = self; let mut res = ResourcesToml::default(); let all = AllResourcesById::load().await?; let id_to_tags = get_id_to_tags(None).await?; + let ReadArgs { user } = args; for target in targets { match target { ResourceTarget::Alerter(id) => { @@ -368,7 +368,7 @@ impl Resolve for State { }; } - add_user_groups(user_groups, &mut res, &all, &user) + add_user_groups(user_groups, &mut res, &all, args) .await .context("failed to add user groups")?; @@ -398,11 +398,12 @@ async fn add_user_groups( user_groups: Vec, res: &mut ResourcesToml, all: &AllResourcesById, - user: &User, + args: &ReadArgs, ) -> anyhow::Result<()> { - let user_groups = State - .resolve(ListUserGroups {}, user.clone()) - .await? + let user_groups = ListUserGroups {} + .resolve(args) + .await + .map_err(|e| e.error)? .into_iter() .filter(|ug| { user_groups.contains(&ug.name) || user_groups.contains(&ug.id) diff --git a/bin/core/src/api/read/update.rs b/bin/core/src/api/read/update.rs index 89d59440d..f5f0a1d26 100644 --- a/bin/core/src/api/read/update.rs +++ b/bin/core/src/api/read/update.rs @@ -28,22 +28,19 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::{ - config::core_config, - resource, - state::{db_client, State}, -}; +use crate::{config::core_config, resource, state::db_client}; + +use super::ReadArgs; const UPDATES_PER_PAGE: i64 = 100; -impl Resolve for State { +impl Resolve for ListUpdates { async fn resolve( - &self, - ListUpdates { query, page }: ListUpdates, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let query = if user.admin || core_config().transparent_mode { - query + self.query } else { let server_query = resource::get_resource_ids_for_user::(&user) @@ -157,7 +154,7 @@ impl Resolve for State { }) .unwrap_or_else(|| doc! { "target.type": "ResourceSync" }); - let mut query = query.unwrap_or_default(); + let mut query = self.query.unwrap_or_default(); query.extend(doc! { "$or": [ server_query, @@ -188,7 +185,7 @@ impl Resolve for State { query, FindOptions::builder() .sort(doc! { "start_ts": -1 }) - .skip(page as u64 * UPDATES_PER_PAGE as u64) + .skip(self.page as u64 * UPDATES_PER_PAGE as u64) .limit(UPDATES_PER_PAGE) .build(), ) @@ -220,7 +217,7 @@ impl Resolve for State { .collect::>(); let next_page = if updates.len() == UPDATES_PER_PAGE as usize { - Some(page + 1) + Some(self.page + 1) } else { None }; @@ -229,13 +226,12 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for GetUpdate { async fn resolve( - &self, - GetUpdate { id }: GetUpdate, - user: User, - ) -> anyhow::Result { - let update = find_one_by_id(&db_client().updates, &id) + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let update = find_one_by_id(&db_client().updates, &self.id) .await .context("failed to query to db")? .context("no update exists with given id")?; @@ -244,9 +240,9 @@ impl Resolve for State { } match &update.target { ResourceTarget::System(_) => { - return Err(anyhow!( - "user must be admin to view system updates" - )) + return Err( + anyhow!("user must be admin to view system updates").into(), + ) } ResourceTarget::Server(id) => { resource::get_check_permissions::( diff --git a/bin/core/src/api/read/user.rs b/bin/core/src/api/read/user.rs index d120e5bbb..ad62b6d17 100644 --- a/bin/core/src/api/read/user.rs +++ b/bin/core/src/api/read/user.rs @@ -6,7 +6,7 @@ use komodo_client::{ ListApiKeysForServiceUserResponse, ListApiKeysResponse, ListUsers, ListUsersResponse, }, - entities::user::{admin_service_user, User, UserConfig}, + entities::user::{admin_service_user, UserConfig}, }; use mungos::{ by_id::find_one_by_id, @@ -15,25 +15,23 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::{ - helpers::query::get_user, - state::{db_client, State}, -}; +use crate::{helpers::query::get_user, state::db_client}; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetUsername { async fn resolve( - &self, - GetUsername { user_id }: GetUsername, - _: User, - ) -> anyhow::Result { - if let Some(user) = admin_service_user(&user_id) { + self, + _: &ReadArgs, + ) -> serror::Result { + if let Some(user) = admin_service_user(&self.user_id) { return Ok(GetUsernameResponse { username: user.username, avatar: None, }); } - let user = find_one_by_id(&db_client().users, &user_id) + let user = find_one_by_id(&db_client().users, &self.user_id) .await .context("failed at mongo query for user")? .context("no user found with id")?; @@ -51,27 +49,27 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for FindUser { async fn resolve( - &self, - FindUser { user }: FindUser, - admin: User, - ) -> anyhow::Result { + self, + ReadArgs { user: admin }: &ReadArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This method is admin only.")); + return Err(anyhow!("This method is admin only.").into()); } - get_user(&user).await + Ok(get_user(&self.user).await?) } } -impl Resolve for State { +impl Resolve for ListUsers { async fn resolve( - &self, - ListUsers {}: ListUsers, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("this route is only accessable by admins")); + return Err( + anyhow!("this route is only accessable by admins").into(), + ); } let mut users = find_collect( &db_client().users, @@ -85,12 +83,11 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListApiKeys { async fn resolve( - &self, - ListApiKeys {}: ListApiKeys, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let api_keys = find_collect( &db_client().api_keys, doc! { "user_id": &user.id }, @@ -108,20 +105,19 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListApiKeysForServiceUser { async fn resolve( - &self, - ListApiKeysForServiceUser { user }: ListApiKeysForServiceUser, - admin: User, - ) -> anyhow::Result { + self, + ReadArgs { user: admin }: &ReadArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This method is admin only.")); + return Err(anyhow!("This method is admin only.").into()); } - let user = get_user(&user).await?; + let user = get_user(&self.user).await?; let UserConfig::Service { .. } = user.config else { - return Err(anyhow!("Given user is not service user")); + return Err(anyhow!("Given user is not service user").into()); }; let api_keys = find_collect( &db_client().api_keys, diff --git a/bin/core/src/api/read/user_group.rs b/bin/core/src/api/read/user_group.rs index 363332eaa..2f0eceae0 100644 --- a/bin/core/src/api/read/user_group.rs +++ b/bin/core/src/api/read/user_group.rs @@ -1,13 +1,7 @@ use std::str::FromStr; use anyhow::Context; -use komodo_client::{ - api::read::{ - GetUserGroup, GetUserGroupResponse, ListUserGroups, - ListUserGroupsResponse, - }, - entities::user::User, -}; +use komodo_client::api::read::*; use mungos::{ find::find_collect, mongodb::{ @@ -17,48 +11,50 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::state::{db_client, State}; +use crate::state::db_client; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetUserGroup { async fn resolve( - &self, - GetUserGroup { user_group }: GetUserGroup, - user: User, - ) -> anyhow::Result { - let mut filter = match ObjectId::from_str(&user_group) { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let mut filter = match ObjectId::from_str(&self.user_group) { Ok(id) => doc! { "_id": id }, - Err(_) => doc! { "name": &user_group }, + Err(_) => doc! { "name": &self.user_group }, }; // Don't allow non admin users to get UserGroups they aren't a part of. if !user.admin { // Filter for only UserGroups which contain the users id filter.insert("users", &user.id); } - db_client() + let res = db_client() .user_groups .find_one(filter) .await .context("failed to query db for user groups")? - .context("no UserGroup found with given name or id") + .context("no UserGroup found with given name or id")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for ListUserGroups { async fn resolve( - &self, - ListUserGroups {}: ListUserGroups, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let mut filter = Document::new(); if !user.admin { filter.insert("users", &user.id); } - find_collect( + let res = find_collect( &db_client().user_groups, filter, FindOptions::builder().sort(doc! { "name": 1 }).build(), ) .await - .context("failed to query db for UserGroups") + .context("failed to query db for UserGroups")?; + Ok(res) } } diff --git a/bin/core/src/api/read/variable.rs b/bin/core/src/api/read/variable.rs index 9fa42c517..f196c1df2 100644 --- a/bin/core/src/api/read/variable.rs +++ b/bin/core/src/api/read/variable.rs @@ -1,27 +1,19 @@ use anyhow::Context; -use komodo_client::{ - api::read::{ - GetVariable, GetVariableResponse, ListVariables, - ListVariablesResponse, - }, - entities::user::User, -}; +use komodo_client::api::read::*; use mongo_indexed::doc; use mungos::{find::find_collect, mongodb::options::FindOptions}; use resolver_api::Resolve; -use crate::{ - helpers::query::get_variable, - state::{db_client, State}, -}; +use crate::{helpers::query::get_variable, state::db_client}; -impl Resolve for State { +use super::ReadArgs; + +impl Resolve for GetVariable { async fn resolve( - &self, - GetVariable { name }: GetVariable, - user: User, - ) -> anyhow::Result { - let mut variable = get_variable(&name).await?; + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { + let mut variable = get_variable(&self.name).await?; if !variable.is_secret || user.admin { return Ok(variable); } @@ -30,12 +22,11 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for ListVariables { async fn resolve( - &self, - ListVariables {}: ListVariables, - user: User, - ) -> anyhow::Result { + self, + ReadArgs { user }: &ReadArgs, + ) -> serror::Result { let variables = find_collect( &db_client().variables, None, diff --git a/bin/core/src/api/user.rs b/bin/core/src/api/user.rs index 8d6bd527c..959da98e3 100644 --- a/bin/core/src/api/user.rs +++ b/bin/core/src/api/user.rs @@ -2,19 +2,15 @@ use std::{collections::VecDeque, time::Instant}; use anyhow::{anyhow, Context}; use axum::{middleware, routing::post, Extension, Json, Router}; -use axum_extra::{headers::ContentType, TypedHeader}; +use derive_variants::EnumVariants; use komodo_client::{ - api::user::{ - CreateApiKey, CreateApiKeyResponse, DeleteApiKey, - DeleteApiKeyResponse, PushRecentlyViewed, - PushRecentlyViewedResponse, SetLastSeenUpdate, - SetLastSeenUpdateResponse, - }, + api::user::*, entities::{api_key::ApiKey, komodo_timestamp, user::User}, }; use mongo_indexed::doc; use mungos::{by_id::update_one_by_id, mongodb::bson::to_bson}; -use resolver_api::{derive::Resolver, Resolve, Resolver}; +use resolver_api::Resolve; +use response::Response; use serde::{Deserialize, Serialize}; use typeshare::typeshare; use uuid::Uuid; @@ -22,13 +18,20 @@ use uuid::Uuid; use crate::{ auth::auth_request, helpers::{query::get_user, random_string}, - state::{db_client, State}, + state::db_client, }; +pub struct UserArgs { + pub user: User, +} + #[typeshare] -#[derive(Serialize, Deserialize, Debug, Clone, Resolver)] -#[resolver_target(State)] -#[resolver_args(User)] +#[derive( + Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants, +)] +#[args(UserArgs)] +#[response(Response)] +#[error(serror::Error)] #[serde(tag = "type", content = "params")] enum UserRequest { PushRecentlyViewed(PushRecentlyViewed), @@ -47,47 +50,37 @@ pub fn router() -> Router { async fn handler( Extension(user): Extension, Json(request): Json, -) -> serror::Result<(TypedHeader, String)> { +) -> serror::Result { let timer = Instant::now(); let req_id = Uuid::new_v4(); debug!( "/user request {req_id} | user: {} ({})", user.username, user.id ); - let res = - State - .resolve_request(request, user) - .await - .map_err(|e| match e { - resolver_api::Error::Serialization(e) => { - anyhow!("{e:?}").context("response serialization error") - } - resolver_api::Error::Inner(e) => e, - }); + let res = request.resolve(&UserArgs { user }).await; if let Err(e) = &res { - warn!("/user request {req_id} error: {e:#}"); + warn!("/user request {req_id} error: {:#}", e.error); } let elapsed = timer.elapsed(); debug!("/user request {req_id} | resolve time: {elapsed:?}"); - Ok((TypedHeader(ContentType::json()), res?)) + res.map(|res| res.0) } const RECENTLY_VIEWED_MAX: usize = 10; -impl Resolve for State { +impl Resolve for PushRecentlyViewed { #[instrument( name = "PushRecentlyViewed", level = "debug", - skip(self, user) + skip(user) )] async fn resolve( - &self, - PushRecentlyViewed { resource }: PushRecentlyViewed, - user: User, - ) -> anyhow::Result { + self, + UserArgs { user }: &UserArgs, + ) -> serror::Result { let user = get_user(&user.id).await?; - let (resource_type, id) = resource.extract_variant_id(); + let (resource_type, id) = self.resource.extract_variant_id(); let update = match user.recents.get(&resource_type) { Some(recents) => { let mut recents = recents @@ -117,17 +110,16 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for SetLastSeenUpdate { #[instrument( name = "SetLastSeenUpdate", level = "debug", - skip(self, user) + skip(user) )] async fn resolve( - &self, - SetLastSeenUpdate {}: SetLastSeenUpdate, - user: User, - ) -> anyhow::Result { + self, + UserArgs { user }: &UserArgs, + ) -> serror::Result { update_one_by_id( &db_client().users, &user.id, @@ -145,17 +137,12 @@ impl Resolve for State { const SECRET_LENGTH: usize = 40; const BCRYPT_COST: u32 = 10; -impl Resolve for State { - #[instrument( - name = "CreateApiKey", - level = "debug", - skip(self, user) - )] +impl Resolve for CreateApiKey { + #[instrument(name = "CreateApiKey", level = "debug", skip(user))] async fn resolve( - &self, - CreateApiKey { name, expires }: CreateApiKey, - user: User, - ) -> anyhow::Result { + self, + UserArgs { user }: &UserArgs, + ) -> serror::Result { let user = get_user(&user.id).await?; let key = format!("K-{}", random_string(SECRET_LENGTH)); @@ -164,12 +151,12 @@ impl Resolve for State { .context("failed at hashing secret string")?; let api_key = ApiKey { - name, + name: self.name, key: key.clone(), secret: secret_hash, user_id: user.id.clone(), created_at: komodo_timestamp(), - expires, + expires: self.expires, }; db_client() .api_keys @@ -180,26 +167,21 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument( - name = "DeleteApiKey", - level = "debug", - skip(self, user) - )] +impl Resolve for DeleteApiKey { + #[instrument(name = "DeleteApiKey", level = "debug", skip(user))] async fn resolve( - &self, - DeleteApiKey { key }: DeleteApiKey, - user: User, - ) -> anyhow::Result { + self, + UserArgs { user }: &UserArgs, + ) -> serror::Result { let client = db_client(); let key = client .api_keys - .find_one(doc! { "key": &key }) + .find_one(doc! { "key": &self.key }) .await .context("failed at db query")? .context("no api key with key found")?; if user.id != key.user_id { - return Err(anyhow!("api key does not belong to user")); + return Err(anyhow!("api key does not belong to user").into()); } client .api_keys diff --git a/bin/core/src/api/write/action.rs b/bin/core/src/api/write/action.rs index f18c3a6b9..2511ceb82 100644 --- a/bin/core/src/api/write/action.rs +++ b/bin/core/src/api/write/action.rs @@ -2,70 +2,70 @@ use komodo_client::{ api::write::*, entities::{ action::Action, permission::PermissionLevel, update::Update, - user::User, }, }; use resolver_api::Resolve; -use crate::{resource, state::State}; +use crate::resource; -impl Resolve for State { - #[instrument(name = "CreateAction", skip(self, user))] - async fn resolve( - &self, - CreateAction { name, config }: CreateAction, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await - } -} +use super::WriteArgs; -impl Resolve for State { - #[instrument(name = "CopyAction", skip(self, user))] +impl Resolve for CreateAction { + #[instrument(name = "CreateAction", skip(user))] async fn resolve( - &self, - CopyAction { name, id }: CopyAction, - user: User, - ) -> anyhow::Result { - let Action { config, .. } = resource::get_check_permissions::< - Action, - >( - &id, &user, PermissionLevel::Write + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, ) - .await?; - resource::create::(&name, config.into(), &user).await } } -impl Resolve for State { - #[instrument(name = "UpdateAction", skip(self, user))] +impl Resolve for CopyAction { + #[instrument(name = "CopyAction", skip(user))] async fn resolve( - &self, - UpdateAction { id, config }: UpdateAction, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + let Action { config, .. } = + resource::get_check_permissions::( + &self.id, + &user, + PermissionLevel::Write, + ) + .await?; + Ok( + resource::create::(&self.name, config.into(), &user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RenameAction", skip(self, user))] +impl Resolve for UpdateAction { + #[instrument(name = "UpdateAction", skip(user))] async fn resolve( - &self, - RenameAction { id, name }: RenameAction, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::update::(&self.id, self.config, user).await?) } } -impl Resolve for State { - #[instrument(name = "DeleteAction", skip(self, user))] +impl Resolve for RenameAction { + #[instrument(name = "RenameAction", skip(user))] async fn resolve( - &self, - DeleteAction { id }: DeleteAction, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::rename::(&self.id, &self.name, user).await?) + } +} + +impl Resolve for DeleteAction { + #[instrument(name = "DeleteAction", skip(args))] + async fn resolve(self, args: &WriteArgs) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } diff --git a/bin/core/src/api/write/alerter.rs b/bin/core/src/api/write/alerter.rs index 2047f4872..e59afbdcc 100644 --- a/bin/core/src/api/write/alerter.rs +++ b/bin/core/src/api/write/alerter.rs @@ -2,70 +2,76 @@ use komodo_client::{ api::write::*, entities::{ alerter::Alerter, permission::PermissionLevel, update::Update, - user::User, }, }; use resolver_api::Resolve; -use crate::{resource, state::State}; +use crate::resource; -impl Resolve for State { - #[instrument(name = "CreateAlerter", skip(self, user))] - async fn resolve( - &self, - CreateAlerter { name, config }: CreateAlerter, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await - } -} +use super::WriteArgs; -impl Resolve for State { - #[instrument(name = "CopyAlerter", skip(self, user))] +impl Resolve for CreateAlerter { + #[instrument(name = "CreateAlerter", skip(user))] async fn resolve( - &self, - CopyAlerter { name, id }: CopyAlerter, - user: User, - ) -> anyhow::Result { - let Alerter { config, .. } = resource::get_check_permissions::< - Alerter, - >( - &id, &user, PermissionLevel::Write + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, ) - .await?; - resource::create::(&name, config.into(), &user).await } } -impl Resolve for State { - #[instrument(name = "DeleteAlerter", skip(self, user))] +impl Resolve for CopyAlerter { + #[instrument(name = "CopyAlerter", skip(user))] async fn resolve( - &self, - DeleteAlerter { id }: DeleteAlerter, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + let Alerter { config, .. } = + resource::get_check_permissions::( + &self.id, + &user, + PermissionLevel::Write, + ) + .await?; + Ok( + resource::create::(&self.name, config.into(), user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "UpdateAlerter", skip(self, user))] +impl Resolve for DeleteAlerter { + #[instrument(name = "DeleteAlerter", skip(args))] async fn resolve( - &self, - UpdateAlerter { id, config }: UpdateAlerter, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + args: &WriteArgs, + ) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "RenameAlerter", skip(self, user))] +impl Resolve for UpdateAlerter { + #[instrument(name = "UpdateAlerter", skip(user))] async fn resolve( - &self, - RenameAlerter { id, name }: RenameAlerter, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::update::(&self.id, self.config, user) + .await?, + ) + } +} + +impl Resolve for RenameAlerter { + #[instrument(name = "RenameAlerter", skip(user))] + async fn resolve( + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::rename::(&self.id, &self.name, user).await?) } } diff --git a/bin/core/src/api/write/build.rs b/bin/core/src/api/write/build.rs index e1ee86abd..307890eb9 100644 --- a/bin/core/src/api/write/build.rs +++ b/bin/core/src/api/write/build.rs @@ -7,7 +7,6 @@ use komodo_client::{ config::core::CoreConfig, permission::PermissionLevel, update::Update, - user::User, CloneArgs, NoData, }, }; @@ -22,89 +21,88 @@ use crate::{ config::core_config, helpers::git_token, resource, - state::{db_client, github_client, State}, + state::{db_client, github_client}, }; -impl Resolve for State { - #[instrument(name = "CreateBuild", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateBuild { + #[instrument(name = "CreateBuild", skip(user))] async fn resolve( - &self, - CreateBuild { name, config }: CreateBuild, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "CopyBuild", skip(self, user))] +impl Resolve for CopyBuild { + #[instrument(name = "CopyBuild", skip(user))] async fn resolve( - &self, - CopyBuild { name, id }: CopyBuild, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Build { mut config, .. } = resource::get_check_permissions::( - &id, - &user, + &self.id, + user, PermissionLevel::Write, ) .await?; // reset version to 0.0.0 config.version = Default::default(); - resource::create::(&name, config.into(), &user).await + Ok( + resource::create::(&self.name, config.into(), user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteBuild", skip(self, user))] +impl Resolve for DeleteBuild { + #[instrument(name = "DeleteBuild", skip(args))] + async fn resolve(self, args: &WriteArgs) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) + } +} + +impl Resolve for UpdateBuild { + #[instrument(name = "UpdateBuild", skip(user))] async fn resolve( - &self, - DeleteBuild { id }: DeleteBuild, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::update::(&self.id, self.config, user).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateBuild", skip(self, user))] +impl Resolve for RenameBuild { + #[instrument(name = "RenameBuild", skip(user))] async fn resolve( - &self, - UpdateBuild { id, config }: UpdateBuild, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::rename::(&self.id, &self.name, user).await?) } } -impl Resolve for State { - #[instrument(name = "RenameBuild", skip(self, user))] - async fn resolve( - &self, - RenameBuild { id, name }: RenameBuild, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await - } -} - -impl Resolve for State { +impl Resolve for RefreshBuildCache { #[instrument( name = "RefreshBuildCache", level = "debug", - skip(self, user) + skip(user) )] async fn resolve( - &self, - RefreshBuildCache { build }: RefreshBuildCache, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { // 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 = resource::get_check_permissions::( - &build, - &user, + &self.build, + user, PermissionLevel::Execute, ) .await?; @@ -178,39 +176,44 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "CreateBuildWebhook", skip(self, user))] +impl Resolve for CreateBuildWebhook { + #[instrument(name = "CreateBuildWebhook", skip(args))] async fn resolve( - &self, - CreateBuildWebhook { build }: CreateBuildWebhook, - user: User, - ) -> anyhow::Result { + self, + args: &WriteArgs, + ) -> serror::Result { let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; + let WriteArgs { user } = args; + let build = resource::get_check_permissions::( - &build, - &user, + &self.build, + user, PermissionLevel::Write, ) .await?; if build.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't create webhook" - )); + return Err( + anyhow!("No repo configured, can't create webhook").into(), + ); } let mut split = build.config.repo.split('/'); let owner = split.next().context("Build repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo = @@ -271,64 +274,65 @@ impl Resolve for State { .context("failed to create webhook")?; if !build.config.webhook_enabled { - self - .resolve( - UpdateBuild { - id: build.id, - config: PartialBuildConfig { - webhook_enabled: Some(true), - ..Default::default() - }, - }, - user, - ) - .await - .context("failed to update build to enable webhook")?; + UpdateBuild { + id: build.id, + config: PartialBuildConfig { + webhook_enabled: Some(true), + ..Default::default() + }, + } + .resolve(args) + .await + .map_err(|e| e.error) + .context("failed to update build to enable webhook")?; } Ok(NoData {}) } } -impl Resolve for State { - #[instrument(name = "DeleteBuildWebhook", skip(self, user))] +impl Resolve for DeleteBuildWebhook { + #[instrument(name = "DeleteBuildWebhook", skip(user))] async fn resolve( - &self, - DeleteBuildWebhook { build }: DeleteBuildWebhook, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; let build = resource::get_check_permissions::( - &build, + &self.build, &user, PermissionLevel::Write, ) .await?; if build.config.git_provider != "github.com" { - return Err(anyhow!( - "Can only manage github.com repo webhooks" - )); + return Err( + anyhow!("Can only manage github.com repo webhooks").into(), + ); } if build.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't delete webhook" - )); + return Err( + anyhow!("No repo configured, can't delete webhook").into(), + ); } let mut split = build.config.repo.split('/'); let owner = split.next().context("Build repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo = diff --git a/bin/core/src/api/write/builder.rs b/bin/core/src/api/write/builder.rs index d99fabe0d..434513b7a 100644 --- a/bin/core/src/api/write/builder.rs +++ b/bin/core/src/api/write/builder.rs @@ -2,70 +2,79 @@ use komodo_client::{ api::write::*, entities::{ builder::Builder, permission::PermissionLevel, update::Update, - user::User, }, }; use resolver_api::Resolve; -use crate::{resource, state::State}; +use crate::resource; -impl Resolve for State { - #[instrument(name = "CreateBuilder", skip(self, user))] - async fn resolve( - &self, - CreateBuilder { name, config }: CreateBuilder, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await - } -} +use super::WriteArgs; -impl Resolve for State { - #[instrument(name = "CopyBuilder", skip(self, user))] +impl Resolve for CreateBuilder { + #[instrument(name = "CreateBuilder", skip(user))] async fn resolve( - &self, - CopyBuilder { name, id }: CopyBuilder, - user: User, - ) -> anyhow::Result { - let Builder { config, .. } = resource::get_check_permissions::< - Builder, - >( - &id, &user, PermissionLevel::Write + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, ) - .await?; - resource::create::(&name, config.into(), &user).await } } -impl Resolve for State { - #[instrument(name = "DeleteBuilder", skip(self, user))] +impl Resolve for CopyBuilder { + #[instrument(name = "CopyBuilder", skip(user))] async fn resolve( - &self, - DeleteBuilder { id }: DeleteBuilder, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + let Builder { config, .. } = + resource::get_check_permissions::( + &self.id, + user, + PermissionLevel::Write, + ) + .await?; + Ok( + resource::create::(&self.name, config.into(), &user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "UpdateBuilder", skip(self, user))] +impl Resolve for DeleteBuilder { + #[instrument(name = "DeleteBuilder", skip(args))] async fn resolve( - &self, - UpdateBuilder { id, config }: UpdateBuilder, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + args: &WriteArgs, + ) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "RenameBuilder", skip(self, user))] +impl Resolve for UpdateBuilder { + #[instrument(name = "UpdateBuilder", skip(user))] async fn resolve( - &self, - RenameBuilder { id, name }: RenameBuilder, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::update::(&self.id, self.config, user) + .await?, + ) + } +} + +impl Resolve for RenameBuilder { + #[instrument(name = "RenameBuilder", skip(user))] + async fn resolve( + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::rename::(&self.id, &self.name, &user) + .await?, + ) } } diff --git a/bin/core/src/api/write/deployment.rs b/bin/core/src/api/write/deployment.rs index dd4ccca51..3dbadf230 100644 --- a/bin/core/src/api/write/deployment.rs +++ b/bin/core/src/api/write/deployment.rs @@ -12,7 +12,6 @@ use komodo_client::{ server::{Server, ServerState}, to_komodo_name, update::Update, - user::User, Operation, }, }; @@ -27,51 +26,57 @@ use crate::{ update::{add_update, make_update}, }, resource, - state::{action_states, db_client, server_status_cache, State}, + state::{action_states, db_client, server_status_cache}, }; -impl Resolve for State { - #[instrument(name = "CreateDeployment", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateDeployment { + #[instrument(name = "CreateDeployment", skip(user))] async fn resolve( - &self, - CreateDeployment { name, config }: CreateDeployment, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "CopyDeployment", skip(self, user))] +impl Resolve for CopyDeployment { + #[instrument(name = "CopyDeployment", skip(user))] async fn resolve( - &self, - CopyDeployment { name, id }: CopyDeployment, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Deployment { config, .. } = resource::get_check_permissions::( - &id, - &user, + &self.id, + user, PermissionLevel::Write, ) .await?; - resource::create::(&name, config.into(), &user).await + Ok( + resource::create::( + &self.name, + config.into(), + &user, + ) + .await?, + ) } } -impl Resolve for State { - #[instrument( - name = "CreateDeploymentFromContainer", - skip(self, user) - )] +impl Resolve for CreateDeploymentFromContainer { + #[instrument(name = "CreateDeploymentFromContainer", skip(user))] async fn resolve( - &self, - CreateDeploymentFromContainer { name, server }: CreateDeploymentFromContainer, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Write, ) .await?; @@ -79,13 +84,18 @@ impl Resolve for State { .get_or_insert_default(&server.id) .await; if cache.state != ServerState::Ok { - return Err(anyhow!( - "Cannot inspect container: server is {:?}", - cache.state - )); + return Err( + anyhow!( + "Cannot inspect container: server is {:?}", + cache.state + ) + .into(), + ); } let container = periphery_client(&server)? - .request(InspectContainer { name: name.clone() }) + .request(InspectContainer { + name: self.name.clone(), + }) .await .context("Failed to inspect container")?; @@ -146,42 +156,45 @@ impl Resolve for State { }); } - resource::create::(&name, config, &user).await + Ok( + resource::create::(&self.name, config, &user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteDeployment", skip(self, user))] +impl Resolve for DeleteDeployment { + #[instrument(name = "DeleteDeployment", skip(args))] async fn resolve( - &self, - DeleteDeployment { id }: DeleteDeployment, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + args: &WriteArgs, + ) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateDeployment", skip(self, user))] +impl Resolve for UpdateDeployment { + #[instrument(name = "UpdateDeployment", skip(user))] async fn resolve( - &self, - UpdateDeployment { id, config }: UpdateDeployment, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::update::(&self.id, self.config, &user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RenameDeployment", skip(self, user))] +impl Resolve for RenameDeployment { + #[instrument(name = "RenameDeployment", skip(user))] async fn resolve( - &self, - RenameDeployment { id, name }: RenameDeployment, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let deployment = resource::get_check_permissions::( - &id, - &user, + &self.id, + user, PermissionLevel::Write, ) .await?; @@ -197,14 +210,17 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.renaming = true)?; - let name = to_komodo_name(&name); + let name = to_komodo_name(&self.name); let container_state = get_deployment_state(&deployment).await?; if container_state == DeploymentState::Unknown { - return Err(anyhow!( - "Cannot rename Deployment when container status is unknown" - )); + return Err( + anyhow!( + "Cannot rename Deployment when container status is unknown" + ) + .into(), + ); } let mut update = diff --git a/bin/core/src/api/write/description.rs b/bin/core/src/api/write/description.rs index 2740e722b..93d3b7df6 100644 --- a/bin/core/src/api/write/description.rs +++ b/bin/core/src/api/write/description.rs @@ -5,33 +5,34 @@ use komodo_client::{ action::Action, alerter::Alerter, build::Build, builder::Builder, deployment::Deployment, procedure::Procedure, repo::Repo, server::Server, server_template::ServerTemplate, stack::Stack, - sync::ResourceSync, user::User, ResourceTarget, + sync::ResourceSync, ResourceTarget, }, }; use resolver_api::Resolve; -use crate::{resource, state::State}; +use crate::resource; -impl Resolve for State { - #[instrument(name = "UpdateDescription", skip(self, user))] +use super::WriteArgs; + +impl Resolve for UpdateDescription { + #[instrument(name = "UpdateDescription", skip(user))] async fn resolve( - &self, - UpdateDescription { - target, - description, - }: UpdateDescription, - user: User, - ) -> anyhow::Result { - match target { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + match self.target { ResourceTarget::System(_) => { - return Err(anyhow!( - "cannot update description of System resource target" - )) + return Err( + anyhow!( + "cannot update description of System resource target" + ) + .into(), + ) } ResourceTarget::Server(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -39,7 +40,7 @@ impl Resolve for State { ResourceTarget::Deployment(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -47,7 +48,7 @@ impl Resolve for State { ResourceTarget::Build(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -55,7 +56,7 @@ impl Resolve for State { ResourceTarget::Repo(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -63,7 +64,7 @@ impl Resolve for State { ResourceTarget::Builder(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -71,7 +72,7 @@ impl Resolve for State { ResourceTarget::Alerter(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -79,7 +80,7 @@ impl Resolve for State { ResourceTarget::Procedure(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -87,7 +88,7 @@ impl Resolve for State { ResourceTarget::Action(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -95,7 +96,7 @@ impl Resolve for State { ResourceTarget::ServerTemplate(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -103,7 +104,7 @@ impl Resolve for State { ResourceTarget::ResourceSync(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; @@ -111,7 +112,7 @@ impl Resolve for State { ResourceTarget::Stack(id) => { resource::update_description::( &id, - &description, + &self.description, &user, ) .await?; diff --git a/bin/core/src/api/write/mod.rs b/bin/core/src/api/write/mod.rs index f2d14fce1..4acb72e1c 100644 --- a/bin/core/src/api/write/mod.rs +++ b/bin/core/src/api/write/mod.rs @@ -1,17 +1,17 @@ use std::time::Instant; -use anyhow::{anyhow, Context}; +use anyhow::Context; use axum::{middleware, routing::post, Extension, Router}; -use axum_extra::{headers::ContentType, TypedHeader}; use derive_variants::{EnumVariants, ExtractVariant}; use komodo_client::{api::write::*, entities::user::User}; -use resolver_api::{derive::Resolver, Resolver}; +use resolver_api::Resolve; +use response::Response; use serde::{Deserialize, Serialize}; use serror::Json; use typeshare::typeshare; use uuid::Uuid; -use crate::{auth::auth_request, state::State}; +use crate::auth::auth_request; mod action; mod alerter; @@ -33,13 +33,18 @@ mod user; mod user_group; mod variable; +pub struct WriteArgs { + pub user: User, +} + #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Resolver, EnumVariants, + Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants, )] #[variant_derive(Debug)] -#[resolver_target(State)] -#[resolver_args(User)] +#[args(WriteArgs)] +#[response(Response)] +#[error(serror::Error)] #[serde(tag = "type", content = "params")] pub enum WriteRequest { // ==== USER ==== @@ -194,7 +199,7 @@ pub fn router() -> Router { async fn handler( Extension(user): Extension, Json(request): Json, -) -> serror::Result<(TypedHeader, String)> { +) -> serror::Result { let req_id = Uuid::new_v4(); let res = tokio::spawn(task(req_id, request, user)) @@ -205,7 +210,7 @@ async fn handler( warn!("/write request {req_id} spawn error: {e:#}"); } - Ok((TypedHeader(ContentType::json()), res??)) + res? } #[instrument( @@ -220,28 +225,19 @@ async fn task( req_id: Uuid, request: WriteRequest, user: User, -) -> anyhow::Result { +) -> serror::Result { info!("/write request | user: {}", user.username); let timer = Instant::now(); - let res = - State - .resolve_request(request, user) - .await - .map_err(|e| match e { - resolver_api::Error::Serialization(e) => { - anyhow!("{e:?}").context("response serialization error") - } - resolver_api::Error::Inner(e) => e, - }); + let res = request.resolve(&WriteArgs { user }).await; if let Err(e) = &res { - warn!("/write request {req_id} error: {e:#}"); + warn!("/write request {req_id} error: {:#}", e.error); } let elapsed = timer.elapsed(); debug!("/write request {req_id} | resolve time: {elapsed:?}"); - res + res.map(|res| res.0) } diff --git a/bin/core/src/api/write/permissions.rs b/bin/core/src/api/write/permissions.rs index 65e96655b..632e3ab59 100644 --- a/bin/core/src/api/write/permissions.rs +++ b/bin/core/src/api/write/permissions.rs @@ -2,16 +2,9 @@ use std::str::FromStr; use anyhow::{anyhow, Context}; use komodo_client::{ - api::write::{ - UpdatePermissionOnResourceType, - UpdatePermissionOnResourceTypeResponse, UpdatePermissionOnTarget, - UpdatePermissionOnTargetResponse, UpdateUserAdmin, - UpdateUserAdminResponse, UpdateUserBasePermissions, - UpdateUserBasePermissionsResponse, - }, + api::write::*, entities::{ permission::{UserTarget, UserTargetVariant}, - user::User, ResourceTarget, ResourceTargetVariant, }, }; @@ -24,37 +17,40 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::{ - helpers::query::get_user, - state::{db_client, State}, -}; +use crate::{helpers::query::get_user, state::db_client}; -impl Resolve for State { +use super::WriteArgs; + +impl Resolve for UpdateUserAdmin { + #[instrument(name = "UpdateUserAdmin", skip(super_admin))] async fn resolve( - &self, - UpdateUserAdmin { user_id, admin }: UpdateUserAdmin, - super_admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: super_admin }: &WriteArgs, + ) -> serror::Result { if !super_admin.super_admin { - return Err(anyhow!("Only super admins can call this method.")); + return Err( + anyhow!("Only super admins can call this method.").into(), + ); } - let user = find_one_by_id(&db_client().users, &user_id) + let user = find_one_by_id(&db_client().users, &self.user_id) .await .context("failed to query mongo for user")? .context("did not find user with given id")?; if !user.enabled { - return Err(anyhow!("User is disabled. Enable user first.")); + return Err( + anyhow!("User is disabled. Enable user first.").into(), + ); } if user.super_admin { - return Err(anyhow!("Cannot update other super admins")); + return Err(anyhow!("Cannot update other super admins").into()); } update_one_by_id( &db_client().users, - &user_id, - doc! { "$set": { "admin": admin } }, + &self.user_id, + doc! { "$set": { "admin": self.admin } }, None, ) .await?; @@ -63,20 +59,21 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "UpdateUserBasePermissions", skip(self, admin))] +impl Resolve for UpdateUserBasePermissions { + #[instrument(name = "UpdateUserBasePermissions", skip(admin))] async fn resolve( - &self, - UpdateUserBasePermissions { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { + let UpdateUserBasePermissions { user_id, enabled, create_servers, create_builds, - }: UpdateUserBasePermissions, - admin: User, - ) -> anyhow::Result { + } = self; + if !admin.admin { - return Err(anyhow!("this method is admin only")); + return Err(anyhow!("this method is admin only").into()); } let user = find_one_by_id(&db_client().users, &user_id) @@ -84,14 +81,17 @@ impl Resolve for State { .context("failed to query mongo for user")? .context("did not find user with given id")?; if user.super_admin { - return Err(anyhow!( - "Cannot use this method to update super admins permissions" - )); + return Err( + anyhow!( + "Cannot use this method to update super admins permissions" + ) + .into(), + ); } if user.admin && !admin.super_admin { return Err(anyhow!( "Only super admins can use this method to update other admins permissions" - )); + ).into()); } let mut update_doc = Document::new(); if let Some(enabled) = enabled { @@ -116,34 +116,35 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument( - name = "UpdatePermissionOnResourceType", - skip(self, admin) - )] +impl Resolve for UpdatePermissionOnResourceType { + #[instrument(name = "UpdatePermissionOnResourceType", skip(admin))] async fn resolve( - &self, - UpdatePermissionOnResourceType { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { + let UpdatePermissionOnResourceType { user_target, resource_type, permission, - }: UpdatePermissionOnResourceType, - admin: User, - ) -> anyhow::Result { + } = self; + if !admin.admin { - return Err(anyhow!("this method is admin only")); + return Err(anyhow!("this method is admin only").into()); } // Some extra checks if user target is an actual User if let UserTarget::User(user_id) = &user_target { let user = get_user(user_id).await?; if user.admin { - return Err(anyhow!( + return Err( + anyhow!( "cannot use this method to update other admins permissions" - )); + ) + .into(), + ); } if !user.enabled { - return Err(anyhow!("user not enabled")); + return Err(anyhow!("user not enabled").into()); } } @@ -181,31 +182,35 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "UpdatePermissionOnTarget", skip(self, admin))] +impl Resolve for UpdatePermissionOnTarget { + #[instrument(name = "UpdatePermissionOnTarget", skip(admin))] async fn resolve( - &self, - UpdatePermissionOnTarget { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { + let UpdatePermissionOnTarget { user_target, resource_target, permission, - }: UpdatePermissionOnTarget, - admin: User, - ) -> anyhow::Result { + } = self; + if !admin.admin { - return Err(anyhow!("this method is admin only")); + return Err(anyhow!("this method is admin only").into()); } // Some extra checks if user target is an actual User if let UserTarget::User(user_id) = &user_target { let user = get_user(user_id).await?; if user.admin { - return Err(anyhow!( + return Err( + anyhow!( "cannot use this method to update other admins permissions" - )); + ) + .into(), + ); } if !user.enabled { - return Err(anyhow!("user not enabled")); + return Err(anyhow!("user not enabled").into()); } } @@ -247,7 +252,7 @@ impl Resolve for State { /// 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, -) -> anyhow::Result<(UserTargetVariant, String)> { +) -> serror::Result<(UserTargetVariant, String)> { match user_target { UserTarget::User(ident) => { let filter = match ObjectId::from_str(ident) { @@ -283,7 +288,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, -) -> anyhow::Result<(ResourceTargetVariant, String)> { +) -> serror::Result<(ResourceTargetVariant, String)> { match resource_target { ResourceTarget::System(_) => { let res = resource_target.extract_variant_id(); diff --git a/bin/core/src/api/write/procedure.rs b/bin/core/src/api/write/procedure.rs index 393c6d820..c4745f2b5 100644 --- a/bin/core/src/api/write/procedure.rs +++ b/bin/core/src/api/write/procedure.rs @@ -1,72 +1,80 @@ use komodo_client::{ api::write::*, entities::{ - permission::PermissionLevel, procedure::Procedure, - update::Update, user::User, + permission::PermissionLevel, procedure::Procedure, update::Update, }, }; use resolver_api::Resolve; -use crate::{resource, state::State}; +use crate::resource; -impl Resolve for State { - #[instrument(name = "CreateProcedure", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateProcedure { + #[instrument(name = "CreateProcedure", skip(user))] async fn resolve( - &self, - CreateProcedure { name, config }: CreateProcedure, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "CopyProcedure", skip(self, user))] +impl Resolve for CopyProcedure { + #[instrument(name = "CopyProcedure", skip(user))] async fn resolve( - &self, - CopyProcedure { name, id }: CopyProcedure, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Procedure { config, .. } = resource::get_check_permissions::( - &id, + &self.id, &user, PermissionLevel::Write, ) .await?; - resource::create::(&name, config.into(), &user).await + Ok( + resource::create::(&self.name, config.into(), user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "UpdateProcedure", skip(self, user))] +impl Resolve for UpdateProcedure { + #[instrument(name = "UpdateProcedure", skip(user))] async fn resolve( - &self, - UpdateProcedure { id, config }: UpdateProcedure, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::update::(&self.id, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RenameProcedure", skip(self, user))] +impl Resolve for RenameProcedure { + #[instrument(name = "RenameProcedure", skip(user))] async fn resolve( - &self, - RenameProcedure { id, name }: RenameProcedure, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::rename::(&self.id, &self.name, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteProcedure", skip(self, user))] +impl Resolve for DeleteProcedure { + #[instrument(name = "DeleteProcedure", skip(args))] async fn resolve( - &self, - DeleteProcedure { id }: DeleteProcedure, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + args: &WriteArgs, + ) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } diff --git a/bin/core/src/api/write/provider.rs b/bin/core/src/api/write/provider.rs index 1b5f20f19..f92cc6450 100644 --- a/bin/core/src/api/write/provider.rs +++ b/bin/core/src/api/write/provider.rs @@ -3,7 +3,6 @@ use komodo_client::{ api::write::*, entities::{ provider::{DockerRegistryAccount, GitProviderAccount}, - user::User, Operation, ResourceTarget, }, }; @@ -15,29 +14,31 @@ use resolver_api::Resolve; use crate::{ helpers::update::{add_update, make_update}, - state::{db_client, State}, + state::db_client, }; -impl Resolve for State { +use super::WriteArgs; + +impl Resolve for CreateGitProviderAccount { async fn resolve( - &self, - CreateGitProviderAccount { account }: CreateGitProviderAccount, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "only admins can create git provider accounts" - )); + return Err( + anyhow!("only admins can create git provider accounts") + .into(), + ); } - let mut account: GitProviderAccount = account.into(); + let mut account: GitProviderAccount = self.account.into(); if account.domain.is_empty() { - return Err(anyhow!("domain cannot be empty string.")); + return Err(anyhow!("domain cannot be empty string.").into()); } if account.username.is_empty() { - return Err(anyhow!("username cannot be empty string.")); + return Err(anyhow!("username cannot be empty string.").into()); } let mut update = make_update( @@ -77,36 +78,38 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for UpdateGitProviderAccount { async fn resolve( - &self, - UpdateGitProviderAccount { id, mut account }: UpdateGitProviderAccount, - user: User, - ) -> anyhow::Result { + mut self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "only admins can update git provider accounts" - )); + return Err( + anyhow!("only admins can update git provider accounts") + .into(), + ); } - if let Some(domain) = &account.domain { + if let Some(domain) = &self.account.domain { if domain.is_empty() { - return Err(anyhow!( - "cannot update git provider with empty domain" - )); + return Err( + anyhow!("cannot update git provider with empty domain") + .into(), + ); } } - if let Some(username) = &account.username { + if let Some(username) = &self.account.username { if username.is_empty() { - return Err(anyhow!( - "cannot update git provider with empty username" - )); + return Err( + anyhow!("cannot update git provider with empty username") + .into(), + ); } } // Ensure update does not change id - account.id = None; + self.account.id = None; let mut update = make_update( ResourceTarget::system(), @@ -114,25 +117,24 @@ impl Resolve for State { &user, ); - let account = to_document(&account).context( + let account = to_document(&self.account).context( "failed to serialize partial git provider account to bson", )?; let db = db_client(); update_one_by_id( &db.git_accounts, - &id, + &self.id, doc! { "$set": account }, None, ) .await .context("failed to update git provider account on db")?; - let Some(account) = - find_one_by_id(&db.git_accounts, &id) - .await - .context("failed to query db for git accounts")? + let Some(account) = find_one_by_id(&db.git_accounts, &self.id) + .await + .context("failed to query db for git accounts")? else { - return Err(anyhow!("no account found with given id")); + return Err(anyhow!("no account found with given id").into()); }; update.push_simple_log( @@ -156,16 +158,16 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for DeleteGitProviderAccount { async fn resolve( - &self, - DeleteGitProviderAccount { id }: DeleteGitProviderAccount, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "only admins can delete git provider accounts" - )); + return Err( + anyhow!("only admins can delete git provider accounts") + .into(), + ); } let mut update = make_update( @@ -175,14 +177,13 @@ impl Resolve for State { ); let db = db_client(); - let Some(account) = - find_one_by_id(&db.git_accounts, &id) - .await - .context("failed to query db for git accounts")? + let Some(account) = find_one_by_id(&db.git_accounts, &self.id) + .await + .context("failed to query db for git accounts")? else { - return Err(anyhow!("no account found with given id")); + return Err(anyhow!("no account found with given id").into()); }; - delete_one_by_id(&db.git_accounts, &id, None) + delete_one_by_id(&db.git_accounts, &self.id, None) .await .context("failed to delete git account on db")?; @@ -207,26 +208,28 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for CreateDockerRegistryAccount { async fn resolve( - &self, - CreateDockerRegistryAccount { account }: CreateDockerRegistryAccount, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "only admins can create docker registry account accounts" - )); + return Err( + anyhow!( + "only admins can create docker registry account accounts" + ) + .into(), + ); } - let mut account: DockerRegistryAccount = account.into(); + let mut account: DockerRegistryAccount = self.account.into(); if account.domain.is_empty() { - return Err(anyhow!("domain cannot be empty string.")); + return Err(anyhow!("domain cannot be empty string.").into()); } if account.username.is_empty() { - return Err(anyhow!("username cannot be empty string.")); + return Err(anyhow!("username cannot be empty string.").into()); } let mut update = make_update( @@ -268,35 +271,41 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for UpdateDockerRegistryAccount { async fn resolve( - &self, - UpdateDockerRegistryAccount { id, mut account }: UpdateDockerRegistryAccount, - user: User, - ) -> anyhow::Result { + mut self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "only admins can update docker registry accounts" - )); + return Err( + anyhow!("only admins can update docker registry accounts") + .into(), + ); } - if let Some(domain) = &account.domain { + if let Some(domain) = &self.account.domain { if domain.is_empty() { - return Err(anyhow!( - "cannot update docker registry account with empty domain" - )); + return Err( + anyhow!( + "cannot update docker registry account with empty domain" + ) + .into(), + ); } } - if let Some(username) = &account.username { + if let Some(username) = &self.account.username { if username.is_empty() { - return Err(anyhow!( + return Err( + anyhow!( "cannot update docker registry account with empty username" - )); + ) + .into(), + ); } } - account.id = None; + self.account.id = None; let mut update = make_update( ResourceTarget::system(), @@ -304,14 +313,14 @@ impl Resolve for State { &user, ); - let account = to_document(&account).context( + let account = to_document(&self.account).context( "failed to serialize partial docker registry account account to bson", )?; let db = db_client(); update_one_by_id( &db.registry_accounts, - &id, + &self.id, doc! { "$set": account }, None, ) @@ -320,11 +329,12 @@ impl Resolve for State { "failed to update docker registry account account on db", )?; - let Some(account) = find_one_by_id(&db.registry_accounts, &id) - .await - .context("failed to query db for registry accounts")? + let Some(account) = + find_one_by_id(&db.registry_accounts, &self.id) + .await + .context("failed to query db for registry accounts")? else { - return Err(anyhow!("no account found with given id")); + return Err(anyhow!("no account found with given id").into()); }; update.push_simple_log( @@ -348,16 +358,16 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for DeleteDockerRegistryAccount { async fn resolve( - &self, - DeleteDockerRegistryAccount { id }: DeleteDockerRegistryAccount, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!( - "only admins can delete docker registry accounts" - )); + return Err( + anyhow!("only admins can delete docker registry accounts") + .into(), + ); } let mut update = make_update( @@ -367,13 +377,14 @@ impl Resolve for State { ); let db = db_client(); - let Some(account) = find_one_by_id(&db.registry_accounts, &id) - .await - .context("failed to query db for git accounts")? + let Some(account) = + find_one_by_id(&db.registry_accounts, &self.id) + .await + .context("failed to query db for git accounts")? else { - return Err(anyhow!("no account found with given id")); + return Err(anyhow!("no account found with given id").into()); }; - delete_one_by_id(&db.registry_accounts, &id, None) + delete_one_by_id(&db.registry_accounts, &self.id, None) .await .context("failed to delete registry account on db")?; diff --git a/bin/core/src/api/write/repo.rs b/bin/core/src/api/write/repo.rs index 11def0dc9..2189380a0 100644 --- a/bin/core/src/api/write/repo.rs +++ b/bin/core/src/api/write/repo.rs @@ -11,7 +11,6 @@ use komodo_client::{ server::Server, to_komodo_name, update::{Log, Update}, - user::User, CloneArgs, NoData, Operation, }, }; @@ -30,69 +29,66 @@ use crate::{ update::{add_update, make_update}, }, resource, - state::{action_states, db_client, github_client, State}, + state::{action_states, db_client, github_client}, }; -impl Resolve for State { - #[instrument(name = "CreateRepo", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateRepo { + #[instrument(name = "CreateRepo", skip(user))] async fn resolve( - &self, - CreateRepo { name, config }: CreateRepo, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::create::(&self.name, self.config, user).await?) } } -impl Resolve for State { - #[instrument(name = "CopyRepo", skip(self, user))] +impl Resolve for CopyRepo { + #[instrument(name = "CopyRepo", skip(user))] async fn resolve( - &self, - CopyRepo { name, id }: CopyRepo, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Repo { config, .. } = resource::get_check_permissions::( - &id, + &self.id, &user, PermissionLevel::Write, ) .await?; - resource::create::(&name, config.into(), &user).await + Ok( + resource::create::(&self.name, config.into(), &user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteRepo", skip(self, user))] - async fn resolve( - &self, - DeleteRepo { id }: DeleteRepo, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await +impl Resolve for DeleteRepo { + #[instrument(name = "DeleteRepo", skip(args))] + async fn resolve(self, args: &WriteArgs) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateRepo", skip(self, user))] +impl Resolve for UpdateRepo { + #[instrument(name = "UpdateRepo", skip(user))] async fn resolve( - &self, - UpdateRepo { id, config }: UpdateRepo, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::update::(&self.id, self.config, user).await?) } } -impl Resolve for State { - #[instrument(name = "RenameRepo", skip(self, user))] +impl Resolve for RenameRepo { + #[instrument(name = "RenameRepo", skip(user))] async fn resolve( - &self, - RenameRepo { id, name }: RenameRepo, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let repo = resource::get_check_permissions::( - &id, + &self.id, &user, PermissionLevel::Write, ) @@ -101,7 +97,9 @@ impl Resolve for State { if repo.config.server_id.is_empty() || !repo.config.path.is_empty() { - return resource::rename::(&repo.id, &name, &user).await; + return Ok( + resource::rename::(&repo.id, &self.name, &user).await?, + ); } // get the action state for the repo (or insert default). @@ -113,7 +111,7 @@ impl Resolve for State { let _action_guard = action_state.update(|state| state.renaming = true)?; - let name = to_komodo_name(&name); + let name = to_komodo_name(&self.name); let mut update = make_update(&repo, Operation::RenameRepo, &user); @@ -159,21 +157,20 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for RefreshRepoCache { #[instrument( name = "RefreshRepoCache", level = "debug", - skip(self, user) + skip(user) )] async fn resolve( - &self, - RefreshRepoCache { repo }: RefreshRepoCache, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { // 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 = resource::get_check_permissions::( - &repo, + &self.repo, &user, PermissionLevel::Execute, ) @@ -245,39 +242,42 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "CreateRepoWebhook", skip(self, user))] +impl Resolve for CreateRepoWebhook { + #[instrument(name = "CreateRepoWebhook", skip(args))] async fn resolve( - &self, - CreateRepoWebhook { repo, action }: CreateRepoWebhook, - user: User, - ) -> anyhow::Result { + self, + args: &WriteArgs, + ) -> serror::Result { let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; let repo = resource::get_check_permissions::( - &repo, - &user, + &self.repo, + &args.user, PermissionLevel::Write, ) .await?; if repo.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't create webhook" - )); + return Err( + anyhow!("No repo configured, can't create webhook").into(), + ); } let mut split = repo.config.repo.split('/'); let owner = split.next().context("Repo repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo_name = @@ -310,7 +310,7 @@ impl Resolve for State { } else { webhook_base_url }; - let url = match action { + let url = match self.action { RepoWebhookAction::Clone => { format!("{host}/listener/github/repo/{}/clone", repo.id) } @@ -348,64 +348,65 @@ impl Resolve for State { .context("failed to create webhook")?; if !repo.config.webhook_enabled { - self - .resolve( - UpdateRepo { - id: repo.id, - config: PartialRepoConfig { - webhook_enabled: Some(true), - ..Default::default() - }, - }, - user, - ) - .await - .context("failed to update repo to enable webhook")?; + UpdateRepo { + id: repo.id, + config: PartialRepoConfig { + webhook_enabled: Some(true), + ..Default::default() + }, + } + .resolve(args) + .await + .map_err(|e| e.error) + .context("failed to update repo to enable webhook")?; } Ok(NoData {}) } } -impl Resolve for State { - #[instrument(name = "DeleteRepoWebhook", skip(self, user))] +impl Resolve for DeleteRepoWebhook { + #[instrument(name = "DeleteRepoWebhook", skip(user))] async fn resolve( - &self, - DeleteRepoWebhook { repo, action }: DeleteRepoWebhook, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; let repo = resource::get_check_permissions::( - &repo, - &user, + &self.repo, + user, PermissionLevel::Write, ) .await?; if repo.config.git_provider != "github.com" { - return Err(anyhow!( - "Can only manage github.com repo webhooks" - )); + return Err( + anyhow!("Can only manage github.com repo webhooks").into(), + ); } if repo.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't create webhook" - )); + return Err( + anyhow!("No repo configured, can't create webhook").into(), + ); } let mut split = repo.config.repo.split('/'); let owner = split.next().context("Repo repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo_name = @@ -431,7 +432,7 @@ impl Resolve for State { } else { webhook_base_url }; - let url = match action { + let url = match self.action { RepoWebhookAction::Clone => { format!("{host}/listener/github/repo/{}/clone", repo.id) } diff --git a/bin/core/src/api/write/server.rs b/bin/core/src/api/write/server.rs index d9a60abc7..da9a91d73 100644 --- a/bin/core/src/api/write/server.rs +++ b/bin/core/src/api/write/server.rs @@ -5,7 +5,6 @@ use komodo_client::{ permission::PermissionLevel, server::Server, update::{Update, UpdateStatus}, - user::User, Operation, }, }; @@ -18,63 +17,59 @@ use crate::{ update::{add_update, make_update, update_update}, }, resource, - state::State, }; -impl Resolve for State { - #[instrument(name = "CreateServer", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateServer { + #[instrument(name = "CreateServer", skip(user))] async fn resolve( - &self, - CreateServer { name, config }: CreateServer, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteServer", skip(self, user))] - async fn resolve( - &self, - DeleteServer { id }: DeleteServer, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await +impl Resolve for DeleteServer { + #[instrument(name = "DeleteServer", skip(args))] + async fn resolve(self, args: &WriteArgs) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateServer", skip(self, user))] +impl Resolve for UpdateServer { + #[instrument(name = "UpdateServer", skip(user))] async fn resolve( - &self, - UpdateServer { id, config }: UpdateServer, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::update::(&self.id, self.config, user).await?) } } -impl Resolve for State { - #[instrument(name = "RenameServer", skip(self, user))] +impl Resolve for RenameServer { + #[instrument(name = "RenameServer", skip(user))] async fn resolve( - &self, - RenameServer { id, name }: RenameServer, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::rename::(&self.id, &self.name, user).await?) } } -impl Resolve for State { - #[instrument(name = "CreateNetwork", skip(self, user))] +impl Resolve for CreateNetwork { + #[instrument(name = "CreateNetwork", skip(user))] async fn resolve( - &self, - CreateNetwork { server, name }: CreateNetwork, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let server = resource::get_check_permissions::( - &server, - &user, + &self.server, + user, PermissionLevel::Write, ) .await?; @@ -87,7 +82,10 @@ impl Resolve for State { update.id = add_update(update.clone()).await?; match periphery - .request(api::network::CreateNetwork { name, driver: None }) + .request(api::network::CreateNetwork { + name: self.name, + driver: None, + }) .await { Ok(log) => update.logs.push(log), diff --git a/bin/core/src/api/write/server_template.rs b/bin/core/src/api/write/server_template.rs index 0a873349d..9d8118787 100644 --- a/bin/core/src/api/write/server_template.rs +++ b/bin/core/src/api/write/server_template.rs @@ -5,72 +5,88 @@ use komodo_client::{ }, entities::{ permission::PermissionLevel, server_template::ServerTemplate, - update::Update, user::User, + update::Update, }, }; use resolver_api::Resolve; -use crate::{resource, state::State}; +use crate::resource; -impl Resolve for State { - #[instrument(name = "CreateServerTemplate", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateServerTemplate { + #[instrument(name = "CreateServerTemplate", skip(user))] async fn resolve( - &self, - CreateServerTemplate { name, config }: CreateServerTemplate, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::( + &self.name, + self.config, + &user, + ) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "CopyServerTemplate", skip(self, user))] +impl Resolve for CopyServerTemplate { + #[instrument(name = "CopyServerTemplate", skip(user))] async fn resolve( - &self, - CopyServerTemplate { name, id }: CopyServerTemplate, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let ServerTemplate { config, .. } = resource::get_check_permissions::( - &id, + &self.id, &user, PermissionLevel::Write, ) .await?; - resource::create::(&name, config.into(), &user) - .await + Ok( + resource::create::( + &self.name, + config.into(), + &user, + ) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteServerTemplate", skip(self, user))] +impl Resolve for DeleteServerTemplate { + #[instrument(name = "DeleteServerTemplate", skip(args))] async fn resolve( - &self, - DeleteServerTemplate { id }: DeleteServerTemplate, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + args: &WriteArgs, + ) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateServerTemplate", skip(self, user))] +impl Resolve for UpdateServerTemplate { + #[instrument(name = "UpdateServerTemplate", skip(user))] async fn resolve( - &self, - UpdateServerTemplate { id, config }: UpdateServerTemplate, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::update::(&self.id, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RenameServerTemplate", skip(self, user))] +impl Resolve for RenameServerTemplate { + #[instrument(name = "RenameServerTemplate", skip(user))] async fn resolve( - &self, - RenameServerTemplate { id, name }: RenameServerTemplate, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::rename::(&self.id, &self.name, user) + .await?, + ) } } diff --git a/bin/core/src/api/write/service_user.rs b/bin/core/src/api/write/service_user.rs index e23884656..964250089 100644 --- a/bin/core/src/api/write/service_user.rs +++ b/bin/core/src/api/write/service_user.rs @@ -2,16 +2,7 @@ use std::str::FromStr; use anyhow::{anyhow, Context}; use komodo_client::{ - api::{ - user::CreateApiKey, - write::{ - CreateApiKeyForServiceUser, CreateApiKeyForServiceUserResponse, - CreateServiceUser, CreateServiceUserResponse, - DeleteApiKeyForServiceUser, DeleteApiKeyForServiceUserResponse, - UpdateServiceUserDescription, - UpdateServiceUserDescriptionResponse, - }, - }, + api::{user::CreateApiKey, write::*}, entities::{ komodo_timestamp, user::{User, UserConfig}, @@ -23,28 +14,30 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::state::{db_client, State}; +use crate::{api::user::UserArgs, state::db_client}; -impl Resolve for State { - #[instrument(name = "CreateServiceUser", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateServiceUser { + #[instrument(name = "CreateServiceUser", skip(user))] async fn resolve( - &self, - CreateServiceUser { - username, - description, - }: CreateServiceUser, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("user not admin")); + return Err(anyhow!("user not admin").into()); } - if ObjectId::from_str(&username).is_ok() { - return Err(anyhow!("username cannot be valid ObjectId")); + if ObjectId::from_str(&self.username).is_ok() { + return Err( + anyhow!("username cannot be valid ObjectId").into(), + ); } - let config = UserConfig::Service { description }; + let config = UserConfig::Service { + description: self.description, + }; let mut user = User { id: Default::default(), - username, + username: self.username, config, enabled: true, admin: false, @@ -69,88 +62,81 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument( - name = "UpdateServiceUserDescription", - skip(self, user) - )] +impl Resolve for UpdateServiceUserDescription { + #[instrument(name = "UpdateServiceUserDescription", skip(user))] async fn resolve( - &self, - UpdateServiceUserDescription { - username, - description, - }: UpdateServiceUserDescription, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("user not admin")); + return Err(anyhow!("user not admin").into()); } let db = db_client(); let service_user = db .users - .find_one(doc! { "username": &username }) + .find_one(doc! { "username": &self.username }) .await .context("failed to query db for user")? .context("no user with given username")?; let UserConfig::Service { .. } = &service_user.config else { - return Err(anyhow!("user is not service user")); + return Err(anyhow!("user is not service user").into()); }; db.users .update_one( - doc! { "username": &username }, - doc! { "$set": { "config.data.description": description } }, + doc! { "username": &self.username }, + doc! { "$set": { "config.data.description": self.description } }, ) .await .context("failed to update user on db")?; - db.users - .find_one(doc! { "username": &username }) + let res = db + .users + .find_one(doc! { "username": &self.username }) .await .context("failed to query db for user")? - .context("user with username not found") + .context("user with username not found")?; + Ok(res) } } -impl Resolve for State { - #[instrument(name = "CreateApiKeyForServiceUser", skip(self, user))] +impl Resolve for CreateApiKeyForServiceUser { + #[instrument(name = "CreateApiKeyForServiceUser", skip(user))] async fn resolve( - &self, - CreateApiKeyForServiceUser { - user_id, - name, - expires, - }: CreateApiKeyForServiceUser, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("user not admin")); + return Err(anyhow!("user not admin").into()); } - let service_user = find_one_by_id(&db_client().users, &user_id) - .await - .context("failed to query db for user")? - .context("no user found with id")?; + let service_user = + find_one_by_id(&db_client().users, &self.user_id) + .await + .context("failed to query db for user")? + .context("no user found with id")?; let UserConfig::Service { .. } = &service_user.config else { - return Err(anyhow!("user is not service user")); + return Err(anyhow!("user is not service user").into()); }; - self - .resolve(CreateApiKey { name, expires }, service_user) - .await + CreateApiKey { + name: self.name, + expires: self.expires, + } + .resolve(&UserArgs { user: service_user }) + .await } } -impl Resolve for State { - #[instrument(name = "DeleteApiKeyForServiceUser", skip(self, user))] +impl Resolve for DeleteApiKeyForServiceUser { + #[instrument(name = "DeleteApiKeyForServiceUser", skip(user))] async fn resolve( - &self, - DeleteApiKeyForServiceUser { key }: DeleteApiKeyForServiceUser, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("user not admin")); + return Err(anyhow!("user not admin").into()); } let db = db_client(); let api_key = db .api_keys - .find_one(doc! { "key": &key }) + .find_one(doc! { "key": &self.key }) .await .context("failed to query db for api key")? .context("did not find matching api key")?; @@ -160,10 +146,10 @@ impl Resolve for State { .context("failed to query db for user")? .context("no user found with id")?; let UserConfig::Service { .. } = &service_user.config else { - return Err(anyhow!("user is not service user")); + return Err(anyhow!("user is not service user").into()); }; db.api_keys - .delete_one(doc! { "key": key }) + .delete_one(doc! { "key": self.key }) .await .context("failed to delete api key on db")?; Ok(DeleteApiKeyForServiceUserResponse {}) diff --git a/bin/core/src/api/write/stack.rs b/bin/core/src/api/write/stack.rs index 4c14764b0..74401f92d 100644 --- a/bin/core/src/api/write/stack.rs +++ b/bin/core/src/api/write/stack.rs @@ -8,7 +8,7 @@ use komodo_client::{ server::ServerState, stack::{PartialStackConfig, Stack, StackInfo}, update::Update, - user::{stack_user, User}, + user::stack_user, FileContents, NoData, Operation, }, }; @@ -36,81 +36,81 @@ use crate::{ remote::{get_repo_compose_contents, RemoteComposeContents}, services::extract_services_into_res, }, - state::{db_client, github_client, State}, + state::{db_client, github_client}, }; -impl Resolve for State { - #[instrument(name = "CreateStack", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateStack { + #[instrument(name = "CreateStack", skip(user))] async fn resolve( - &self, - CreateStack { name, config }: CreateStack, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::(&self.name, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "CopyStack", skip(self, user))] +impl Resolve for CopyStack { + #[instrument(name = "CopyStack", skip(user))] async fn resolve( - &self, - CopyStack { name, id }: CopyStack, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Stack { config, .. } = resource::get_check_permissions::( - &id, - &user, + &self.id, + user, PermissionLevel::Write, ) .await?; - resource::create::(&name, config.into(), &user).await + Ok( + resource::create::(&self.name, config.into(), user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteStack", skip(self, user))] - async fn resolve( - &self, - DeleteStack { id }: DeleteStack, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await +impl Resolve for DeleteStack { + #[instrument(name = "DeleteStack", skip(args))] + async fn resolve(self, args: &WriteArgs) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateStack", skip(self, user))] +impl Resolve for UpdateStack { + #[instrument(name = "UpdateStack", skip(user))] async fn resolve( - &self, - UpdateStack { id, config }: UpdateStack, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::update::(&self.id, self.config, user).await?) } } -impl Resolve for State { - #[instrument(name = "RenameStack", skip(self, user))] +impl Resolve for RenameStack { + #[instrument(name = "RenameStack", skip(user))] async fn resolve( - &self, - RenameStack { id, name }: RenameStack, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok(resource::rename::(&self.id, &self.name, user).await?) } } -impl Resolve for State { +impl Resolve for WriteStackFileContents { async fn resolve( - &self, - WriteStackFileContents { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + let WriteStackFileContents { stack, file_path, contents, - }: WriteStackFileContents, - user: User, - ) -> anyhow::Result { + } = self; let (mut stack, server) = get_stack_and_server( &stack, &user, @@ -122,7 +122,7 @@ impl Resolve for State { if !stack.config.files_on_host && stack.config.repo.is_empty() { return Err(anyhow!( "Stack is not configured to use Files on Host or Git Repo, can't write file contents" - )); + ).into()); } let mut update = @@ -173,7 +173,7 @@ impl Resolve for State { match periphery_client(&server)? .request(WriteCommitComposeContents { stack, - username: Some(user.username), + username: Some(user.username.clone()), file_path, contents, git_token, @@ -193,12 +193,12 @@ impl Resolve for State { }; } - if let Err(e) = State - .resolve( - RefreshStackCache { stack: stack_id }, - stack_user().to_owned(), - ) + if let Err(e) = (RefreshStackCache { stack: stack_id }) + .resolve(&WriteArgs { + user: stack_user().to_owned(), + }) .await + .map_err(|e| e.error) .context( "Failed to refresh stack cache after writing file contents", ) @@ -216,22 +216,21 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for RefreshStackCache { #[instrument( name = "RefreshStackCache", level = "debug", - skip(self, user) + skip(user) )] async fn resolve( - &self, - RefreshStackCache { stack }: RefreshStackCache, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { // Even though this is a write request, this doesn't change any config. Anyone that can execute the // stack should be able to do this. let stack = resource::get_check_permissions::( - &stack, - &user, + &self.stack, + user, PermissionLevel::Execute, ) .await?; @@ -300,9 +299,9 @@ impl Resolve for State { &mut services, ) { warn!( - "failed to extract stack services, things won't works correctly. stack: {} | {e:#}", - stack.name - ); + "failed to extract stack services, things won't works correctly. stack: {} | {e:#}", + stack.name + ); } } @@ -414,39 +413,44 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "CreateStackWebhook", skip(self, user))] +impl Resolve for CreateStackWebhook { + #[instrument(name = "CreateStackWebhook", skip(args))] async fn resolve( - &self, - CreateStackWebhook { stack, action }: CreateStackWebhook, - user: User, - ) -> anyhow::Result { + self, + args: &WriteArgs, + ) -> serror::Result { + let WriteArgs { user } = args; + let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; let stack = resource::get_check_permissions::( - &stack, - &user, + &self.stack, + user, PermissionLevel::Write, ) .await?; if stack.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't create webhook" - )); + return Err( + anyhow!("No repo configured, can't create webhook").into(), + ); } let mut split = stack.config.repo.split('/'); let owner = split.next().context("Stack repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo = @@ -479,7 +483,7 @@ impl Resolve for State { } else { webhook_base_url }; - let url = match action { + let url = match self.action { StackWebhookAction::Refresh => { format!("{host}/listener/github/stack/{}/refresh", stack.id) } @@ -514,64 +518,65 @@ impl Resolve for State { .context("failed to create webhook")?; if !stack.config.webhook_enabled { - self - .resolve( - UpdateStack { - id: stack.id, - config: PartialStackConfig { - webhook_enabled: Some(true), - ..Default::default() - }, - }, - user, - ) - .await - .context("failed to update stack to enable webhook")?; + UpdateStack { + id: stack.id, + config: PartialStackConfig { + webhook_enabled: Some(true), + ..Default::default() + }, + } + .resolve(args) + .await + .map_err(|e| e.error) + .context("failed to update stack to enable webhook")?; } Ok(NoData {}) } } -impl Resolve for State { - #[instrument(name = "DeleteStackWebhook", skip(self, user))] +impl Resolve for DeleteStackWebhook { + #[instrument(name = "DeleteStackWebhook", skip(user))] async fn resolve( - &self, - DeleteStackWebhook { stack, action }: DeleteStackWebhook, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; let stack = resource::get_check_permissions::( - &stack, - &user, + &self.stack, + user, PermissionLevel::Write, ) .await?; if stack.config.git_provider != "github.com" { - return Err(anyhow!( - "Can only manage github.com repo webhooks" - )); + return Err( + anyhow!("Can only manage github.com repo webhooks").into(), + ); } if stack.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't create webhook" - )); + return Err( + anyhow!("No repo configured, can't create webhook").into(), + ); } let mut split = stack.config.repo.split('/'); let owner = split.next().context("Stack repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo = @@ -597,7 +602,7 @@ impl Resolve for State { } else { webhook_base_url }; - let url = match action { + let url = match self.action { StackWebhookAction::Refresh => { format!("{host}/listener/github/stack/{}/refresh", stack.id) } diff --git a/bin/core/src/api/write/sync.rs b/bin/core/src/api/write/sync.rs index 7d522c657..47ec02b3b 100644 --- a/bin/core/src/api/write/sync.rs +++ b/bin/core/src/api/write/sync.rs @@ -26,7 +26,7 @@ use komodo_client::{ }, to_komodo_name, update::{Log, Update}, - user::{sync_user, User}, + user::sync_user, CloneArgs, NoData, Operation, ResourceTarget, }, }; @@ -42,104 +42,123 @@ use tokio::fs; use crate::{ alert::send_alerts, + api::read::ReadArgs, config::core_config, helpers::{ query::get_id_to_tags, update::{add_update, make_update, update_update}, }, resource::{self, refresh_resource_sync_state_cache}, - state::{db_client, github_client, State}, + state::{db_client, github_client}, sync::{ deploy::SyncDeployParams, remote::RemoteResources, view::push_updates_for_view, AllResourcesById, }, }; -impl Resolve for State { - #[instrument(name = "CreateResourceSync", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateResourceSync { + #[instrument(name = "CreateResourceSync", skip(user))] async fn resolve( - &self, - CreateResourceSync { name, config }: CreateResourceSync, - user: User, - ) -> anyhow::Result { - resource::create::(&name, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::create::( + &self.name, + self.config, + &user, + ) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "CopyResourceSync", skip(self, user))] +impl Resolve for CopyResourceSync { + #[instrument(name = "CopyResourceSync", skip(user))] async fn resolve( - &self, - CopyResourceSync { name, id }: CopyResourceSync, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let ResourceSync { config, .. } = resource::get_check_permissions::( - &id, + &self.id, &user, PermissionLevel::Write, ) .await?; - resource::create::(&name, config.into(), &user) - .await + Ok( + resource::create::( + &self.name, + config.into(), + user, + ) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "DeleteResourceSync", skip(self, user))] +impl Resolve for DeleteResourceSync { + #[instrument(name = "DeleteResourceSync", skip(args))] async fn resolve( - &self, - DeleteResourceSync { id }: DeleteResourceSync, - user: User, - ) -> anyhow::Result { - resource::delete::(&id, &user).await + self, + args: &WriteArgs, + ) -> serror::Result { + Ok(resource::delete::(&self.id, args).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateResourceSync", skip(self, user))] +impl Resolve for UpdateResourceSync { + #[instrument(name = "UpdateResourceSync", skip(user))] async fn resolve( - &self, - UpdateResourceSync { id, config }: UpdateResourceSync, - user: User, - ) -> anyhow::Result { - resource::update::(&id, config, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::update::(&self.id, self.config, user) + .await?, + ) } } -impl Resolve for State { - #[instrument(name = "RenameResourceSync", skip(self, user))] +impl Resolve for RenameResourceSync { + #[instrument(name = "RenameResourceSync", skip(user))] async fn resolve( - &self, - RenameResourceSync { id, name }: RenameResourceSync, - user: User, - ) -> anyhow::Result { - resource::rename::(&id, &name, &user).await + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + Ok( + resource::rename::(&self.id, &self.name, user) + .await?, + ) } } -impl Resolve for State { - async fn resolve( - &self, - WriteSyncFileContents { +impl Resolve for WriteSyncFileContents { + async fn resolve(self, args: &WriteArgs) -> serror::Result { + let WriteSyncFileContents { sync, resource_path, file_path, contents, - }: WriteSyncFileContents, - user: User, - ) -> anyhow::Result { + } = self; + let WriteArgs { user } = args; + let sync = resource::get_check_permissions::( &sync, - &user, + user, PermissionLevel::Write, ) .await?; if !sync.config.files_on_host && sync.config.repo.is_empty() { - return Err(anyhow!( + return Err( + anyhow!( "This method is only for files on host, or repo based syncs." - )); + ) + .into(), + ); } let mut update = @@ -187,12 +206,14 @@ impl Resolve for State { } if sync.config.files_on_host { - if let Err(e) = State - .resolve(RefreshResourceSyncPending { sync: sync.name }, user) + if let Err(e) = (RefreshResourceSyncPending { sync: sync.name }) + .resolve(args) .await { - update - .push_error_log("Refresh failed", format_serror(&e.into())); + update.push_error_log( + "Refresh failed", + format_serror(&e.error.into()), + ); } update.finalize(); @@ -210,12 +231,14 @@ impl Resolve for State { update.logs.extend(commit_res.logs); - if let Err(e) = State - .resolve(RefreshResourceSyncPending { sync: sync.name }, user) + if let Err(e) = (RefreshResourceSyncPending { sync: sync.name }) + .resolve(args) .await { - update - .push_error_log("Refresh failed", format_serror(&e.into())); + update.push_error_log( + "Refresh failed", + format_serror(&e.error.into()), + ); } update.finalize(); @@ -225,16 +248,14 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "CommitSync", skip(self, user))] - async fn resolve( - &self, - CommitSync { sync }: CommitSync, - user: User, - ) -> anyhow::Result { +impl Resolve for CommitSync { + #[instrument(name = "CommitSync", skip(args))] + async fn resolve(self, args: &WriteArgs) -> serror::Result { + let WriteArgs { user } = args; + let sync = resource::get_check_permissions::< entities::sync::ResourceSync, - >(&sync, &user, PermissionLevel::Write) + >(&self.sync, &user, PermissionLevel::Write) .await?; let file_contents_empty = sync.config.file_contents_empty(); @@ -244,9 +265,10 @@ impl Resolve for State { && file_contents_empty; if !sync.config.managed && !fresh_sync { - return Err(anyhow!( - "Cannot commit to sync. Enabled 'managed' mode." - )); + return Err( + anyhow!("Cannot commit to sync. Enabled 'managed' mode.") + .into(), + ); } // Get this here so it can fail before update created. @@ -265,23 +287,22 @@ impl Resolve for State { .context("Resource path missing '.toml' extension")? != "toml" { - return Err(anyhow!( - "Resource path missing '.toml' extension" - )); + return Err( + anyhow!("Resource path missing '.toml' extension").into(), + ); } Some(resource_path) } else { None }; - let res = State - .resolve( - ExportAllResourcesToToml { - tags: sync.config.match_tags.clone(), - }, - sync_user().to_owned(), - ) - .await?; + let res = ExportAllResourcesToToml { + tags: sync.config.match_tags.clone(), + } + .resolve(&ReadArgs { + user: sync_user().to_owned(), + }) + .await?; let mut update = make_update(&sync, Operation::CommitSync, &user); update.id = add_update(update.clone()).await?; @@ -366,13 +387,13 @@ impl Resolve for State { return Ok(update); } - if let Err(e) = State - .resolve(RefreshResourceSyncPending { sync: sync.name }, user) + if let Err(e) = (RefreshResourceSyncPending { sync: sync.name }) + .resolve(args) .await { update.push_error_log( "Refresh sync pending", - format_serror(&(&e).into()), + format_serror(&e.error.into()), ); }; @@ -398,22 +419,21 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for RefreshResourceSyncPending { #[instrument( name = "RefreshResourceSyncPending", level = "debug", - skip(self, user) + skip(user) )] async fn resolve( - &self, - RefreshResourceSyncPending { sync }: RefreshResourceSyncPending, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { // Even though this is a write request, this doesn't change any config. Anyone that can execute the // sync should be able to do this. let mut sync = resource::get_check_permissions::< entities::sync::ResourceSync, - >(&sync, &user, PermissionLevel::Execute) + >(&self.sync, user, PermissionLevel::Execute) .await?; if !sync.config.managed @@ -443,9 +463,12 @@ impl Resolve for State { sync.info.pending_message = message; if !sync.info.remote_errors.is_empty() { - return Err(anyhow!( - "Remote resources have errors. Cannot compute diffs." - )); + return Err( + anyhow!( + "Remote resources have errors. Cannot compute diffs." + ) + .into(), + ); } let resources = resources?; @@ -744,43 +767,47 @@ impl Resolve for State { } }); - crate::resource::get::(&sync.id).await + Ok(crate::resource::get::(&sync.id).await?) } } -impl Resolve for State { - #[instrument(name = "CreateSyncWebhook", skip(self, user))] +impl Resolve for CreateSyncWebhook { + #[instrument(name = "CreateSyncWebhook", skip(args))] async fn resolve( - &self, - CreateSyncWebhook { sync, action }: CreateSyncWebhook, - user: User, - ) -> anyhow::Result { + self, + args: &WriteArgs, + ) -> serror::Result { + let WriteArgs { user } = args; let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; let sync = resource::get_check_permissions::( - &sync, - &user, + &self.sync, + user, PermissionLevel::Write, ) .await?; if sync.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't create webhook" - )); + return Err( + anyhow!("No repo configured, can't create webhook").into(), + ); } let mut split = sync.config.repo.split('/'); let owner = split.next().context("Sync repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo = @@ -813,7 +840,7 @@ impl Resolve for State { } else { webhook_base_url }; - let url = match action { + let url = match self.action { SyncWebhookAction::Refresh => { format!("{host}/listener/github/sync/{}/refresh", sync.id) } @@ -848,64 +875,65 @@ impl Resolve for State { .context("failed to create webhook")?; if !sync.config.webhook_enabled { - self - .resolve( - UpdateResourceSync { - id: sync.id, - config: PartialResourceSyncConfig { - webhook_enabled: Some(true), - ..Default::default() - }, - }, - user, - ) - .await - .context("failed to update sync to enable webhook")?; + UpdateResourceSync { + id: sync.id, + config: PartialResourceSyncConfig { + webhook_enabled: Some(true), + ..Default::default() + }, + } + .resolve(args) + .await + .map_err(|e| e.error) + .context("failed to update sync to enable webhook")?; } Ok(NoData {}) } } -impl Resolve for State { - #[instrument(name = "DeleteSyncWebhook", skip(self, user))] +impl Resolve for DeleteSyncWebhook { + #[instrument(name = "DeleteSyncWebhook", skip(user))] async fn resolve( - &self, - DeleteSyncWebhook { sync, action }: DeleteSyncWebhook, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let Some(github) = github_client() else { - return Err(anyhow!( - "github_webhook_app is not configured in core config toml" - )); + return Err( + anyhow!( + "github_webhook_app is not configured in core config toml" + ) + .into(), + ); }; let sync = resource::get_check_permissions::( - &sync, - &user, + &self.sync, + user, PermissionLevel::Write, ) .await?; if sync.config.git_provider != "github.com" { - return Err(anyhow!( - "Can only manage github.com repo webhooks" - )); + return Err( + anyhow!("Can only manage github.com repo webhooks").into(), + ); } if sync.config.repo.is_empty() { - return Err(anyhow!( - "No repo configured, can't create webhook" - )); + return Err( + anyhow!("No repo configured, can't create webhook").into(), + ); } let mut split = sync.config.repo.split('/'); let owner = split.next().context("Sync repo has no owner")?; let Some(github) = github.get(owner) else { - return Err(anyhow!( - "Cannot manage repo webhooks under owner {owner}" - )); + return Err( + anyhow!("Cannot manage repo webhooks under owner {owner}") + .into(), + ); }; let repo = @@ -931,7 +959,7 @@ impl Resolve for State { } else { webhook_base_url }; - let url = match action { + let url = match self.action { SyncWebhookAction::Refresh => { format!("{host}/listener/github/sync/{}/refresh", sync.id) } diff --git a/bin/core/src/api/write/tag.rs b/bin/core/src/api/write/tag.rs index a45b37850..fdb87fa1a 100644 --- a/bin/core/src/api/write/tag.rs +++ b/bin/core/src/api/write/tag.rs @@ -11,7 +11,7 @@ use komodo_client::{ deployment::Deployment, permission::PermissionLevel, procedure::Procedure, repo::Repo, server::Server, server_template::ServerTemplate, stack::Stack, - sync::ResourceSync, tag::Tag, user::User, ResourceTarget, + sync::ResourceSync, tag::Tag, ResourceTarget, }, }; use mungos::{ @@ -23,23 +23,24 @@ use resolver_api::Resolve; use crate::{ helpers::query::{get_tag, get_tag_check_owner}, resource, - state::{db_client, State}, + state::db_client, }; -impl Resolve for State { - #[instrument(name = "CreateTag", skip(self, user))] +use super::WriteArgs; + +impl Resolve for CreateTag { + #[instrument(name = "CreateTag", skip(user))] async fn resolve( - &self, - CreateTag { name }: CreateTag, - user: User, - ) -> anyhow::Result { - if ObjectId::from_str(&name).is_ok() { - return Err(anyhow!("tag name cannot be ObjectId")); + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + if ObjectId::from_str(&self.name).is_ok() { + return Err(anyhow!("tag name cannot be ObjectId").into()); } let mut tag = Tag { id: Default::default(), - name, + name: self.name, owner: user.id.clone(), }; @@ -57,167 +58,170 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "RenameTag", skip(self, user))] +impl Resolve for RenameTag { + #[instrument(name = "RenameTag", skip(user))] async fn resolve( - &self, - RenameTag { id, name }: RenameTag, - user: User, - ) -> anyhow::Result { - if ObjectId::from_str(&name).is_ok() { - return Err(anyhow!("tag name cannot be ObjectId")); + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + if ObjectId::from_str(&self.name).is_ok() { + return Err(anyhow!("tag name cannot be ObjectId").into()); } - get_tag_check_owner(&id, &user).await?; + get_tag_check_owner(&self.id, &user).await?; update_one_by_id( &db_client().tags, - &id, - doc! { "$set": { "name": name } }, + &self.id, + doc! { "$set": { "name": self.name } }, None, ) .await .context("failed to rename tag on db")?; - get_tag(&id).await + Ok(get_tag(&self.id).await?) } } -impl Resolve for State { - #[instrument(name = "DeleteTag", skip(self, user))] +impl Resolve for DeleteTag { + #[instrument(name = "DeleteTag", skip(user))] async fn resolve( - &self, - DeleteTag { id }: DeleteTag, - user: User, - ) -> anyhow::Result { - let tag = get_tag_check_owner(&id, &user).await?; + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + let tag = get_tag_check_owner(&self.id, &user).await?; tokio::try_join!( - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), - resource::remove_tag_from_all::(&id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), + resource::remove_tag_from_all::(&self.id), )?; - delete_one_by_id(&db_client().tags, &id, None).await?; + delete_one_by_id(&db_client().tags, &self.id, None).await?; Ok(tag) } } -impl Resolve for State { - #[instrument(name = "UpdateTagsOnResource", skip(self, user))] +impl Resolve for UpdateTagsOnResource { + #[instrument(name = "UpdateTagsOnResource", skip(args))] async fn resolve( - &self, - UpdateTagsOnResource { target, tags }: UpdateTagsOnResource, - user: User, - ) -> anyhow::Result { - match target { - ResourceTarget::System(_) => return Err(anyhow!("")), + self, + args: &WriteArgs, + ) -> serror::Result { + let WriteArgs { user } = args; + match self.target { + ResourceTarget::System(_) => { + return Err(anyhow!("Invalid target type: System").into()) + } ResourceTarget::Build(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await?; + resource::update_tags::(&id, self.tags, args).await?; } ResourceTarget::Builder(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args).await? } ResourceTarget::Deployment(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args) + .await? } ResourceTarget::Server(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args).await? } ResourceTarget::Repo(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args).await? } ResourceTarget::Alerter(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args).await? } ResourceTarget::Procedure(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args) + .await? } ResourceTarget::Action(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args).await? } ResourceTarget::ServerTemplate(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user) + resource::update_tags::(&id, self.tags, args) .await? } ResourceTarget::ResourceSync(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args) + .await? } ResourceTarget::Stack(id) => { resource::get_check_permissions::( &id, - &user, + user, PermissionLevel::Write, ) .await?; - resource::update_tags::(&id, tags, user).await? + resource::update_tags::(&id, self.tags, args).await? } }; Ok(UpdateTagsOnResourceResponse {}) diff --git a/bin/core/src/api/write/user.rs b/bin/core/src/api/write/user.rs index 764a435b6..5ddd638c5 100644 --- a/bin/core/src/api/write/user.rs +++ b/bin/core/src/api/write/user.rs @@ -7,46 +7,41 @@ use komodo_client::{ UpdateUserPasswordResponse, UpdateUserUsername, UpdateUserUsernameResponse, }, - entities::{ - user::{User, UserConfig}, - NoData, - }, + entities::{user::UserConfig, NoData}, }; use mungos::mongodb::bson::{doc, oid::ObjectId}; use resolver_api::Resolve; -use crate::{ - helpers::hash_password, - state::{db_client, State}, -}; +use crate::{helpers::hash_password, state::db_client}; + +use super::WriteArgs; // -impl Resolve for State { +impl Resolve for UpdateUserUsername { async fn resolve( - &self, - UpdateUserUsername { username }: UpdateUserUsername, - user: User, - ) -> anyhow::Result { - if username.is_empty() { - return Err(anyhow!("Username cannot be empty.")); + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + if self.username.is_empty() { + return Err(anyhow!("Username cannot be empty.").into()); } let db = db_client(); if db .users - .find_one(doc! { "username": &username }) + .find_one(doc! { "username": &self.username }) .await .context("Failed to query for existing users")? .is_some() { - return Err(anyhow!("Username already taken.")); + return Err(anyhow!("Username already taken.").into()); } let id = ObjectId::from_str(&user.id) .context("User id not valid ObjectId.")?; db.users .update_one( doc! { "_id": id }, - doc! { "$set": { "username": username } }, + doc! { "$set": { "username": self.username } }, ) .await .context("Failed to update user username on database.")?; @@ -56,21 +51,20 @@ impl Resolve for State { // -impl Resolve for State { +impl Resolve for UpdateUserPassword { async fn resolve( - &self, - UpdateUserPassword { password }: UpdateUserPassword, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { let UserConfig::Local { .. } = user.config else { - return Err(anyhow!("User is not local user")); + return Err(anyhow!("User is not local user").into()); }; - if password.is_empty() { - return Err(anyhow!("Password cannot be empty.")); + if self.password.is_empty() { + return Err(anyhow!("Password cannot be empty.").into()); } let id = ObjectId::from_str(&user.id) .context("User id not valid ObjectId.")?; - let hashed_password = hash_password(password)?; + let hashed_password = hash_password(self.password)?; db_client() .users .update_one( @@ -87,22 +81,21 @@ impl Resolve for State { // -impl Resolve for State { +impl Resolve for DeleteUser { async fn resolve( - &self, - DeleteUser { user }: DeleteUser, - admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("Calling user is not admin.")); + return Err(anyhow!("Calling user is not admin.").into()); } - if admin.username == user || admin.id == user { - return Err(anyhow!("User cannot delete themselves.")); + if admin.username == self.user || admin.id == self.user { + return Err(anyhow!("User cannot delete themselves.").into()); } - let query = if let Ok(id) = ObjectId::from_str(&user) { + let query = if let Ok(id) = ObjectId::from_str(&self.user) { doc! { "_id": id } } else { - doc! { "username": user } + doc! { "username": self.user } }; let db = db_client(); let Some(user) = db @@ -111,15 +104,20 @@ impl Resolve for State { .await .context("Failed to query database for users.")? else { - return Err(anyhow!("No user found with given id / username")); + return Err( + anyhow!("No user found with given id / username").into(), + ); }; if user.super_admin { - return Err(anyhow!("Cannot delete a super admin user.")); + return Err( + anyhow!("Cannot delete a super admin user.").into(), + ); } if user.admin && !admin.super_admin { - return Err(anyhow!( - "Only a Super Admin can delete an admin user." - )); + return Err( + anyhow!("Only a Super Admin can delete an admin user.") + .into(), + ); } db.users .delete_one(query) diff --git a/bin/core/src/api/write/user_group.rs b/bin/core/src/api/write/user_group.rs index 95f34170a..407c7dc21 100644 --- a/bin/core/src/api/write/user_group.rs +++ b/bin/core/src/api/write/user_group.rs @@ -6,7 +6,7 @@ use komodo_client::{ AddUserToUserGroup, CreateUserGroup, DeleteUserGroup, RemoveUserFromUserGroup, RenameUserGroup, SetUsersInUserGroup, }, - entities::{komodo_timestamp, user::User, user_group::UserGroup}, + entities::{komodo_timestamp, user_group::UserGroup}, }; use mungos::{ by_id::{delete_one_by_id, find_one_by_id, update_one_by_id}, @@ -15,23 +15,24 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::state::{db_client, State}; +use crate::state::db_client; -impl Resolve for State { +use super::WriteArgs; + +impl Resolve for CreateUserGroup { async fn resolve( - &self, - CreateUserGroup { name }: CreateUserGroup, - admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This call is admin-only")); + return Err(anyhow!("This call is admin-only").into()); } let user_group = UserGroup { id: Default::default(), users: Default::default(), all: Default::default(), updated_at: komodo_timestamp(), - name, + name: self.name, }; let db = db_client(); let id = db @@ -43,63 +44,63 @@ impl Resolve for State { .as_object_id() .context("inserted id is not ObjectId")? .to_string(); - find_one_by_id(&db.user_groups, &id) + let res = find_one_by_id(&db.user_groups, &id) .await .context("failed to query db for user groups")? - .context("user group at id not found") + .context("user group at id not found")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for RenameUserGroup { async fn resolve( - &self, - RenameUserGroup { id, name }: RenameUserGroup, - admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This call is admin-only")); + return Err(anyhow!("This call is admin-only").into()); } let db = db_client(); update_one_by_id( &db.user_groups, - &id, - doc! { "$set": { "name": name } }, + &self.id, + doc! { "$set": { "name": self.name } }, None, ) .await .context("failed to rename UserGroup on db")?; - find_one_by_id(&db.user_groups, &id) + let res = find_one_by_id(&db.user_groups, &self.id) .await .context("failed to query db for UserGroups")? - .context("no user group with given id") + .context("no user group with given id")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for DeleteUserGroup { async fn resolve( - &self, - DeleteUserGroup { id }: DeleteUserGroup, - admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This call is admin-only")); + return Err(anyhow!("This call is admin-only").into()); } let db = db_client(); - let ug = find_one_by_id(&db.user_groups, &id) + let ug = find_one_by_id(&db.user_groups, &self.id) .await .context("failed to query db for UserGroups")? .context("no UserGroup found with given id")?; - delete_one_by_id(&db.user_groups, &id, None) + delete_one_by_id(&db.user_groups, &self.id, None) .await .context("failed to delete UserGroup from db")?; db.permissions .delete_many(doc! { "user_target.type": "UserGroup", - "user_target.id": id, + "user_target.id": self.id, }) .await .context("failed to clean up UserGroups permissions. User Group has been deleted")?; @@ -108,21 +109,20 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for AddUserToUserGroup { async fn resolve( - &self, - AddUserToUserGroup { user_group, user }: AddUserToUserGroup, - admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This call is admin-only")); + return Err(anyhow!("This call is admin-only").into()); } let db = db_client(); - let filter = match ObjectId::from_str(&user) { + let filter = match ObjectId::from_str(&self.user) { Ok(id) => doc! { "_id": id }, - Err(_) => doc! { "username": &user }, + Err(_) => doc! { "username": &self.user }, }; let user = db .users @@ -131,9 +131,9 @@ impl Resolve for State { .context("failed to query mongo for users")? .context("no matching user found")?; - let filter = match ObjectId::from_str(&user_group) { + let filter = match ObjectId::from_str(&self.user_group) { Ok(id) => doc! { "_id": id }, - Err(_) => doc! { "name": &user_group }, + Err(_) => doc! { "name": &self.user_group }, }; db.user_groups .update_one( @@ -142,32 +142,30 @@ impl Resolve for State { ) .await .context("failed to add user to group on db")?; - db.user_groups + let res = db + .user_groups .find_one(filter) .await .context("failed to query db for UserGroups")? - .context("no user group with given id") + .context("no user group with given id")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for RemoveUserFromUserGroup { async fn resolve( - &self, - RemoveUserFromUserGroup { - user_group, - user, - }: RemoveUserFromUserGroup, - admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This call is admin-only")); + return Err(anyhow!("This call is admin-only").into()); } let db = db_client(); - let filter = match ObjectId::from_str(&user) { + let filter = match ObjectId::from_str(&self.user) { Ok(id) => doc! { "_id": id }, - Err(_) => doc! { "username": &user }, + Err(_) => doc! { "username": &self.user }, }; let user = db .users @@ -176,9 +174,9 @@ impl Resolve for State { .context("failed to query mongo for users")? .context("no matching user found")?; - let filter = match ObjectId::from_str(&user_group) { + let filter = match ObjectId::from_str(&self.user_group) { Ok(id) => doc! { "_id": id }, - Err(_) => doc! { "name": &user_group }, + Err(_) => doc! { "name": &self.user_group }, }; db.user_groups .update_one( @@ -187,22 +185,23 @@ impl Resolve for State { ) .await .context("failed to add user to group on db")?; - db.user_groups + let res = db + .user_groups .find_one(filter) .await .context("failed to query db for UserGroups")? - .context("no user group with given id") + .context("no user group with given id")?; + Ok(res) } } -impl Resolve for State { +impl Resolve for SetUsersInUserGroup { async fn resolve( - &self, - SetUsersInUserGroup { user_group, users }: SetUsersInUserGroup, - admin: User, - ) -> anyhow::Result { + self, + WriteArgs { user: admin }: &WriteArgs, + ) -> serror::Result { if !admin.admin { - return Err(anyhow!("This call is admin-only")); + return Err(anyhow!("This call is admin-only").into()); } let db = db_client(); @@ -215,7 +214,8 @@ impl Resolve for State { .collect::>(); // Make sure all users are user ids - let users = users + let users = self + .users .into_iter() .filter_map(|user| match ObjectId::from_str(&user) { Ok(_) => Some(user), @@ -223,18 +223,20 @@ impl Resolve for State { }) .collect::>(); - let filter = match ObjectId::from_str(&user_group) { + let filter = match ObjectId::from_str(&self.user_group) { Ok(id) => doc! { "_id": id }, - Err(_) => doc! { "name": &user_group }, + Err(_) => doc! { "name": &self.user_group }, }; db.user_groups .update_one(filter.clone(), doc! { "$set": { "users": users } }) .await .context("failed to set users on user group")?; - db.user_groups + let res = db + .user_groups .find_one(filter) .await .context("failed to query db for UserGroups")? - .context("no user group with given id") + .context("no user group with given id")?; + Ok(res) } } diff --git a/bin/core/src/api/write/variable.rs b/bin/core/src/api/write/variable.rs index aef49517c..2f5335420 100644 --- a/bin/core/src/api/write/variable.rs +++ b/bin/core/src/api/write/variable.rs @@ -1,15 +1,7 @@ use anyhow::{anyhow, Context}; use komodo_client::{ - api::write::{ - CreateVariable, CreateVariableResponse, DeleteVariable, - DeleteVariableResponse, UpdateVariableDescription, - UpdateVariableDescriptionResponse, UpdateVariableIsSecret, - UpdateVariableIsSecretResponse, UpdateVariableValue, - UpdateVariableValueResponse, - }, - entities::{ - user::User, variable::Variable, Operation, ResourceTarget, - }, + api::write::*, + entities::{variable::Variable, Operation, ResourceTarget}, }; use mungos::mongodb::bson::doc; use resolver_api::Resolve; @@ -19,23 +11,26 @@ use crate::{ query::get_variable, update::{add_update, make_update}, }, - state::{db_client, State}, + state::db_client, }; -impl Resolve for State { - #[instrument(name = "CreateVariable", skip(self, user, value))] +use super::WriteArgs; + +impl Resolve for CreateVariable { + #[instrument(name = "CreateVariable", skip(user, self), fields(name = &self.name))] async fn resolve( - &self, - CreateVariable { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { + let CreateVariable { name, value, description, is_secret, - }: CreateVariable, - user: User, - ) -> anyhow::Result { + } = self; + if !user.admin { - return Err(anyhow!("only admins can create variables")); + return Err(anyhow!("only admins can create variables").into()); } let variable = Variable { @@ -63,21 +58,22 @@ impl Resolve for State { add_update(update).await?; - get_variable(&variable.name).await + Ok(get_variable(&variable.name).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateVariableValue", skip(self, user, value))] +impl Resolve for UpdateVariableValue { + #[instrument(name = "UpdateVariableValue", skip(user, self), fields(name = &self.name))] async fn resolve( - &self, - UpdateVariableValue { name, value }: UpdateVariableValue, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("only admins can update variables")); + return Err(anyhow!("only admins can update variables").into()); } + let UpdateVariableValue { name, value } = self; + let variable = get_variable(&name).await?; if value == variable.value { @@ -116,74 +112,71 @@ impl Resolve for State { add_update(update).await?; - get_variable(&name).await + Ok(get_variable(&name).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateVariableDescription", skip(self, user))] +impl Resolve for UpdateVariableDescription { + #[instrument(name = "UpdateVariableDescription", skip(user))] async fn resolve( - &self, - UpdateVariableDescription { name, description }: UpdateVariableDescription, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("only admins can update variables")); + return Err(anyhow!("only admins can update variables").into()); } db_client() .variables .update_one( - doc! { "name": &name }, - doc! { "$set": { "description": &description } }, + doc! { "name": &self.name }, + doc! { "$set": { "description": &self.description } }, ) .await .context("failed to update variable description on db")?; - get_variable(&name).await + Ok(get_variable(&self.name).await?) } } -impl Resolve for State { - #[instrument(name = "UpdateVariableIsSecret", skip(self, user))] +impl Resolve for UpdateVariableIsSecret { + #[instrument(name = "UpdateVariableIsSecret", skip(user))] async fn resolve( - &self, - UpdateVariableIsSecret { name, is_secret }: UpdateVariableIsSecret, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("only admins can update variables")); + return Err(anyhow!("only admins can update variables").into()); } db_client() .variables .update_one( - doc! { "name": &name }, - doc! { "$set": { "is_secret": is_secret } }, + doc! { "name": &self.name }, + doc! { "$set": { "is_secret": self.is_secret } }, ) .await .context("failed to update variable is secret on db")?; - get_variable(&name).await + Ok(get_variable(&self.name).await?) } } -impl Resolve for State { +impl Resolve for DeleteVariable { async fn resolve( - &self, - DeleteVariable { name }: DeleteVariable, - user: User, - ) -> anyhow::Result { + self, + WriteArgs { user }: &WriteArgs, + ) -> serror::Result { if !user.admin { - return Err(anyhow!("only admins can delete variables")); + return Err(anyhow!("only admins can delete variables").into()); } - let variable = get_variable(&name).await?; + let variable = get_variable(&self.name).await?; db_client() .variables - .delete_one(doc! { "name": &name }) + .delete_one(doc! { "name": &self.name }) .await .context("failed to delete variable on db")?; let mut update = make_update( ResourceTarget::system(), Operation::DeleteVariable, - &user, + user, ); update diff --git a/bin/core/src/auth/local.rs b/bin/core/src/auth/local.rs index 619d90e93..402e47da9 100644 --- a/bin/core/src/auth/local.rs +++ b/bin/core/src/auth/local.rs @@ -2,7 +2,6 @@ use std::str::FromStr; use anyhow::{anyhow, Context}; use async_timing_util::unix_timestamp_ms; -use axum::http::HeaderMap; use komodo_client::{ api::auth::{ CreateLocalUser, CreateLocalUserResponse, LoginLocalUser, @@ -15,50 +14,52 @@ use mungos::mongodb::bson::{doc, oid::ObjectId}; use resolver_api::Resolve; use crate::{ + api::auth::AuthArgs, config::core_config, helpers::hash_password, - state::{db_client, jwt_client, State}, + state::{db_client, jwt_client}, }; -impl Resolve for State { +impl Resolve for CreateLocalUser { #[instrument(name = "CreateLocalUser", skip(self))] async fn resolve( - &self, - CreateLocalUser { username, password }: CreateLocalUser, - _: HeaderMap, - ) -> anyhow::Result { + self, + _: &AuthArgs, + ) -> serror::Result { let core_config = core_config(); if !core_config.local_auth { - return Err(anyhow!("Local auth is not enabled")); + return Err(anyhow!("Local auth is not enabled").into()); } - if username.is_empty() { - return Err(anyhow!("Username cannot be empty string")); + if self.username.is_empty() { + return Err(anyhow!("Username cannot be empty string").into()); } - if ObjectId::from_str(&username).is_ok() { - return Err(anyhow!("Username cannot be valid ObjectId")); + if ObjectId::from_str(&self.username).is_ok() { + return Err( + anyhow!("Username cannot be valid ObjectId").into(), + ); } - if password.is_empty() { - return Err(anyhow!("Password cannot be empty string")); + if self.password.is_empty() { + return Err(anyhow!("Password cannot be empty string").into()); } - let hashed_password = hash_password(password)?; + let hashed_password = hash_password(self.password)?; let no_users_exist = db_client().users.find_one(Document::new()).await?.is_none(); if !no_users_exist && core_config.disable_user_registration { - return Err(anyhow!("User registration is disabled")); + return Err(anyhow!("User registration is disabled").into()); } let ts = unix_timestamp_ms() as i64; let user = User { id: Default::default(), - username, + username: self.username, enabled: no_users_exist || core_config.enable_new_users, admin: no_users_exist, super_admin: no_users_exist, @@ -91,40 +92,42 @@ impl Resolve for State { } } -impl Resolve for State { +impl Resolve for LoginLocalUser { #[instrument(name = "LoginLocalUser", level = "debug", skip(self))] async fn resolve( - &self, - LoginLocalUser { username, password }: LoginLocalUser, - _: HeaderMap, - ) -> anyhow::Result { + self, + _: &AuthArgs, + ) -> serror::Result { if !core_config().local_auth { - return Err(anyhow!("local auth is not enabled")); + return Err(anyhow!("local auth is not enabled").into()); } let user = db_client() .users - .find_one(doc! { "username": &username }) + .find_one(doc! { "username": &self.username }) .await .context("failed at db query for users")? .with_context(|| { - format!("did not find user with username {username}") + format!("did not find user with username {}", self.username) })?; let UserConfig::Local { password: user_pw_hash, } = user.config else { - return Err(anyhow!( - "non-local auth users can not log in with a password" - )); + return Err( + anyhow!( + "non-local auth users can not log in with a password" + ) + .into(), + ); }; - let verified = bcrypt::verify(password, &user_pw_hash) + let verified = bcrypt::verify(self.password, &user_pw_hash) .context("failed at verify password")?; if !verified { - return Err(anyhow!("invalid credentials")); + return Err(anyhow!("invalid credentials").into()); } let jwt = jwt_client() diff --git a/bin/core/src/cloud/aws/ec2.rs b/bin/core/src/cloud/aws/ec2.rs index 03770c34d..4f8ace86d 100644 --- a/bin/core/src/cloud/aws/ec2.rs +++ b/bin/core/src/cloud/aws/ec2.rs @@ -1087,7 +1087,90 @@ fn handle_unknown_instance_type( | InstanceType::Z1d6xlarge | InstanceType::Z1dLarge | InstanceType::Z1dMetal - | InstanceType::Z1dXlarge => Ok(instance_type), + | InstanceType::Z1dXlarge + | InstanceType::C7gdMetal + | InstanceType::C7gnMetal + | InstanceType::C7iFlex2xlarge + | InstanceType::C7iFlex4xlarge + | InstanceType::C7iFlex8xlarge + | InstanceType::C7iFlexLarge + | InstanceType::C7iFlexXlarge + | InstanceType::C8g12xlarge + | InstanceType::C8g16xlarge + | InstanceType::C8g24xlarge + | InstanceType::C8g2xlarge + | InstanceType::C8g48xlarge + | InstanceType::C8g4xlarge + | InstanceType::C8g8xlarge + | InstanceType::C8gLarge + | InstanceType::C8gMedium + | InstanceType::C8gMetal24xl + | InstanceType::C8gMetal48xl + | InstanceType::C8gXlarge + | InstanceType::G612xlarge + | InstanceType::G616xlarge + | InstanceType::G624xlarge + | InstanceType::G62xlarge + | InstanceType::G648xlarge + | InstanceType::G64xlarge + | InstanceType::G68xlarge + | InstanceType::G6Xlarge + | InstanceType::G6e12xlarge + | InstanceType::G6e16xlarge + | InstanceType::G6e24xlarge + | InstanceType::G6e2xlarge + | InstanceType::G6e48xlarge + | InstanceType::G6e4xlarge + | InstanceType::G6e8xlarge + | InstanceType::G6eXlarge + | InstanceType::Gr64xlarge + | InstanceType::Gr68xlarge + | InstanceType::M7gdMetal + | InstanceType::M8g12xlarge + | InstanceType::M8g16xlarge + | InstanceType::M8g24xlarge + | InstanceType::M8g2xlarge + | InstanceType::M8g48xlarge + | InstanceType::M8g4xlarge + | InstanceType::M8g8xlarge + | InstanceType::M8gLarge + | InstanceType::M8gMedium + | InstanceType::M8gMetal24xl + | InstanceType::M8gMetal48xl + | InstanceType::M8gXlarge + | InstanceType::Mac2M1ultraMetal + | InstanceType::R7gdMetal + | InstanceType::R7izMetal16xl + | InstanceType::R7izMetal32xl + | InstanceType::R8g12xlarge + | InstanceType::R8g16xlarge + | InstanceType::R8g24xlarge + | InstanceType::R8g2xlarge + | InstanceType::R8g48xlarge + | InstanceType::R8g4xlarge + | InstanceType::R8g8xlarge + | InstanceType::R8gLarge + | InstanceType::R8gMedium + | InstanceType::R8gMetal24xl + | InstanceType::R8gMetal48xl + | InstanceType::R8gXlarge + | InstanceType::U7i12tb224xlarge + | InstanceType::U7ib12tb224xlarge + | InstanceType::U7in16tb224xlarge + | InstanceType::U7in24tb224xlarge + | InstanceType::U7in32tb224xlarge + | InstanceType::X8g12xlarge + | InstanceType::X8g16xlarge + | InstanceType::X8g24xlarge + | InstanceType::X8g2xlarge + | InstanceType::X8g48xlarge + | InstanceType::X8g4xlarge + | InstanceType::X8g8xlarge + | InstanceType::X8gLarge + | InstanceType::X8gMedium + | InstanceType::X8gMetal24xl + | InstanceType::X8gMetal48xl + | InstanceType::X8gXlarge => Ok(instance_type), other => Err(anyhow!("unknown InstanceType: {other:?}")), } } diff --git a/bin/core/src/helpers/mod.rs b/bin/core/src/helpers/mod.rs index 086f3e290..cd229e786 100644 --- a/bin/core/src/helpers/mod.rs +++ b/bin/core/src/helpers/mod.rs @@ -25,9 +25,8 @@ use rand::{distributions::Alphanumeric, thread_rng, Rng}; use resolver_api::Resolve; use crate::{ - config::core_config, - resource, - state::{db_client, State}, + api::write::WriteArgs, config::core_config, resource, + state::db_client, }; pub mod action_state; @@ -305,46 +304,44 @@ pub async fn ensure_first_server_and_builder() { let server = if let Some(server) = server { server } else { - match State - .resolve( - CreateServer { - name: format!("server-{}", random_string(5)), - config: PartialServerConfig { - address: Some(first_server.to_string()), - enabled: Some(true), - ..Default::default() - }, - }, - system_user().to_owned(), - ) - .await + match (CreateServer { + name: format!("server-{}", random_string(5)), + config: PartialServerConfig { + address: Some(first_server.to_string()), + enabled: Some(true), + ..Default::default() + }, + }) + .resolve(&WriteArgs { + user: system_user().to_owned(), + }) + .await { Ok(server) => server, Err(e) => { - error!("Failed to initialize 'first_server'. Failed to CreateServer. {e:?}"); + error!("Failed to initialize 'first_server'. Failed to CreateServer. {:#}", e.error); return; } } }; let Ok(None) = db.builders .find_one(Document::new()).await - .inspect_err(|e| error!("Failed to initialize 'first_builder'. Failed to query db. {e:?}")) else { + .inspect_err(|e| error!("Failed to initialize 'first_builder' | Failed to query db | {e:?}")) else { return; }; - if let Err(e) = State - .resolve( - CreateBuilder { - name: String::from("local"), - config: PartialBuilderConfig::Server( - PartialServerBuilderConfig { - server_id: Some(server.id), - }, - ), + if let Err(e) = (CreateBuilder { + name: String::from("local"), + config: PartialBuilderConfig::Server( + PartialServerBuilderConfig { + server_id: Some(server.id), }, - system_user().to_owned(), - ) - .await + ), + }) + .resolve(&WriteArgs { + user: system_user().to_owned(), + }) + .await { - error!("Failed to initialize 'first_builder'. Failed to CreateBuilder. {e:?}"); + error!("Failed to initialize 'first_builder' | Failed to CreateBuilder | {:#}", e.error); } } diff --git a/bin/core/src/helpers/procedure.rs b/bin/core/src/helpers/procedure.rs index a55a6fb10..3652fe9a8 100644 --- a/bin/core/src/helpers/procedure.rs +++ b/bin/core/src/helpers/procedure.rs @@ -21,9 +21,12 @@ use resolver_api::Resolve; use tokio::sync::Mutex; use crate::{ - api::execute::ExecuteRequest, + api::{ + execute::{ExecuteArgs, ExecuteRequest}, + write::WriteArgs, + }, resource::{list_full_for_user_using_pattern, KomodoResource}, - state::{db_client, State}, + state::db_client, }; use super::update::{init_execution_update, update_update}; @@ -227,9 +230,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RunProcedure"), &update_id, ) @@ -249,9 +253,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RunAction"), &update_id, ) @@ -271,9 +276,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RunBuild"), &update_id, ) @@ -293,9 +299,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at CancelBuild"), &update_id, ) @@ -309,9 +316,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at Deploy"), &update_id, ) @@ -331,9 +339,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PullDeployment"), &update_id, ) @@ -347,9 +356,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StartDeployment"), &update_id, ) @@ -363,9 +373,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RestartDeployment"), &update_id, ) @@ -379,9 +390,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PauseDeployment"), &update_id, ) @@ -395,9 +407,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at UnpauseDeployment"), &update_id, ) @@ -411,9 +424,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StopDeployment"), &update_id, ) @@ -427,9 +441,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RemoveDeployment"), &update_id, ) @@ -449,9 +464,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at CloneRepo"), &update_id, ) @@ -471,9 +487,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PullRepo"), &update_id, ) @@ -493,9 +510,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at BuildRepo"), &update_id, ) @@ -515,9 +533,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at CancelRepoBuild"), &update_id, ) @@ -531,9 +550,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StartContainer"), &update_id, ) @@ -547,9 +567,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RestartContainer"), &update_id, ) @@ -563,9 +584,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PauseContainer"), &update_id, ) @@ -579,9 +601,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at UnpauseContainer"), &update_id, ) @@ -595,9 +618,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StopContainer"), &update_id, ) @@ -611,9 +635,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RemoveContainer"), &update_id, ) @@ -627,9 +652,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StartAllContainers"), &update_id, ) @@ -643,9 +669,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RestartAllContainers"), &update_id, ) @@ -659,9 +686,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PauseAllContainers"), &update_id, ) @@ -675,9 +703,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at UnpauseAllContainers"), &update_id, ) @@ -691,9 +720,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StopAllContainers"), &update_id, ) @@ -707,9 +737,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PruneContainers"), &update_id, ) @@ -723,9 +754,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at DeleteNetwork"), &update_id, ) @@ -739,9 +771,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PruneNetworks"), &update_id, ) @@ -755,9 +788,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at DeleteImage"), &update_id, ) @@ -771,9 +805,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PruneImages"), &update_id, ) @@ -787,9 +822,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at DeleteVolume"), &update_id, ) @@ -803,9 +839,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PruneVolumes"), &update_id, ) @@ -819,9 +856,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PruneDockerBuilders"), &update_id, ) @@ -835,9 +873,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PruneBuildx"), &update_id, ) @@ -851,9 +890,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PruneSystem"), &update_id, ) @@ -867,18 +907,20 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RunSync"), &update_id, ) .await? } // Exception: This is a write operation. - Execution::CommitSync(req) => State - .resolve(req, user) + Execution::CommitSync(req) => req + .resolve(&WriteArgs { user }) .await + .map_err(|e| e.error) .context("Failed at CommitSync")?, Execution::DeployStack(req) => { let req = ExecuteRequest::DeployStack(req); @@ -888,9 +930,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at DeployStack"), &update_id, ) @@ -910,9 +953,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at DeployStackIfChanged"), &update_id, ) @@ -932,9 +976,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PullStack"), &update_id, ) @@ -948,9 +993,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StartStack"), &update_id, ) @@ -964,9 +1010,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at RestartStack"), &update_id, ) @@ -980,9 +1027,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at PauseStack"), &update_id, ) @@ -996,9 +1044,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at UnpauseStack"), &update_id, ) @@ -1012,9 +1061,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at StopStack"), &update_id, ) @@ -1028,9 +1078,10 @@ async fn execute_execution( }; let update_id = update.id.clone(); handle_resolve_result( - State - .resolve(req, (user, update)) + req + .resolve(&ExecuteArgs { user, update }) .await + .map_err(|e| e.error) .context("Failed at DestroyStack"), &update_id, ) diff --git a/bin/core/src/listener/resources.rs b/bin/core/src/listener/resources.rs index b37a300dd..c46ebf886 100644 --- a/bin/core/src/listener/resources.rs +++ b/bin/core/src/listener/resources.rs @@ -15,8 +15,11 @@ use resolver_api::Resolve; use serde::Deserialize; use crate::{ - api::execute::ExecuteRequest, - helpers::update::init_execution_update, state::State, + api::{ + execute::{ExecuteArgs, ExecuteRequest}, + write::WriteArgs, + }, + helpers::update::init_execution_update, }; use super::{ListenerLockCache, ANY_BRANCH}; @@ -58,7 +61,10 @@ pub async fn handle_build_webhook( let ExecuteRequest::RunBuild(req) = req else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; Ok(()) } @@ -93,7 +99,10 @@ impl RepoExecution for CloneRepo { else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; Ok(()) } } @@ -110,7 +119,10 @@ impl RepoExecution for PullRepo { else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; Ok(()) } } @@ -127,7 +139,10 @@ impl RepoExecution for BuildRepo { else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; Ok(()) } } @@ -196,21 +211,22 @@ fn stack_locks() -> &'static ListenerLockCache { } pub trait StackExecution { - async fn resolve(stack: Stack) -> anyhow::Result<()>; + async fn resolve(stack: Stack) -> serror::Result<()>; } impl StackExecution for RefreshStackCache { - async fn resolve(stack: Stack) -> anyhow::Result<()> { - let user = git_webhook_user().to_owned(); - State - .resolve(RefreshStackCache { stack: stack.id }, user) + async fn resolve(stack: Stack) -> serror::Result<()> { + RefreshStackCache { stack: stack.id } + .resolve(&WriteArgs { + user: git_webhook_user().to_owned(), + }) .await?; Ok(()) } } impl StackExecution for DeployStack { - async fn resolve(stack: Stack) -> anyhow::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 { @@ -222,7 +238,10 @@ impl StackExecution for DeployStack { let ExecuteRequest::DeployStack(req) = req else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; } else { let req = ExecuteRequest::DeployStackIfChanged(DeployStackIfChanged { @@ -233,7 +252,10 @@ impl StackExecution for DeployStack { let ExecuteRequest::DeployStackIfChanged(req) = req else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; } Ok(()) @@ -282,7 +304,7 @@ pub async fn handle_stack_webhook_inner< B::verify_branch(&body, &stack.config.branch)?; - E::resolve(stack).await + E::resolve(stack).await.map_err(|e| e.error) } // ====== @@ -306,10 +328,12 @@ pub trait SyncExecution { impl SyncExecution for RefreshResourceSyncPending { async fn resolve(sync: ResourceSync) -> anyhow::Result<()> { - let user = git_webhook_user().to_owned(); - State - .resolve(RefreshResourceSyncPending { sync: sync.id }, user) - .await?; + RefreshResourceSyncPending { sync: sync.id } + .resolve(&WriteArgs { + user: git_webhook_user().to_owned(), + }) + .await + .map_err(|e| e.error)?; Ok(()) } } @@ -326,7 +350,10 @@ impl SyncExecution for RunSync { let ExecuteRequest::RunSync(req) = req else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; Ok(()) } } @@ -422,7 +449,10 @@ pub async fn handle_procedure_webhook( let ExecuteRequest::RunProcedure(req) = req else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; Ok(()) } @@ -467,6 +497,9 @@ pub async fn handle_action_webhook( let ExecuteRequest::RunAction(req) = req else { unreachable!() }; - State.resolve(req, (user, update)).await?; + req + .resolve(&ExecuteArgs { user, update }) + .await + .map_err(|e| e.error)?; Ok(()) } diff --git a/bin/core/src/main.rs b/bin/core/src/main.rs index 608c21f4e..e992bd418 100644 --- a/bin/core/src/main.rs +++ b/bin/core/src/main.rs @@ -23,6 +23,7 @@ mod helpers; mod listener; mod monitor; mod resource; +mod resource2; mod stack; mod state; mod sync; @@ -101,14 +102,16 @@ async fn app() -> anyhow::Result<()> { .context("Invalid ssl cert / key")?; axum_server::bind_rustls(socket_addr, ssl_config) .serve(app) - .await? + .await + .context("failed to start https server") } else { info!("🔓 Core SSL Disabled"); info!("Komodo Core starting on http://{socket_addr}"); - axum_server::bind(socket_addr).serve(app).await? + axum_server::bind(socket_addr) + .serve(app) + .await + .context("failed to start http server") } - - Ok(()) } #[tokio::main] @@ -116,15 +119,10 @@ async fn main() -> anyhow::Result<()> { let mut term_signal = tokio::signal::unix::signal( tokio::signal::unix::SignalKind::terminate(), )?; - - let app = tokio::spawn(app()); - tokio::select! { - res = app => return res?, - _ = term_signal.recv() => {}, + res = tokio::spawn(app()) => res?, + _ = term_signal.recv() => Ok(()), } - - Ok(()) } fn cors() -> anyhow::Result { diff --git a/bin/core/src/monitor/resources.rs b/bin/core/src/monitor/resources.rs index f2b3ea065..180b8759a 100644 --- a/bin/core/src/monitor/resources.rs +++ b/bin/core/src/monitor/resources.rs @@ -395,7 +395,7 @@ pub async fn update_stack_cache( send_alerts(&[alert]).await; } Err(e) => { - warn!("Failed auto update Stack {} | {e:#}", stack.name) + warn!("Failed auto update Stack {} | {e:#}", stack.name,) } } }); diff --git a/bin/core/src/resource/mod.rs b/bin/core/src/resource/mod.rs index 289b1a786..cba347f14 100644 --- a/bin/core/src/resource/mod.rs +++ b/bin/core/src/resource/mod.rs @@ -34,6 +34,7 @@ use resolver_api::Resolve; use serde::{de::DeserializeOwned, Serialize}; use crate::{ + api::{read::ReadArgs, write::WriteArgs}, config::core_config, helpers::{ create_permission, flatten_document, @@ -43,7 +44,7 @@ use crate::{ }, update::{add_update, make_update}, }, - state::{db_client, State}, + state::db_client, }; mod action; @@ -783,20 +784,17 @@ pub async fn update_description( pub async fn update_tags( id_or_name: &str, tags: Vec, - user: User, + args: &WriteArgs, ) -> anyhow::Result<()> { let futures = tags.iter().map(|tag| async { match get_tag(tag).await { Ok(tag) => Ok(tag.id), - Err(_) => State - .resolve( - CreateTag { - name: tag.to_string(), - }, - user.clone(), - ) - .await - .map(|tag| tag.id), + Err(_) => CreateTag { + name: tag.to_string(), + } + .resolve(args) + .await + .map(|tag| tag.id), } }); let tags = join_all(futures) @@ -884,11 +882,11 @@ pub async fn rename( pub async fn delete( id_or_name: &str, - user: &User, + args: &WriteArgs, ) -> anyhow::Result> { let resource = get_check_permissions::( id_or_name, - user, + &args.user, PermissionLevel::Write, ) .await?; @@ -898,19 +896,19 @@ pub async fn delete( } let target = resource_target::(resource.id.clone()); - let toml = State - .resolve( - ExportResourcesToToml { - targets: vec![target.clone()], - ..Default::default() - }, - user.clone(), - ) - .await? - .toml; + let toml = ExportResourcesToToml { + targets: vec![target.clone()], + ..Default::default() + } + .resolve(&ReadArgs { + user: args.user.clone(), + }) + .await + .map_err(|e| e.error)? + .toml; let mut update = - make_update(target.clone(), T::delete_operation(), user); + make_update(target.clone(), T::delete_operation(), &args.user); T::pre_delete(&resource, &mut update).await?; diff --git a/bin/core/src/resource/refresh.rs b/bin/core/src/resource/refresh.rs index 85da9807d..5740d6afd 100644 --- a/bin/core/src/resource/refresh.rs +++ b/bin/core/src/resource/refresh.rs @@ -12,9 +12,9 @@ use mungos::find::find_collect; use resolver_api::Resolve; use crate::{ - api::execute::pull_deployment_inner, + api::{execute::pull_deployment_inner, write::WriteArgs}, config::core_config, - state::{db_client, State}, + state::db_client, }; pub fn spawn_resource_refresh_loop() { @@ -53,14 +53,13 @@ async fn refresh_stacks() { return; }; for stack in stacks { - State + RefreshStackCache { stack: stack.id } .resolve( - RefreshStackCache { stack: stack.id }, - stack_user().clone(), + &WriteArgs { user: stack_user().clone() }, ) .await .inspect_err(|e| { - warn!("Failed to refresh Stack cache in refresh task | Stack: {} | {e:#}", stack.name) + warn!("Failed to refresh Stack cache in refresh task | Stack: {} | {:#}", stack.name, e.error) }) .ok(); } @@ -115,14 +114,13 @@ async fn refresh_builds() { return; }; for build in builds { - State + RefreshBuildCache { build: build.id } .resolve( - RefreshBuildCache { build: build.id }, - build_user().clone(), + &WriteArgs { user: build_user().clone() }, ) .await .inspect_err(|e| { - warn!("Failed to refresh Build cache in refresh task | Build: {} | {e:#}", build.name) + warn!("Failed to refresh Build cache in refresh task | Build: {} | {:#}", build.name, e.error) }) .ok(); } @@ -140,14 +138,13 @@ async fn refresh_repos() { return; }; for repo in repos { - State + RefreshRepoCache { repo: repo.id } .resolve( - RefreshRepoCache { repo: repo.id }, - repo_user().clone(), + &WriteArgs { user: repo_user().clone() }, ) .await .inspect_err(|e| { - warn!("Failed to refresh Repo cache in refresh task | Repo: {} | {e:#}", repo.name) + warn!("Failed to refresh Repo cache in refresh task | Repo: {} | {:#}", repo.name, e.error) }) .ok(); } @@ -169,14 +166,13 @@ async fn refresh_syncs() { if sync.config.repo.is_empty() { continue; } - State + RefreshResourceSyncPending { sync: sync.id } .resolve( - RefreshResourceSyncPending { sync: sync.id }, - sync_user().clone(), + &WriteArgs { user: sync_user().clone() }, ) .await .inspect_err(|e| { - warn!("Failed to refresh ResourceSync in refresh task | Sync: {} | {e:#}", sync.name) + warn!("Failed to refresh ResourceSync in refresh task | Sync: {} | {:#}", sync.name, e.error) }) .ok(); } diff --git a/bin/core/src/resource/stack.rs b/bin/core/src/resource/stack.rs index 0de89ad9e..c51406377 100644 --- a/bin/core/src/resource/stack.rs +++ b/bin/core/src/resource/stack.rs @@ -21,12 +21,12 @@ use periphery_client::api::compose::ComposeExecution; use resolver_api::Resolve; use crate::{ + api::write::WriteArgs, config::core_config, helpers::{periphery_client, query::get_stack_state}, monitor::update_cache_for_server, state::{ - action_states, db_client, server_status_cache, - stack_status_cache, State, + action_states, db_client, server_status_cache, stack_status_cache, }, }; @@ -152,18 +152,17 @@ impl super::KomodoResource for Stack { created: &Resource, update: &mut Update, ) -> anyhow::Result<()> { - if let Err(e) = State - .resolve( - RefreshStackCache { - stack: created.name.clone(), - }, - stack_user().to_owned(), - ) - .await + if let Err(e) = (RefreshStackCache { + stack: created.name.clone(), + }) + .resolve(&WriteArgs { + user: stack_user().to_owned(), + }) + .await { update.push_error_log( "Refresh stack cache", - format_serror(&e.context("The stack cache has failed to refresh. This is likely due to a misconfiguration of the Stack").into()) + format_serror(&e.error.context("The stack cache has failed to refresh. This is likely due to a misconfiguration of the Stack").into()) ); }; if created.config.server_id.is_empty() { diff --git a/bin/core/src/resource/sync.rs b/bin/core/src/resource/sync.rs index b7b856647..b48442ecb 100644 --- a/bin/core/src/resource/sync.rs +++ b/bin/core/src/resource/sync.rs @@ -25,8 +25,9 @@ use mungos::{ }; use resolver_api::Resolve; -use crate::state::{ - action_states, db_client, resource_sync_state_cache, State, +use crate::{ + api::write::WriteArgs, + state::{action_states, db_client, resource_sync_state_cache}, }; impl super::KomodoResource for ResourceSync { @@ -103,18 +104,17 @@ impl super::KomodoResource for ResourceSync { created: &Resource, update: &mut Update, ) -> anyhow::Result<()> { - if let Err(e) = State - .resolve( - RefreshResourceSyncPending { - sync: created.id.clone(), - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (RefreshResourceSyncPending { + sync: created.id.clone(), + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { update.push_error_log( "Refresh sync pending", - format_serror(&e.context("The sync pending cache has failed to refresh. This is likely due to a misconfiguration of the sync").into()) + format_serror(&e.error.context("The sync pending cache has failed to refresh. This is likely due to a misconfiguration of the sync").into()) ); }; refresh_resource_sync_state_cache().await; diff --git a/bin/core/src/resource2/crud.rs b/bin/core/src/resource2/crud.rs new file mode 100644 index 000000000..e508dcb60 --- /dev/null +++ b/bin/core/src/resource2/crud.rs @@ -0,0 +1,233 @@ +use std::str::FromStr; + +use anyhow::{anyhow, Context}; +use komodo_client::entities::{ + komodo_timestamp, + permission::{Permission, PermissionLevel, UserTarget}, + resource::Resource, + to_komodo_name, + update::Update, + user::{system_user, User}, + Operation, +}; +use mungos::mongodb::bson::{oid::ObjectId, to_document, Document}; +use serde::Serialize; + +use crate::{ + helpers::update::{add_update, make_update}, + state::db_client, +}; + +use super::{query::ResourceQuery, resource_target, ResourceBase}; + +pub trait ResourceCrud: ResourceQuery { + type PartialConfig: Into + Serialize; + + // ======== + // CREATE + // ======== + + fn create_operation() -> Operation; + + fn user_can_create(user: &User) -> bool; + + async fn validate_create_config( + config: &mut Self::PartialConfig, + user: &User, + ) -> anyhow::Result<()>; + + async fn default_info() -> anyhow::Result { + Ok(Default::default()) + } + + async fn post_create( + created: &Resource, + update: &mut Update, + ) -> anyhow::Result<()>; + + // ======== + // UPDATE + // ======== + + fn update_operation() -> Operation; + + async fn validate_update_config( + id: &str, + config: &mut Self::PartialConfig, + user: &User, + ) -> anyhow::Result<()>; + + /// Should be overridden for enum configs, eg Alerter, Builder, ... + fn update_document( + _original: Resource, + config: Self::PartialConfig, + ) -> Result { + to_document(&config) + } + + /// Run any required task after resource updated in database but + /// before the request resolves. + async fn post_update( + updated: &Resource, + update: &mut Update, + ) -> anyhow::Result<()>; + + // ======== + // RENAME + // ======== + + fn rename_operation() -> Operation; + + // ======== + // DELETE + // ======== + + fn delete_operation() -> Operation; + + /// Clean up all links to this resource before deleting it. + async fn pre_delete( + resource: &Resource, + update: &mut Update, + ) -> anyhow::Result<()>; + + /// Run any required task after resource deleted from database but + /// before the request resolves. + async fn post_delete( + resource: &Resource, + update: &mut Update, + ) -> anyhow::Result<()>; +} + +// ======== +// CREATE +// ======== + +pub async fn create( + name: &str, + mut config: R::PartialConfig, + user: &User, +) -> anyhow::Result> { + if !R::user_can_create(user) { + return Err(anyhow!( + "User does not have permissions to create {}.", + R::resource_type() + )); + } + + if name.is_empty() { + return Err(anyhow!("Must provide non-empty name for resource.")); + } + + let name = to_komodo_name(name); + + if ObjectId::from_str(&name).is_ok() { + return Err(anyhow!("valid ObjectIds cannot be used as names.")); + } + + // Ensure an existing resource with same name doesn't already exist + // The database indexing also ensures this but doesn't give a good error message. + if super::query::list_full_for_user::( + Default::default(), + system_user(), + &[], + ) + .await + .context("Failed to list all resources for duplicate name check")? + .into_iter() + .any(|r| r.name == name) + { + return Err(anyhow!("Must provide unique name for resource.")); + } + + let start_ts = komodo_timestamp(); + + R::validate_create_config(&mut config, user).await?; + + let resource = Resource:: { + id: Default::default(), + name, + updated_at: start_ts, + description: Default::default(), + tags: Default::default(), + config: config.into(), + info: R::default_info().await?, + base_permission: PermissionLevel::None, + }; + + let resource_id = R::coll() + .insert_one(&resource) + .await + .with_context(|| { + format!("failed to add {} to db", R::resource_type()) + })? + .inserted_id + .as_object_id() + .context("inserted_id is not ObjectId")? + .to_string(); + + let resource = super::query::get::(&resource_id).await?; + + create_permission::(user, resource_id, PermissionLevel::Write) + .await; + + let mut update = make_update( + resource_target::(resource.id.clone()), + R::create_operation(), + user, + ); + update.start_ts = start_ts; + update.push_simple_log( + &format!("create {}", R::resource_type()), + format!( + "created {}\nid: {}\nname: {}", + R::resource_type(), + resource.id, + resource.name + ), + ); + update.push_simple_log( + "config", + serde_json::to_string_pretty(&resource.config) + .context("failed to serialize resource config to JSON")?, + ); + + R::post_create(&resource, &mut update).await?; + + update.finalize(); + add_update(update).await?; + + Ok(resource) +} + +#[instrument] +pub async fn create_permission( + user: &User, + id: String, + level: PermissionLevel, +) { + // No need to actually create permissions for admins + if user.admin { + return; + } + let target = super::resource_target::(id); + if let Err(e) = db_client() + .permissions + .insert_one(Permission { + id: Default::default(), + user_target: UserTarget::User(user.id.clone()), + resource_target: target.clone(), + level, + }) + .await + { + error!("failed to create permission for {target:?} | {e:#}"); + }; +} + +// ======== +// UPDATE +// ======== + +// ======== +// DELETE +// ======== diff --git a/bin/core/src/resource2/mod.rs b/bin/core/src/resource2/mod.rs new file mode 100644 index 000000000..8a67d0556 --- /dev/null +++ b/bin/core/src/resource2/mod.rs @@ -0,0 +1,102 @@ +use komodo_client::entities::{ + action::Action, build::Build, builder::Builder, + deployment::Deployment, procedure::Procedure, repo::Repo, + server::Server, server_template::ServerTemplate, stack::Stack, + sync::ResourceSync, ResourceTarget, ResourceTargetVariant, +}; + +pub mod crud; +pub mod query; + +pub trait ResourceBase { + fn resource_type() -> ResourceTargetVariant; +} + +pub fn resource_target( + id: String, +) -> ResourceTarget { + match R::resource_type() { + ResourceTargetVariant::System => ResourceTarget::System(id), + ResourceTargetVariant::Build => ResourceTarget::Build(id), + ResourceTargetVariant::Builder => ResourceTarget::Builder(id), + ResourceTargetVariant::Deployment => { + ResourceTarget::Deployment(id) + } + ResourceTargetVariant::Server => ResourceTarget::Server(id), + ResourceTargetVariant::Repo => ResourceTarget::Repo(id), + ResourceTargetVariant::Alerter => ResourceTarget::Alerter(id), + ResourceTargetVariant::Procedure => ResourceTarget::Procedure(id), + ResourceTargetVariant::ServerTemplate => { + ResourceTarget::ServerTemplate(id) + } + ResourceTargetVariant::ResourceSync => { + ResourceTarget::ResourceSync(id) + } + ResourceTargetVariant::Stack => ResourceTarget::Stack(id), + ResourceTargetVariant::Action => ResourceTarget::Action(id), + } +} + +// ================= +// IMPLEMENTATIONS +// ================= + +impl ResourceBase for Server { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Server + } +} + +impl ResourceBase for Stack { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Stack + } +} + +impl ResourceBase for Deployment { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Deployment + } +} + +impl ResourceBase for Build { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Build + } +} + +impl ResourceBase for Repo { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Repo + } +} + +impl ResourceBase for Procedure { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Procedure + } +} + +impl ResourceBase for Action { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Action + } +} + +impl ResourceBase for Builder { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::Builder + } +} + +impl ResourceBase for ServerTemplate { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::ServerTemplate + } +} + +impl ResourceBase for ResourceSync { + fn resource_type() -> ResourceTargetVariant { + ResourceTargetVariant::ResourceSync + } +} diff --git a/bin/core/src/resource2/query.rs b/bin/core/src/resource2/query.rs new file mode 100644 index 000000000..036f5b039 --- /dev/null +++ b/bin/core/src/resource2/query.rs @@ -0,0 +1,1027 @@ +use std::{ + collections::{HashMap, HashSet}, + str::FromStr, +}; + +use anyhow::{anyhow, Context}; +use futures::future::{join_all, FutureExt}; +use komodo_client::{ + entities::{ + action::*, + build::*, + builder::*, + deployment::*, + permission::PermissionLevel, + procedure::*, + repo::*, + resource::{ + AddFilters, Resource, ResourceQuery as ResourceQuerySchema, + }, + server::*, + server_template::*, + stack::*, + sync::*, + tag::Tag, + user::User, + ResourceTargetVariant, + }, + parsers::parse_string_list, +}; +use mongo_indexed::Document; +use mungos::{ + find::find_collect, + mongodb::{ + bson::{doc, oid::ObjectId}, + options::FindOptions, + Collection, + }, +}; +use serde::{de::DeserializeOwned, Serialize}; + +use crate::{ + config::core_config, + helpers::query::{ + get_user_user_groups, id_or_name_filter, user_target_query, + }, + state::{ + action_state_cache, action_states, build_state_cache, db_client, + deployment_status_cache, procedure_state_cache, repo_state_cache, + repo_status_cache, resource_sync_state_cache, + server_status_cache, stack_status_cache, + }, +}; + +use super::ResourceBase; + +pub trait ResourceQuery: ResourceBase { + type Config: Default + + Send + + Sync + + Serialize + + DeserializeOwned + + 'static; + type Info: Default + + Send + + Sync + + Serialize + + DeserializeOwned + + 'static; + type ListItem: Send; + type QuerySpecifics: AddFilters + Default + std::fmt::Debug; + + fn coll() -> &'static Collection>; + + async fn to_list_item( + resource: Resource, + ) -> Self::ListItem; +} + +#[instrument(level = "debug")] +pub async fn get( + id_or_name: &str, +) -> anyhow::Result> { + R::coll() + .find_one(id_or_name_filter(id_or_name)) + .await + .context("failed to query db for resource")? + .with_context(|| { + format!( + "did not find any {} matching {id_or_name}", + R::resource_type() + ) + }) +} + +#[instrument(level = "debug")] +pub async fn get_check_permissions( + id_or_name: &str, + user: &User, + permission_level: PermissionLevel, +) -> anyhow::Result> { + let resource = get::(id_or_name).await?; + if user.admin + // Allow if its just read or below, and transparent mode enabled + || (permission_level <= PermissionLevel::Read + && core_config().transparent_mode) + // Allow if resource has base permission level greater than or equal to required permission level + || resource.base_permission >= permission_level + { + return Ok(resource); + } + let permissions = + get_user_permission_on_resource::(user, &resource.id).await?; + if permissions >= permission_level { + Ok(resource) + } else { + Err(anyhow!( + "User does not have required permissions on this {}. Must have at least {permission_level} permissions", + R::resource_type() + )) + } +} + +#[instrument(level = "debug")] +pub async fn get_user_permission_on_resource( + user: &User, + resource_id: &str, +) -> anyhow::Result { + if user.admin { + return Ok(PermissionLevel::Write); + } + + let resource_type = R::resource_type(); + + // Start with base of Read or None + let mut base = if core_config().transparent_mode { + PermissionLevel::Read + } else { + PermissionLevel::None + }; + + // Add in the resource level global base permission + let resource_base = get::(resource_id).await?.base_permission; + if resource_base > base { + base = resource_base; + } + + // Overlay users base on resource variant + if let Some(level) = user.all.get(&resource_type).cloned() { + if level > base { + base = level; + } + } + if base == PermissionLevel::Write { + // No reason to keep going if already Write at this point. + return Ok(PermissionLevel::Write); + } + + // Overlay any user groups base on resource variant + let groups = get_user_user_groups(&user.id).await?; + for group in &groups { + if let Some(level) = group.all.get(&resource_type).cloned() { + if level > base { + base = level; + } + } + } + if base == PermissionLevel::Write { + // No reason to keep going if already Write at this point. + return Ok(PermissionLevel::Write); + } + + // Overlay any specific permissions + let permission = find_collect( + &db_client().permissions, + doc! { + + + "$or": user_target_query(&user.id, &groups)?, + "resource_target.type": resource_type.as_ref(), + "resource_target.id": resource_id + }, + None, + ) + .await + .context("failed to query db for permissions")? + .into_iter() + // get the max permission user has between personal / any user groups + .fold(base, |level, permission| { + if permission.level > level { + permission.level + } else { + level + } + }); + Ok(permission) +} + +// ====== +// LIST +// ====== + +/// Returns None if still no need to filter by resource id (eg transparent mode, group membership with all access). +#[instrument(level = "debug")] +pub async fn get_resource_object_ids_for_user( + user: &User, +) -> anyhow::Result>> { + get_resource_ids_for_user::(user).await.map(|ids| { + ids.map(|ids| { + ids + .into_iter() + .flat_map(|id| ObjectId::from_str(&id)) + .collect() + }) + }) +} + +/// Returns None if still no need to filter by resource id (eg transparent mode, group membership with all access). +#[instrument(level = "debug")] +pub async fn get_resource_ids_for_user( + user: &User, +) -> anyhow::Result>> { + // Check admin or transparent mode + if user.admin || core_config().transparent_mode { + return Ok(None); + } + + let resource_type = R::resource_type(); + + // Check user 'all' on variant + if let Some(level) = user.all.get(&resource_type).cloned() { + if level > PermissionLevel::None { + return Ok(None); + } + } + + // Check user groups 'all' on variant + let groups = get_user_user_groups(&user.id).await?; + for group in &groups { + if let Some(level) = group.all.get(&resource_type).cloned() { + if level > PermissionLevel::None { + return Ok(None); + } + } + } + + let (base, perms) = tokio::try_join!( + // Get any resources with non-none base permission, + find_collect( + R::coll(), + doc! { "base_permission": { "$exists": true, "$ne": "None" } }, + None, + ) + .map(|res| res.with_context(|| format!( + "failed to query {resource_type} on db" + ))), + // And any ids using the permissions table + find_collect( + &db_client().permissions, + doc! { + + + "$or": user_target_query(&user.id, &groups)?, + "resource_target.type": resource_type.as_ref(), + "level": { "$exists": true, "$ne": "None" } + }, + None, + ) + .map(|res| res.context("failed to query permissions on db")) + )?; + + // Add specific ids + let ids = perms + .into_iter() + .map(|p| p.resource_target.extract_variant_id().1.to_string()) + // Chain in the ones with non-None base permissions + .chain(base.into_iter().map(|res| res.id)) + // collect into hashset first to remove any duplicates + .collect::>(); + + Ok(Some(ids.into_iter().collect())) +} + +#[instrument(level = "debug")] +pub async fn list_for_user( + mut query: ResourceQuerySchema, + user: &User, + all_tags: &[Tag], +) -> anyhow::Result> { + validate_resource_query_tags(&mut query, all_tags)?; + let mut filters = Document::new(); + query.add_filters(&mut filters); + list_for_user_using_document::(filters, user).await +} + +#[instrument(level = "debug")] +pub async fn list_for_user_using_pattern( + pattern: &str, + query: ResourceQuerySchema, + user: &User, + all_tags: &[Tag], +) -> anyhow::Result> { + let list = list_full_for_user_using_pattern::( + pattern, query, user, all_tags, + ) + .await? + .into_iter() + .map(|resource| R::to_list_item(resource)); + Ok(join_all(list).await) +} + +#[instrument(level = "debug")] +pub async fn list_for_user_using_document( + filters: Document, + user: &User, +) -> anyhow::Result> { + let list = list_full_for_user_using_document::(filters, user) + .await? + .into_iter() + .map(|resource| R::to_list_item(resource)); + Ok(join_all(list).await) +} + +/// Lists full resource matching wildcard syntax, +/// or regex if wrapped with "\\" +/// +/// ## Example +/// ``` +/// let items = list_full_for_user_using_match_string::("foo-*", Default::default(), user, all_tags).await?; +/// let items = list_full_for_user_using_match_string::("\\^foo-.*$\\", Default::default(), user, all_tags).await?; +/// ``` +#[instrument(level = "debug")] +pub async fn list_full_for_user_using_pattern( + pattern: &str, + query: ResourceQuerySchema, + user: &User, + all_tags: &[Tag], +) -> anyhow::Result>> { + let resources = + list_full_for_user::(query, user, all_tags).await?; + + let patterns = parse_string_list(pattern); + let mut names = HashSet::::new(); + + for pattern in patterns { + if pattern.starts_with('\\') && pattern.ends_with('\\') { + let regex = regex::Regex::new(&pattern[1..(pattern.len() - 1)]) + .context("Regex matching string invalid")?; + for resource in &resources { + if regex.is_match(&resource.name) { + names.insert(resource.name.clone()); + } + } + } else { + let wildcard = wildcard::Wildcard::new(pattern.as_bytes()) + .context("Wildcard matching string invalid")?; + for resource in &resources { + if wildcard.is_match(resource.name.as_bytes()) { + names.insert(resource.name.clone()); + } + } + }; + } + + Ok( + resources + .into_iter() + .filter(|resource| names.contains(resource.name.as_str())) + .collect(), + ) +} + +#[instrument(level = "debug")] +pub async fn list_full_for_user( + mut query: ResourceQuerySchema, + user: &User, + all_tags: &[Tag], +) -> anyhow::Result>> { + validate_resource_query_tags(&mut query, all_tags)?; + let mut filters = Document::new(); + query.add_filters(&mut filters); + list_full_for_user_using_document::(filters, user).await +} + +#[instrument(level = "debug")] +pub async fn list_full_for_user_using_document( + mut filters: Document, + user: &User, +) -> anyhow::Result>> { + if let Some(ids) = + get_resource_object_ids_for_user::(user).await? + { + filters.insert("_id", doc! { "$in": ids }); + } + find_collect( + R::coll(), + filters, + FindOptions::builder().sort(doc! { "name": 1 }).build(), + ) + .await + .with_context(|| { + format!("failed to pull {}s from mongo", R::resource_type()) + }) +} + +pub type IdResourceMap = HashMap< + String, + Resource<::Config, ::Info>, +>; + +#[instrument(level = "debug")] +pub async fn get_id_to_resource_map( + id_to_tags: &HashMap, + match_tags: &[String], +) -> anyhow::Result> { + let res = find_collect(R::coll(), None, None) + .await + .with_context(|| { + format!("failed to pull {}s from mongo", R::resource_type()) + })? + .into_iter() + .filter(|resource| { + if match_tags.is_empty() { + return true; + } + for tag in match_tags.iter() { + for resource_tag in &resource.tags { + match ObjectId::from_str(resource_tag) { + Ok(_) => match id_to_tags + .get(resource_tag) + .map(|tag| tag.name.as_str()) + { + Some(name) => { + if tag != name { + return false; + } + } + None => return false, + }, + Err(_) => { + if resource_tag != tag { + return false; + } + } + } + } + } + true + }) + .map(|r| (r.id.clone(), r)) + .collect(); + Ok(res) +} + +#[instrument(level = "debug")] +pub fn validate_resource_query_tags( + query: &mut ResourceQuerySchema, + all_tags: &[Tag], +) -> anyhow::Result<()> { + query.tags = query + .tags + .iter() + .map(|tag| { + all_tags + .iter() + .find(|t| t.name == *tag || t.id == *tag) + .map(|tag| tag.id.clone()) + .with_context(|| { + format!("No tag found matching name or id: {}", tag) + }) + }) + .collect::>>()?; + Ok(()) +} + +// ================= +// IMPLEMENTATIONS +// ================= + +impl ResourceQuery for Server { + type Config = ServerConfig; + type Info = (); + type ListItem = ServerListItem; + type QuerySpecifics = ServerQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().servers + } + + async fn to_list_item( + server: Resource, + ) -> Self::ListItem { + let status = server_status_cache().get(&server.id).await; + ServerListItem { + name: server.name, + id: server.id, + tags: server.tags, + resource_type: ResourceTargetVariant::Server, + info: ServerListItemInfo { + state: status.map(|s| s.state).unwrap_or_default(), + region: server.config.region, + address: server.config.address, + send_unreachable_alerts: server + .config + .send_unreachable_alerts, + send_cpu_alerts: server.config.send_cpu_alerts, + send_mem_alerts: server.config.send_mem_alerts, + send_disk_alerts: server.config.send_disk_alerts, + }, + } + } +} + +impl ResourceQuery for Stack { + type Config = StackConfig; + type Info = StackInfo; + type ListItem = StackListItem; + type QuerySpecifics = StackQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().stacks + } + + async fn to_list_item( + stack: Resource, + ) -> Self::ListItem { + let status = stack_status_cache().get(&stack.id).await; + let state = + status.as_ref().map(|s| s.curr.state).unwrap_or_default(); + let project_name = stack.project_name(false); + let services = status + .as_ref() + .map(|s| { + s.curr + .services + .iter() + .map(|service| StackServiceWithUpdate { + service: service.service.clone(), + image: service.image.clone(), + update_available: service.update_available, + }) + .collect::>() + }) + .unwrap_or_default(); + + // This is only true if it is KNOWN to be true. so other cases are false. + let (project_missing, status) = + if stack.config.server_id.is_empty() + || matches!(state, StackState::Down | StackState::Unknown) + { + (false, None) + } else if let Some(status) = server_status_cache() + .get(&stack.config.server_id) + .await + .as_ref() + { + if let Some(projects) = &status.projects { + if let Some(project) = projects + .iter() + .find(|project| project.name == project_name) + { + (false, project.status.clone()) + } else { + // The project doesn't exist + (true, None) + } + } else { + (false, None) + } + } else { + (false, None) + }; + + StackListItem { + id: stack.id, + name: stack.name, + tags: stack.tags, + resource_type: ResourceTargetVariant::Stack, + info: StackListItemInfo { + state, + status, + services, + project_missing, + file_contents: !stack.config.file_contents.is_empty(), + server_id: stack.config.server_id, + missing_files: stack.info.missing_files, + files_on_host: stack.config.files_on_host, + git_provider: stack.config.git_provider, + repo: stack.config.repo, + branch: stack.config.branch, + latest_hash: stack.info.latest_hash, + deployed_hash: stack.info.deployed_hash, + }, + } + } +} + +impl ResourceQuery for Deployment { + type Config = DeploymentConfig; + type Info = (); + type ListItem = DeploymentListItem; + type QuerySpecifics = DeploymentQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().deployments + } + + async fn to_list_item( + deployment: Resource, + ) -> Self::ListItem { + let status = deployment_status_cache().get(&deployment.id).await; + let (build_image, build_id) = match deployment.config.image { + DeploymentImage::Build { build_id, version } => { + let (build_name, build_id, build_version) = + super::query::get::(&build_id) + .await + .map(|b| (b.name, b.id, b.config.version)) + .unwrap_or(( + String::from("unknown"), + String::new(), + Default::default(), + )); + let version = if version.is_none() { + build_version.to_string() + } else { + version.to_string() + }; + (format!("{build_name}:{version}"), Some(build_id)) + } + DeploymentImage::Image { image } => (image, None), + }; + let (image, update_available) = status + .as_ref() + .and_then(|s| { + s.curr.container.as_ref().map(|c| { + ( + c.image + .clone() + .unwrap_or_else(|| String::from("Unknown")), + s.curr.update_available, + ) + }) + }) + .unwrap_or((build_image, false)); + DeploymentListItem { + name: deployment.name, + id: deployment.id, + tags: deployment.tags, + resource_type: ResourceTargetVariant::Deployment, + info: DeploymentListItemInfo { + state: status + .as_ref() + .map(|s| s.curr.state) + .unwrap_or_default(), + status: status.as_ref().and_then(|s| { + s.curr.container.as_ref().and_then(|c| c.status.to_owned()) + }), + image, + update_available, + server_id: deployment.config.server_id, + build_id, + }, + } + } +} + +impl ResourceQuery for Build { + type Config = BuildConfig; + type Info = BuildInfo; + type ListItem = BuildListItem; + type QuerySpecifics = BuildQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().builds + } + + async fn to_list_item( + build: Resource, + ) -> Self::ListItem { + let state = if action_states() + .build + .get(&build.id) + .await + .map(|s| s.get().map(|s| s.building)) + .transpose() + .ok() + .flatten() + .unwrap_or_default() + { + BuildState::Building + } else { + build_state_cache().get(&build.id).await.unwrap_or_default() + }; + BuildListItem { + name: build.name, + id: build.id, + tags: build.tags, + resource_type: ResourceTargetVariant::Build, + info: BuildListItemInfo { + last_built_at: build.info.last_built_at, + version: build.config.version, + builder_id: build.config.builder_id, + git_provider: build.config.git_provider, + image_registry_domain: build.config.image_registry.domain, + repo: build.config.repo, + branch: build.config.branch, + built_hash: build.info.built_hash, + latest_hash: build.info.latest_hash, + state, + }, + } + } +} + +impl ResourceQuery for Repo { + type Config = RepoConfig; + type Info = RepoInfo; + type ListItem = RepoListItem; + type QuerySpecifics = RepoQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().repos + } + + async fn to_list_item( + repo: Resource, + ) -> Self::ListItem { + let state = get_repo_state(&repo.id).await; + let status = + repo_status_cache().get(&repo.id).await.unwrap_or_default(); + RepoListItem { + name: repo.name, + id: repo.id, + tags: repo.tags, + resource_type: ResourceTargetVariant::Repo, + info: RepoListItemInfo { + server_id: repo.config.server_id, + builder_id: repo.config.builder_id, + last_pulled_at: repo.info.last_pulled_at, + last_built_at: repo.info.last_built_at, + git_provider: repo.config.git_provider, + repo: repo.config.repo, + branch: repo.config.branch, + state, + cloned_hash: status.latest_hash.clone(), + cloned_message: status.latest_message.clone(), + latest_hash: repo.info.latest_hash, + built_hash: repo.info.built_hash, + }, + } + } +} + +async fn get_repo_state(id: &String) -> RepoState { + if let Some(state) = action_states() + .repo + .get(id) + .await + .and_then(|s| { + s.get() + .map(|s| { + if s.cloning { + Some(RepoState::Cloning) + } else if s.pulling { + Some(RepoState::Pulling) + } else if s.building { + Some(RepoState::Building) + } else { + None + } + }) + .ok() + }) + .flatten() + { + return state; + } + repo_state_cache().get(id).await.unwrap_or_default() +} + +impl ResourceQuery for Procedure { + type Config = ProcedureConfig; + type Info = (); + type ListItem = ProcedureListItem; + type QuerySpecifics = ProcedureQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().procedures + } + + async fn to_list_item( + procedure: Resource, + ) -> Self::ListItem { + let state = if action_states() + .procedure + .get(&procedure.id) + .await + .map(|s| s.get().map(|s| s.running)) + .transpose() + .ok() + .flatten() + .unwrap_or_default() + { + ProcedureState::Running + } else { + procedure_state_cache() + .get(&procedure.id) + .await + .unwrap_or_default() + }; + ProcedureListItem { + name: procedure.name, + id: procedure.id, + tags: procedure.tags, + resource_type: ResourceTargetVariant::Procedure, + info: ProcedureListItemInfo { + stages: procedure.config.stages.len() as i64, + state, + }, + } + } +} + +impl ResourceQuery for Action { + type Config = ActionConfig; + type Info = ActionInfo; + type ListItem = ActionListItem; + type QuerySpecifics = ActionQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().actions + } + + async fn to_list_item( + action: Resource, + ) -> Self::ListItem { + let state = if action_states() + .action + .get(&action.id) + .await + .map(|s| s.get().map(|s| s.running)) + .transpose() + .ok() + .flatten() + .unwrap_or_default() + { + ActionState::Running + } else { + action_state_cache() + .get(&action.id) + .await + .unwrap_or_default() + }; + ActionListItem { + name: action.name, + id: action.id, + tags: action.tags, + resource_type: ResourceTargetVariant::Action, + info: ActionListItemInfo { + state, + last_run_at: action.info.last_run_at, + }, + } + } +} + +impl ResourceQuery for Builder { + type Config = BuilderConfig; + type Info = (); + type ListItem = BuilderListItem; + type QuerySpecifics = BuilderQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().builders + } + + async fn to_list_item( + builder: Resource, + ) -> Self::ListItem { + let (builder_type, instance_type) = match builder.config { + BuilderConfig::Url(_) => { + (BuilderConfigVariant::Url.to_string(), None) + } + BuilderConfig::Server(config) => ( + BuilderConfigVariant::Server.to_string(), + Some(config.server_id), + ), + BuilderConfig::Aws(config) => ( + BuilderConfigVariant::Aws.to_string(), + Some(config.instance_type), + ), + }; + BuilderListItem { + name: builder.name, + id: builder.id, + tags: builder.tags, + resource_type: ResourceTargetVariant::Builder, + info: BuilderListItemInfo { + builder_type, + instance_type, + }, + } + } +} + +impl ResourceQuery for ServerTemplate { + type Config = ServerTemplateConfig; + type Info = (); + type ListItem = ServerTemplateListItem; + type QuerySpecifics = ServerTemplateQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().server_templates + } + + async fn to_list_item( + server_template: Resource, + ) -> Self::ListItem { + let (template_type, instance_type) = match server_template.config + { + ServerTemplateConfig::Aws(config) => ( + ServerTemplateConfigVariant::Aws.to_string(), + Some(config.instance_type), + ), + ServerTemplateConfig::Hetzner(config) => ( + ServerTemplateConfigVariant::Hetzner.to_string(), + Some(config.server_type.as_ref().to_string()), + ), + }; + ServerTemplateListItem { + name: server_template.name, + id: server_template.id, + tags: server_template.tags, + resource_type: ResourceTargetVariant::ServerTemplate, + info: ServerTemplateListItemInfo { + provider: template_type.to_string(), + instance_type, + }, + } + } +} + +async fn get_resource_sync_state( + id: &String, + data: &ResourceSyncInfo, +) -> ResourceSyncState { + if let Some(state) = action_states() + .resource_sync + .get(id) + .await + .and_then(|s| { + s.get() + .map(|s| { + if s.syncing { + Some(ResourceSyncState::Syncing) + } else { + None + } + }) + .ok() + }) + .flatten() + { + return state; + } + if data.pending_error.is_some() { + return ResourceSyncState::Failed; + } + if !data.resource_updates.is_empty() + || !data.variable_updates.is_empty() + || !data.user_group_updates.is_empty() + || data.pending_deploy.to_deploy > 0 + { + return ResourceSyncState::Pending; + } + resource_sync_state_cache() + .get(id) + .await + .unwrap_or_default() +} + +impl ResourceQuery for ResourceSync { + type Config = ResourceSyncConfig; + type Info = ResourceSyncInfo; + type ListItem = ResourceSyncListItem; + type QuerySpecifics = ResourceSyncQuerySpecifics; + + fn coll() -> &'static Collection> + { + &db_client().resource_syncs + } + + async fn to_list_item( + resource_sync: Resource, + ) -> Self::ListItem { + let state = + get_resource_sync_state(&resource_sync.id, &resource_sync.info) + .await; + ResourceSyncListItem { + id: resource_sync.id, + name: resource_sync.name, + tags: resource_sync.tags, + resource_type: ResourceTargetVariant::ResourceSync, + info: ResourceSyncListItemInfo { + file_contents: !resource_sync.config.file_contents.is_empty(), + files_on_host: resource_sync.config.files_on_host, + managed: resource_sync.config.managed, + git_provider: resource_sync.config.git_provider, + repo: resource_sync.config.repo, + branch: resource_sync.config.branch, + last_sync_ts: resource_sync.info.last_sync_ts, + last_sync_hash: resource_sync.info.last_sync_hash, + last_sync_message: resource_sync.info.last_sync_message, + resource_path: resource_sync.config.resource_path, + state, + }, + } + } +} diff --git a/bin/core/src/state.rs b/bin/core/src/state.rs index df9eefd19..dfd8898d0 100644 --- a/bin/core/src/state.rs +++ b/bin/core/src/state.rs @@ -29,8 +29,6 @@ use crate::{ }, }; -pub struct State; - static DB_CLIENT: OnceLock = OnceLock::new(); pub fn db_client() -> &'static DbClient { diff --git a/bin/core/src/sync/deploy.rs b/bin/core/src/sync/deploy.rs index 29fbf5422..bbe303446 100644 --- a/bin/core/src/sync/deploy.rs +++ b/bin/core/src/sync/deploy.rs @@ -24,9 +24,12 @@ use komodo_client::{ use resolver_api::Resolve; use crate::{ - api::execute::ExecuteRequest, + api::{ + execute::{ExecuteArgs, ExecuteRequest}, + read::ReadArgs, + }, helpers::update::init_execution_update, - state::{deployment_status_cache, stack_status_cache, State}, + state::{deployment_status_cache, stack_status_cache}, }; use super::{AllResourcesById, ResourceSyncTrait}; @@ -92,7 +95,12 @@ pub async fn deploy_from_cache( let ExecuteRequest::Deploy(req) = req else { unreachable!() }; - State.resolve(req, (user.to_owned(), update)).await + req + .resolve(&ExecuteArgs { + user: user.to_owned(), + update, + }) + .await } ResourceTarget::Stack(name) => { let req = ExecuteRequest::DeployStack(DeployStack { @@ -105,7 +113,12 @@ pub async fn deploy_from_cache( let ExecuteRequest::DeployStack(req) = req else { unreachable!() }; - State.resolve(req, (user.to_owned(), update)).await + req + .resolve(&ExecuteArgs { + user: user.to_owned(), + update, + }) + .await } _ => unreachable!(), } @@ -124,10 +137,11 @@ pub async fn deploy_from_cache( if let Err(e) = res { has_error = true; log.push_str(&format!( - "\n{}: failed to deploy {resource} '{}' in round {} | {e:#}", + "\n{}: failed to deploy {resource} '{}' in round {} | {:#}", colored("ERROR", Color::Red), bold(name), - bold(round) + bold(round), + e.error )); } else { log.push_str(&format!( @@ -426,19 +440,18 @@ fn build_cache_for_deployment<'a>( // Build version is the same, still need to check 'after' Some(_) => {} None => { - let Some(version) = State - .resolve( - ListBuildVersions { - build: build_id.to_string(), - limit: Some(1), - ..Default::default() - }, - sync_user().to_owned(), - ) - .await - .context("failed to get build versions")? - .pop() - else { + let Some(version) = (ListBuildVersions { + build: build_id.to_string(), + limit: Some(1), + ..Default::default() + }) + .resolve(&ReadArgs { + user: sync_user().to_owned(), + }) + .await + .map_err(|e| e.error) + .context("failed to get build versions")? + .pop() else { // The build has never been built. // Skip deploy regardless of 'after' (it can't be deployed) // Not sure how this would be reached on Running deployment... diff --git a/bin/core/src/sync/execute.rs b/bin/core/src/sync/execute.rs index 8ab030357..78a1bc898 100644 --- a/bin/core/src/sync/execute.rs +++ b/bin/core/src/sync/execute.rs @@ -13,7 +13,7 @@ use mungos::find::find_collect; use partial_derive2::MaybeNone; use resolver_api::Resolve; -use crate::state::State; +use crate::api::write::WriteArgs; use super::{ AllResourcesById, ResourceSyncTrait, ToCreate, ToDelete, ToUpdate, @@ -256,8 +256,13 @@ pub trait ExecuteResourceSync: ResourceSyncTrait { } for resource in to_delete { - if let Err(e) = - crate::resource::delete::(&resource, sync_user()).await + if let Err(e) = crate::resource::delete::( + &resource, + &WriteArgs { + user: sync_user().to_owned(), + }, + ) + .await { has_error = true; log.push_str(&format!( @@ -294,22 +299,22 @@ pub async fn run_update_tags( has_error: &mut bool, ) { // Update tags - if let Err(e) = State - .resolve( - UpdateTagsOnResource { - target: Resource::resource_target(id), - tags, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (UpdateTagsOnResource { + target: Resource::resource_target(id), + tags, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { *has_error = true; log.push_str(&format!( - "\n{}: failed to update tags on {} '{}' | {e:#}", + "\n{}: failed to update tags on {} '{}' | {:#}", colored("ERROR", Color::Red), Resource::resource_type(), bold(name), + e.error )) } else { log.push_str(&format!( @@ -329,22 +334,22 @@ pub async fn run_update_description( log: &mut String, has_error: &mut bool, ) { - if let Err(e) = State - .resolve( - UpdateDescription { - target: Resource::resource_target(id.clone()), - description, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (UpdateDescription { + target: Resource::resource_target(id.clone()), + description, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { *has_error = true; log.push_str(&format!( - "\n{}: failed to update description on {} '{}' | {e:#}", + "\n{}: failed to update description on {} '{}' | {:#}", colored("ERROR", Color::Red), Resource::resource_type(), bold(name), + e.error )) } else { log.push_str(&format!( diff --git a/bin/core/src/sync/resources.rs b/bin/core/src/sync/resources.rs index aa69c9731..cef792512 100644 --- a/bin/core/src/sync/resources.rs +++ b/bin/core/src/sync/resources.rs @@ -24,6 +24,7 @@ use komodo_client::{ use partial_derive2::{MaybeNone, PartialDiff}; use crate::{ + api::write::WriteArgs, resource::KomodoResource, sync::{ execute::{run_update_description, run_update_tags}, @@ -726,8 +727,13 @@ impl ExecuteResourceSync for Procedure { format!("running updates on {}s", Self::resource_type()); for name in to_delete { - if let Err(e) = - crate::resource::delete::(&name, sync_user()).await + if let Err(e) = crate::resource::delete::( + &name, + &WriteArgs { + user: sync_user().to_owned(), + }, + ) + .await { has_error = true; log.push_str(&format!( diff --git a/bin/core/src/sync/user_groups.rs b/bin/core/src/sync/user_groups.rs index 7a00bd82a..6ca7a65b5 100644 --- a/bin/core/src/sync/user_groups.rs +++ b/bin/core/src/sync/user_groups.rs @@ -24,7 +24,10 @@ use mungos::find::find_collect; use regex::Regex; use resolver_api::Resolve; -use crate::state::{db_client, State}; +use crate::{ + api::{read::ReadArgs, write::WriteArgs}, + state::db_client, +}; use super::{toml::TOML_PRETTY_OPTIONS, AllResourcesById}; @@ -211,105 +214,105 @@ pub async fn get_updates_for_execution( }) .collect::>(); - let mut original_permissions = State - .resolve( - ListUserTargetPermissions { - user_target: UserTarget::UserGroup(original.id), - }, - sync_user().to_owned(), - ) - .await - .context("failed to query for existing UserGroup permissions")? - .into_iter() - .filter(|p| p.level > PermissionLevel::None) - .map(|mut p| { - // replace the ids with names - match &mut p.resource_target { - ResourceTarget::System(_) => {} - ResourceTarget::Build(id) => { - *id = all_resources - .builds - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Builder(id) => { - *id = all_resources - .builders - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Deployment(id) => { - *id = all_resources - .deployments - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Server(id) => { - *id = all_resources - .servers - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Repo(id) => { - *id = all_resources - .repos - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Alerter(id) => { - *id = all_resources - .alerters - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Procedure(id) => { - *id = all_resources - .procedures - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Action(id) => { - *id = all_resources - .actions - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::ServerTemplate(id) => { - *id = all_resources - .templates - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::ResourceSync(id) => { - *id = all_resources - .syncs - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Stack(id) => { - *id = all_resources - .stacks - .get(id) - .map(|b| b.name.clone()) - .unwrap_or_default() - } + let mut original_permissions = (ListUserTargetPermissions { + user_target: UserTarget::UserGroup(original.id), + }) + .resolve(&ReadArgs { + user: sync_user().to_owned(), + }) + .await + .map_err(|e| e.error) + .context("failed to query for existing UserGroup permissions")? + .into_iter() + .filter(|p| p.level > PermissionLevel::None) + .map(|mut p| { + // replace the ids with names + match &mut p.resource_target { + ResourceTarget::System(_) => {} + ResourceTarget::Build(id) => { + *id = all_resources + .builds + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() } - PermissionToml { - target: p.resource_target, - level: p.level, + ResourceTarget::Builder(id) => { + *id = all_resources + .builders + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() } - }) - .collect::>(); + ResourceTarget::Deployment(id) => { + *id = all_resources + .deployments + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Server(id) => { + *id = all_resources + .servers + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Repo(id) => { + *id = all_resources + .repos + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Alerter(id) => { + *id = all_resources + .alerters + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Procedure(id) => { + *id = all_resources + .procedures + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Action(id) => { + *id = all_resources + .actions + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::ServerTemplate(id) => { + *id = all_resources + .templates + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::ResourceSync(id) => { + *id = all_resources + .syncs + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Stack(id) => { + *id = all_resources + .stacks + .get(id) + .map(|b| b.name.clone()) + .unwrap_or_default() + } + } + PermissionToml { + target: p.resource_target, + level: p.level, + } + }) + .collect::>(); original_users.sort(); user_group.users.sort(); @@ -404,20 +407,20 @@ pub async fn run_updates( // Create the non-existant user groups for user_group in to_create { // Create the user group - if let Err(e) = State - .resolve( - CreateUserGroup { - name: user_group.name.clone(), - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (CreateUserGroup { + name: user_group.name.clone(), + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { has_error = true; log.push_str(&format!( - "\n{}: failed to create user group '{}' | {e:#}", + "\n{}: failed to create user group '{}' | {:#}", colored("ERROR", Color::Red), - bold(&user_group.name) + bold(&user_group.name), + e.error )); continue; } else { @@ -489,18 +492,18 @@ pub async fn run_updates( } for user_group in to_delete { - if let Err(e) = State - .resolve( - DeleteUserGroup { id: user_group.id }, - sync_user().to_owned(), - ) + if let Err(e) = (DeleteUserGroup { id: user_group.id }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) .await { has_error = true; log.push_str(&format!( - "\n{}: failed to delete user group '{}' | {e:#}", + "\n{}: failed to delete user group '{}' | {:#}", colored("ERROR", Color::Red), - bold(&user_group.name) + bold(&user_group.name), + e.error )) } else { log.push_str(&format!( @@ -526,21 +529,21 @@ async fn set_users( log: &mut String, has_error: &mut bool, ) { - if let Err(e) = State - .resolve( - SetUsersInUserGroup { - user_group: user_group.clone(), - users, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (SetUsersInUserGroup { + user_group: user_group.clone(), + users, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { *has_error = true; log.push_str(&format!( - "\n{}: failed to set users in group {} | {e:#}", + "\n{}: failed to set users in group {} | {:#}", colored("ERROR", Color::Red), - bold(&user_group) + bold(&user_group), + e.error )) } else { log.push_str(&format!( @@ -559,22 +562,22 @@ async fn run_update_all( has_error: &mut bool, ) { for (resource_type, permission) in all_diff { - if let Err(e) = State - .resolve( - UpdatePermissionOnResourceType { - user_target: UserTarget::UserGroup(user_group.clone()), - resource_type, - permission, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (UpdatePermissionOnResourceType { + user_target: UserTarget::UserGroup(user_group.clone()), + resource_type, + permission, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { *has_error = true; log.push_str(&format!( - "\n{}: failed to set base permissions on {resource_type} in group {} | {e:#}", + "\n{}: failed to set base permissions on {resource_type} in group {} | {:#}", colored("ERROR", Color::Red), - bold(&user_group) + bold(&user_group), + e.error )) } else { log.push_str(&format!( @@ -594,22 +597,22 @@ async fn run_update_permissions( has_error: &mut bool, ) { for PermissionToml { target, level } in permissions { - if let Err(e) = State - .resolve( - UpdatePermissionOnTarget { - user_target: UserTarget::UserGroup(user_group.clone()), - resource_target: target.clone(), - permission: level, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (UpdatePermissionOnTarget { + user_target: UserTarget::UserGroup(user_group.clone()), + resource_target: target.clone(), + permission: level, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { *has_error = true; log.push_str(&format!( - "\n{}: failed to set permission in group {} | target: {target:?} | {e:#}", + "\n{}: failed to set permission in group {} | target: {target:?} | {:#}", colored("ERROR", Color::Red), - bold(&user_group) + bold(&user_group), + e.error )) } else { log.push_str(&format!( @@ -830,105 +833,105 @@ pub async fn convert_user_groups( for user_group in user_groups { // this method is admin only, but we already know user can see user group if above does not return Err - let mut permissions = State - .resolve( - ListUserTargetPermissions { - user_target: UserTarget::UserGroup(user_group.id.clone()), - }, - User { - admin: true, - ..Default::default() - }, - ) - .await? - .into_iter() - .map(|mut permission| { - match &mut permission.resource_target { - ResourceTarget::Build(id) => { - *id = all - .builds - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Builder(id) => { - *id = all - .builders - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Deployment(id) => { - *id = all - .deployments - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Server(id) => { - *id = all - .servers - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Repo(id) => { - *id = all - .repos - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Alerter(id) => { - *id = all - .alerters - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Procedure(id) => { - *id = all - .procedures - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Action(id) => { - *id = all - .actions - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::ServerTemplate(id) => { - *id = all - .templates - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::ResourceSync(id) => { - *id = all - .syncs - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::Stack(id) => { - *id = all - .stacks - .get(id) - .map(|r| r.name.clone()) - .unwrap_or_default() - } - ResourceTarget::System(_) => {} + let mut permissions = (ListUserTargetPermissions { + user_target: UserTarget::UserGroup(user_group.id.clone()), + }) + .resolve(&ReadArgs { + user: User { + admin: true, + ..Default::default() + }, + }) + .await + .map_err(|e| e.error)? + .into_iter() + .map(|mut permission| { + match &mut permission.resource_target { + ResourceTarget::Build(id) => { + *id = all + .builds + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() } - PermissionToml { - target: permission.resource_target, - level: permission.level, + ResourceTarget::Builder(id) => { + *id = all + .builders + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() } - }) - .collect::>(); + ResourceTarget::Deployment(id) => { + *id = all + .deployments + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Server(id) => { + *id = all + .servers + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Repo(id) => { + *id = all + .repos + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Alerter(id) => { + *id = all + .alerters + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Procedure(id) => { + *id = all + .procedures + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Action(id) => { + *id = all + .actions + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::ServerTemplate(id) => { + *id = all + .templates + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::ResourceSync(id) => { + *id = all + .syncs + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::Stack(id) => { + *id = all + .stacks + .get(id) + .map(|r| r.name.clone()) + .unwrap_or_default() + } + ResourceTarget::System(_) => {} + } + PermissionToml { + target: permission.resource_target, + level: permission.level, + } + }) + .collect::>(); let mut users = user_group .users .into_iter() diff --git a/bin/core/src/sync/variables.rs b/bin/core/src/sync/variables.rs index a251808f2..b447dc49d 100644 --- a/bin/core/src/sync/variables.rs +++ b/bin/core/src/sync/variables.rs @@ -3,10 +3,7 @@ use std::collections::HashMap; use anyhow::Context; use formatting::{bold, colored, muted, Color}; use komodo_client::{ - api::write::{ - CreateVariable, DeleteVariable, UpdateVariableDescription, - UpdateVariableIsSecret, UpdateVariableValue, - }, + api::write::*, entities::{ sync::DiffData, update::Log, user::sync_user, variable::Variable, }, @@ -14,7 +11,7 @@ use komodo_client::{ use mungos::find::find_collect; use resolver_api::Resolve; -use crate::state::{db_client, State}; +use crate::{api::write::WriteArgs, state::db_client}; use super::toml::TOML_PRETTY_OPTIONS; @@ -154,23 +151,23 @@ pub async fn run_updates( let mut log = String::from("running updates on Variables"); for variable in to_create { - if let Err(e) = State - .resolve( - CreateVariable { - name: variable.name.clone(), - value: variable.value, - description: variable.description, - is_secret: variable.is_secret, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (CreateVariable { + name: variable.name.clone(), + value: variable.value, + description: variable.description, + is_secret: variable.is_secret, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { has_error = true; log.push_str(&format!( - "\n{}: failed to create variable '{}' | {e:#}", + "\n{}: failed to create variable '{}' | {:#}", colored("ERROR", Color::Red), - bold(&variable.name) + bold(&variable.name), + e.error )); } else { log.push_str(&format!( @@ -190,21 +187,21 @@ pub async fn run_updates( } in to_update { if update_value { - if let Err(e) = State - .resolve( - UpdateVariableValue { - name: variable.name.clone(), - value: variable.value, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (UpdateVariableValue { + name: variable.name.clone(), + value: variable.value, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { has_error = true; log.push_str(&format!( - "\n{}: failed to update variable value for '{}' | {e:#}", + "\n{}: failed to update variable value for '{}' | {:#}", colored("ERROR", Color::Red), - bold(&variable.name) + bold(&variable.name), + e.error )) } else { log.push_str(&format!( @@ -216,21 +213,21 @@ pub async fn run_updates( }; } if update_description { - if let Err(e) = State - .resolve( - UpdateVariableDescription { - name: variable.name.clone(), - description: variable.description, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (UpdateVariableDescription { + name: variable.name.clone(), + description: variable.description, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { has_error = true; log.push_str(&format!( - "\n{}: failed to update variable description for '{}' | {e:#}", + "\n{}: failed to update variable description for '{}' | {:#}", colored("ERROR", Color::Red), - bold(&variable.name) + bold(&variable.name), + e.error )) } else { log.push_str(&format!( @@ -242,21 +239,21 @@ pub async fn run_updates( }; } if update_is_secret { - if let Err(e) = State - .resolve( - UpdateVariableIsSecret { - name: variable.name.clone(), - is_secret: variable.is_secret, - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (UpdateVariableIsSecret { + name: variable.name.clone(), + is_secret: variable.is_secret, + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { has_error = true; log.push_str(&format!( - "\n{}: failed to update variable is secret for '{}' | {e:#}", + "\n{}: failed to update variable is secret for '{}' | {:#}", colored("ERROR", Color::Red), - bold(&variable.name) + bold(&variable.name), + e.error, )) } else { log.push_str(&format!( @@ -270,20 +267,20 @@ pub async fn run_updates( } for variable in to_delete { - if let Err(e) = State - .resolve( - DeleteVariable { - name: variable.clone(), - }, - sync_user().to_owned(), - ) - .await + if let Err(e) = (DeleteVariable { + name: variable.clone(), + }) + .resolve(&WriteArgs { + user: sync_user().to_owned(), + }) + .await { has_error = true; log.push_str(&format!( - "\n{}: failed to delete variable '{}' | {e:#}", + "\n{}: failed to delete variable '{}' | {:#}", colored("ERROR", Color::Red), - bold(&variable) + bold(&variable), + e.error )) } else { log.push_str(&format!( diff --git a/bin/periphery/Cargo.toml b/bin/periphery/Cargo.toml index d7021f304..7ba93d19a 100644 --- a/bin/periphery/Cargo.toml +++ b/bin/periphery/Cargo.toml @@ -19,6 +19,7 @@ komodo_client.workspace = true periphery_client.workspace = true environment_file.workspace = true formatting.workspace = true +response.workspace = true command.workspace = true logger.workspace = true cache.workspace = true @@ -33,7 +34,6 @@ run_command.workspace = true svi.workspace = true # external axum-server.workspace = true -axum-extra.workspace = true serde_json.workspace = true futures.workspace = true tracing.workspace = true diff --git a/bin/periphery/src/api/build.rs b/bin/periphery/src/api/build.rs index 57852525c..504585dfd 100644 --- a/bin/periphery/src/api/build.rs +++ b/bin/periphery/src/api/build.rs @@ -20,21 +20,20 @@ use crate::{ config::periphery_config, docker::docker_login, helpers::{parse_extra_args, parse_labels}, - State, }; -impl Resolve for State { - #[instrument(name = "Build", skip_all)] +impl Resolve for build::Build { + #[instrument(name = "Build", skip_all, fields(build = self.build.name.to_string()))] async fn resolve( - &self, - build::Build { + self, + _: &super::Args, + ) -> serror::Result> { + let build::Build { build, registry_token, additional_tags, replacers: core_replacers, - }: build::Build, - _: (), - ) -> anyhow::Result> { + } = self; let Build { name, config: @@ -247,13 +246,9 @@ fn cleanup_secret_env_vars(secret_args: &[EnvironmentVar]) { // -impl Resolve for State { - #[instrument(name = "PruneBuilders", skip(self))] - async fn resolve( - &self, - _: PruneBuilders, - _: (), - ) -> anyhow::Result { +impl Resolve for PruneBuilders { + #[instrument(name = "PruneBuilders", skip_all)] + async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker builder prune -a -f"); Ok( run_komodo_command("prune builders", None, command, false) @@ -264,13 +259,9 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "PruneBuildx", skip(self))] - async fn resolve( - &self, - _: PruneBuildx, - _: (), - ) -> anyhow::Result { +impl Resolve for PruneBuildx { + #[instrument(name = "PruneBuildx", skip_all)] + async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker buildx prune -a -f"); Ok(run_komodo_command("prune buildx", None, command, false).await) } diff --git a/bin/periphery/src/api/compose.rs b/bin/periphery/src/api/compose.rs index 6ea86a9ee..a5a28a209 100644 --- a/bin/periphery/src/api/compose.rs +++ b/bin/periphery/src/api/compose.rs @@ -17,16 +17,14 @@ use crate::{ config::periphery_config, docker::docker_login, helpers::{log_grep, pull_or_clone_stack}, - State, }; -impl Resolve for State { - #[instrument(name = "ComposeInfo", level = "debug", skip(self))] +impl Resolve for ListComposeProjects { + #[instrument(name = "ComposeInfo", level = "debug", skip_all)] async fn resolve( - &self, - ListComposeProjects {}: ListComposeProjects, - _: (), - ) -> anyhow::Result> { + self, + _: &super::Args, + ) -> serror::Result> { let docker_compose = docker_compose(); let res = run_komodo_command( "list projects", @@ -37,9 +35,13 @@ impl Resolve for State { .await; if !res.success { - return Err(anyhow!("{}", res.combined()).context(format!( + return Err( + anyhow!("{}", res.combined()) + .context(format!( "failed to list compose projects using {docker_compose} ls" - ))); + )) + .into(), + ); } let res = @@ -80,22 +82,15 @@ pub struct DockerComposeLsItem { // -impl Resolve for State { - #[instrument( - name = "GetComposeServiceLog", - level = "debug", - skip(self) - )] - async fn resolve( - &self, - GetComposeServiceLog { +impl Resolve for GetComposeServiceLog { + #[instrument(name = "GetComposeServiceLog", level = "debug")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let GetComposeServiceLog { project, service, tail, timestamps, - }: GetComposeServiceLog, - _: (), - ) -> anyhow::Result { + } = self; let docker_compose = docker_compose(); let timestamps = timestamps.then_some(" --timestamps").unwrap_or_default(); @@ -108,24 +103,17 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument( - name = "GetComposeServiceLogSearch", - level = "debug", - skip(self) - )] - async fn resolve( - &self, - GetComposeServiceLogSearch { +impl Resolve for GetComposeServiceLogSearch { + #[instrument(name = "GetComposeServiceLogSearch", level = "debug")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let GetComposeServiceLogSearch { project, service, terms, combinator, invert, timestamps, - }: GetComposeServiceLogSearch, - _: (), - ) -> anyhow::Result { + } = self; let docker_compose = docker_compose(); let grep = log_grep(&terms, combinator, invert); let timestamps = @@ -140,21 +128,17 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument( - name = "GetComposeContentsOnHost", - level = "debug", - skip(self) - )] +impl Resolve for GetComposeContentsOnHost { + #[instrument(name = "GetComposeContentsOnHost", level = "debug")] async fn resolve( - &self, - GetComposeContentsOnHost { + self, + _: &super::Args, + ) -> serror::Result { + let GetComposeContentsOnHost { name, run_directory, file_paths, - }: GetComposeContentsOnHost, - _: (), - ) -> anyhow::Result { + } = self; let root = periphery_config().stack_dir.join(to_komodo_name(&name)); let run_directory = @@ -196,18 +180,15 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "WriteComposeContentsToHost", skip(self))] - async fn resolve( - &self, - WriteComposeContentsToHost { +impl Resolve for WriteComposeContentsToHost { + #[instrument(name = "WriteComposeContentsToHost")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let WriteComposeContentsToHost { name, run_directory, file_path, contents, - }: WriteComposeContentsToHost, - _: (), - ) -> anyhow::Result { + } = self; let file_path = periphery_config() .stack_dir .join(to_komodo_name(&name)) @@ -233,19 +214,28 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "WriteCommitComposeContents", skip(self))] +impl Resolve for WriteCommitComposeContents { + #[instrument( + name = "WriteCommitComposeContents", + skip_all, + fields( + stack = &self.stack.name, + username = &self.username, + file_path = &self.file_path, + ) + )] async fn resolve( - &self, - WriteCommitComposeContents { + self, + _: &super::Args, + ) -> serror::Result { + let WriteCommitComposeContents { stack, username, file_path, contents, git_token, - }: WriteCommitComposeContents, - _: (), - ) -> anyhow::Result { + } = self; + let root = pull_or_clone_stack(&stack, git_token).await?; let file_path = stack @@ -285,21 +275,25 @@ impl<'a> WriteStackRes for &'a mut ComposePullResponse { } } -impl Resolve for State { +impl Resolve for ComposePull { #[instrument( name = "ComposePull", - skip(self, git_token, registry_token) + skip_all, + fields( + stack = &self.stack.name, + service = &self.service, + ) )] async fn resolve( - &self, - ComposePull { + self, + _: &super::Args, + ) -> serror::Result { + let ComposePull { stack, service, git_token, registry_token, - }: ComposePull, - _: (), - ) -> anyhow::Result { + } = self; let mut res = ComposePullResponse::default(); let (run_directory, env_file_path) = write_stack(&stack, git_token, &mut res).await?; @@ -323,7 +317,7 @@ impl Resolve for State { for (path, full_path) in &file_paths { if !full_path.exists() { - return Err(anyhow!("Missing compose file at {path}")); + return Err(anyhow!("Missing compose file at {path}").into()); } } @@ -392,22 +386,26 @@ impl Resolve for State { // -impl Resolve for State { +impl Resolve for ComposeUp { #[instrument( name = "ComposeUp", - skip(self, git_token, registry_token) + skip_all, + fields( + stack = &self.stack.name, + service = &self.service, + ) )] async fn resolve( - &self, - ComposeUp { + self, + _: &super::Args, + ) -> serror::Result { + let ComposeUp { stack, service, git_token, registry_token, replacers, - }: ComposeUp, - _: (), - ) -> anyhow::Result { + } = self; let mut res = ComposeUpResponse::default(); if let Err(e) = compose_up( stack, @@ -430,13 +428,10 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "ComposeExecution", skip(self))] - async fn resolve( - &self, - ComposeExecution { project, command }: ComposeExecution, - _: (), - ) -> anyhow::Result { +impl Resolve for ComposeExecution { + #[instrument(name = "ComposeExecution")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let ComposeExecution { project, command } = self; let docker_compose = docker_compose(); let log = run_komodo_command( "compose command", diff --git a/bin/periphery/src/api/container.rs b/bin/periphery/src/api/container.rs index 8ad785e67..a4e51858b 100644 --- a/bin/periphery/src/api/container.rs +++ b/bin/periphery/src/api/container.rs @@ -12,7 +12,6 @@ use resolver_api::Resolve; use crate::{ docker::{container_stats, docker_client, stop_container_command}, helpers::log_grep, - State, }; // ====== @@ -21,34 +20,26 @@ use crate::{ // -impl Resolve for State { - #[instrument( - name = "InspectContainer", - level = "debug", - skip(self) - )] +impl Resolve for InspectContainer { + #[instrument(name = "InspectContainer", level = "debug")] async fn resolve( - &self, - InspectContainer { name }: InspectContainer, - _: (), - ) -> anyhow::Result { - docker_client().inspect_container(&name).await + self, + _: &super::Args, + ) -> serror::Result { + Ok(docker_client().inspect_container(&self.name).await?) } } // -impl Resolve for State { - #[instrument(name = "GetContainerLog", level = "debug", skip(self))] - async fn resolve( - &self, - GetContainerLog { +impl Resolve for GetContainerLog { + #[instrument(name = "GetContainerLog", level = "debug")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let GetContainerLog { name, tail, timestamps, - }: GetContainerLog, - _: (), - ) -> anyhow::Result { + } = self; let timestamps = timestamps.then_some(" --timestamps").unwrap_or_default(); let command = @@ -62,23 +53,16 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument( - name = "GetContainerLogSearch", - level = "debug", - skip(self) - )] - async fn resolve( - &self, - GetContainerLogSearch { +impl Resolve for GetContainerLogSearch { + #[instrument(name = "GetContainerLogSearch", level = "debug")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let GetContainerLogSearch { name, terms, combinator, invert, timestamps, - }: GetContainerLogSearch, - _: (), - ) -> anyhow::Result { + } = self; let grep = log_grep(&terms, combinator, invert); let timestamps = timestamps.then_some(" --timestamps").unwrap_or_default(); @@ -99,19 +83,14 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument( - name = "GetContainerStats", - level = "debug", - skip(self) - )] +impl Resolve for GetContainerStats { + #[instrument(name = "GetContainerStats", level = "debug")] async fn resolve( - &self, - req: GetContainerStats, - _: (), - ) -> anyhow::Result { - let error = anyhow!("no stats matching {}", req.name); - let mut stats = container_stats(Some(req.name)).await?; + self, + _: &super::Args, + ) -> serror::Result { + let error = anyhow!("no stats matching {}", self.name); + let mut stats = container_stats(Some(self.name)).await?; let stats = stats.pop().ok_or(error)?; Ok(stats) } @@ -119,18 +98,13 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument( - name = "GetContainerStatsList", - level = "debug", - skip(self) - )] +impl Resolve for GetContainerStatsList { + #[instrument(name = "GetContainerStatsList", level = "debug")] async fn resolve( - &self, - _: GetContainerStatsList, - _: (), - ) -> anyhow::Result> { - container_stats(None).await + self, + _: &super::Args, + ) -> serror::Result> { + Ok(container_stats(None).await?) } } @@ -138,18 +112,14 @@ impl Resolve for State { // ACTIONS // ========= -impl Resolve for State { - #[instrument(name = "StartContainer", skip(self))] - async fn resolve( - &self, - StartContainer { name }: StartContainer, - _: (), - ) -> anyhow::Result { +impl Resolve for StartContainer { + #[instrument(name = "StartContainer")] + async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "docker start", None, - format!("docker start {name}"), + format!("docker start {}", self.name), false, ) .await, @@ -159,18 +129,14 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "RestartContainer", skip(self))] - async fn resolve( - &self, - RestartContainer { name }: RestartContainer, - _: (), - ) -> anyhow::Result { +impl Resolve for RestartContainer { + #[instrument(name = "RestartContainer")] + async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "docker restart", None, - format!("docker restart {name}"), + format!("docker restart {}", self.name), false, ) .await, @@ -180,18 +146,14 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "PauseContainer", skip(self))] - async fn resolve( - &self, - PauseContainer { name }: PauseContainer, - _: (), - ) -> anyhow::Result { +impl Resolve for PauseContainer { + #[instrument(name = "PauseContainer")] + async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "docker pause", None, - format!("docker pause {name}"), + format!("docker pause {}", self.name), false, ) .await, @@ -199,18 +161,14 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "UnpauseContainer", skip(self))] - async fn resolve( - &self, - UnpauseContainer { name }: UnpauseContainer, - _: (), - ) -> anyhow::Result { +impl Resolve for UnpauseContainer { + #[instrument(name = "UnpauseContainer")] + async fn resolve(self, _: &super::Args) -> serror::Result { Ok( run_komodo_command( "docker unpause", None, - format!("docker unpause {name}"), + format!("docker unpause {}", self.name), false, ) .await, @@ -220,13 +178,10 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "StopContainer", skip(self))] - async fn resolve( - &self, - StopContainer { name, signal, time }: StopContainer, - _: (), - ) -> anyhow::Result { +impl Resolve for StopContainer { + #[instrument(name = "StopContainer")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let StopContainer { name, signal, time } = self; let command = stop_container_command(&name, signal, time); let log = run_komodo_command("docker stop", None, command, false).await; @@ -251,13 +206,10 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "RemoveContainer", skip(self))] - async fn resolve( - &self, - RemoveContainer { name, signal, time }: RemoveContainer, - _: (), - ) -> anyhow::Result { +impl Resolve for RemoveContainer { + #[instrument(name = "RemoveContainer")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let RemoveContainer { name, signal, time } = self; let stop_command = stop_container_command(&name, signal, time); let command = format!("{stop_command} && docker container rm {name}"); @@ -291,16 +243,13 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "RenameContainer", skip(self))] - async fn resolve( - &self, - RenameContainer { +impl Resolve for RenameContainer { + #[instrument(name = "RenameContainer")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let RenameContainer { curr_name, new_name, - }: RenameContainer, - _: (), - ) -> anyhow::Result { + } = self; let new = to_komodo_name(&new_name); let command = format!("docker rename {curr_name} {new}"); Ok( @@ -311,13 +260,9 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "PruneContainers", skip(self))] - async fn resolve( - &self, - _: PruneContainers, - _: (), - ) -> anyhow::Result { +impl Resolve for PruneContainers { + #[instrument(name = "PruneContainers", skip_all)] + async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker container prune -f"); Ok( run_komodo_command("prune containers", None, command, false) @@ -328,13 +273,12 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "StartAllContainers", skip(self))] +impl Resolve for StartAllContainers { + #[instrument(name = "StartAllContainers", skip_all)] async fn resolve( - &self, - StartAllContainers {}: StartAllContainers, - _: (), - ) -> anyhow::Result> { + self, + _: &super::Args, + ) -> serror::Result> { let containers = docker_client() .list_containers() .await @@ -357,13 +301,12 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "RestartAllContainers", skip(self))] +impl Resolve for RestartAllContainers { + #[instrument(name = "RestartAllContainers", skip_all)] async fn resolve( - &self, - RestartAllContainers {}: RestartAllContainers, - _: (), - ) -> anyhow::Result> { + self, + _: &super::Args, + ) -> serror::Result> { let containers = docker_client() .list_containers() .await @@ -386,13 +329,12 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "PauseAllContainers", skip(self))] +impl Resolve for PauseAllContainers { + #[instrument(name = "PauseAllContainers", skip_all)] async fn resolve( - &self, - PauseAllContainers {}: PauseAllContainers, - _: (), - ) -> anyhow::Result> { + self, + _: &super::Args, + ) -> serror::Result> { let containers = docker_client() .list_containers() .await @@ -415,13 +357,12 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "UnpauseAllContainers", skip(self))] +impl Resolve for UnpauseAllContainers { + #[instrument(name = "UnpauseAllContainers", skip_all)] async fn resolve( - &self, - UnpauseAllContainers {}: UnpauseAllContainers, - _: (), - ) -> anyhow::Result> { + self, + _: &super::Args, + ) -> serror::Result> { let containers = docker_client() .list_containers() .await @@ -444,13 +385,12 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "StopAllContainers", skip(self))] +impl Resolve for StopAllContainers { + #[instrument(name = "StopAllContainers", skip_all)] async fn resolve( - &self, - StopAllContainers {}: StopAllContainers, - _: (), - ) -> anyhow::Result> { + self, + _: &super::Args, + ) -> serror::Result> { let containers = docker_client() .list_containers() .await diff --git a/bin/periphery/src/api/deploy.rs b/bin/periphery/src/api/deploy.rs index 8b9d614c8..109ea4998 100644 --- a/bin/periphery/src/api/deploy.rs +++ b/bin/periphery/src/api/deploy.rs @@ -20,25 +20,26 @@ use crate::{ config::periphery_config, docker::{docker_login, pull_image}, helpers::{parse_extra_args, parse_labels}, - State, }; -impl Resolve for State { +impl Resolve for Deploy { #[instrument( name = "Deploy", - skip(self, core_replacers, registry_token) + skip_all, + fields( + stack = &self.deployment.name, + stop_signal = format!("{:?}", self.stop_signal), + stop_time = self.stop_time, + ) )] - async fn resolve( - &self, - Deploy { + async fn resolve(self, _: &super::Args) -> serror::Result { + let Deploy { deployment, stop_signal, stop_time, registry_token, replacers: core_replacers, - }: Deploy, - _: (), - ) -> anyhow::Result { + } = self; let image = if let DeploymentImage::Image { image } = &deployment.config.image { @@ -73,16 +74,13 @@ impl Resolve for State { let _ = pull_image(image).await; debug!("image pulled"); - let _ = State - .resolve( - RemoveContainer { - name: deployment.name.clone(), - signal: stop_signal, - time: stop_time, - }, - (), - ) - .await; + let _ = (RemoveContainer { + name: deployment.name.clone(), + signal: stop_signal, + time: stop_time, + }) + .resolve(&super::Args) + .await; debug!("container stopped and removed"); let command = docker_run_command(&deployment, image) diff --git a/bin/periphery/src/api/git.rs b/bin/periphery/src/api/git.rs index 08b81ec24..50fab64c6 100644 --- a/bin/periphery/src/api/git.rs +++ b/bin/periphery/src/api/git.rs @@ -8,41 +8,45 @@ use periphery_client::api::git::{ use resolver_api::Resolve; use tokio::fs; -use crate::{config::periphery_config, State}; +use crate::config::periphery_config; -impl Resolve for State { +impl Resolve for GetLatestCommit { + #[instrument(name = "CloneRepo", level = "debug")] async fn resolve( - &self, - GetLatestCommit { name }: GetLatestCommit, - _: (), - ) -> anyhow::Result { - let repo_path = periphery_config().repo_dir.join(name); + self, + _: &super::Args, + ) -> serror::Result { + let repo_path = periphery_config().repo_dir.join(self.name); if !repo_path.is_dir() { - return Err(anyhow!( - "Repo path is not directory. is it cloned?" - )); + return Err( + anyhow!("Repo path is not directory. is it cloned?").into(), + ); } - git::get_commit_hash_info(&repo_path).await + Ok(git::get_commit_hash_info(&repo_path).await?) } } -impl Resolve for State { +impl Resolve for CloneRepo { #[instrument( name = "CloneRepo", - skip(self, git_token, environment, replacers) + skip_all, + fields( + args = format!("{:?}", self.args), + skip_secret_interp = self.skip_secret_interp, + ) )] async fn resolve( - &self, - CloneRepo { + self, + _: &super::Args, + ) -> serror::Result { + let CloneRepo { args, git_token, environment, env_file_path, skip_secret_interp, replacers, - }: CloneRepo, - _: (), - ) -> anyhow::Result { + } = self; let CloneArgs { provider, account, .. } = &args; @@ -81,28 +85,33 @@ impl Resolve for State { } }, ) + .map_err(Into::into) } } // -impl Resolve for State { +impl Resolve for PullRepo { #[instrument( name = "PullRepo", - skip(self, git_token, environment, replacers) + skip_all, + fields( + args = format!("{:?}", self.args), + skip_secret_interp = self.skip_secret_interp, + ) )] async fn resolve( - &self, - PullRepo { + self, + _: &super::Args, + ) -> serror::Result { + let PullRepo { args, git_token, environment, env_file_path, skip_secret_interp, replacers, - }: PullRepo, - _: (), - ) -> anyhow::Result { + } = self; let CloneArgs { provider, account, .. } = &args; @@ -141,28 +150,33 @@ impl Resolve for State { } }, ) + .map_err(Into::into) } } // -impl Resolve for State { +impl Resolve for PullOrCloneRepo { #[instrument( name = "PullOrCloneRepo", - skip(self, git_token, environment, replacers) + skip_all, + fields( + args = format!("{:?}", self.args), + skip_secret_interp = self.skip_secret_interp, + ) )] async fn resolve( - &self, - PullOrCloneRepo { + self, + _: &super::Args, + ) -> serror::Result { + let PullOrCloneRepo { args, git_token, environment, env_file_path, skip_secret_interp, replacers, - }: PullOrCloneRepo, - _: (), - ) -> anyhow::Result { + } = self; let CloneArgs { provider, account, .. } = &args; @@ -201,21 +215,19 @@ impl Resolve for State { } }, ) + .map_err(Into::into) } } // -impl Resolve for State { - #[instrument(name = "RenameRepo", skip(self))] - async fn resolve( - &self, - RenameRepo { +impl Resolve for RenameRepo { + #[instrument(name = "RenameRepo")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let RenameRepo { curr_name, new_name, - }: RenameRepo, - _: (), - ) -> anyhow::Result { + } = self; let renamed = fs::rename( periphery_config().repo_dir.join(&curr_name), periphery_config().repo_dir.join(&new_name), @@ -231,13 +243,10 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "DeleteRepo", skip(self))] - async fn resolve( - &self, - DeleteRepo { name }: DeleteRepo, - _: (), - ) -> anyhow::Result { +impl Resolve for DeleteRepo { + #[instrument(name = "DeleteRepo")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let DeleteRepo { name } = self; // If using custom clone path, it will be passed by core instead of name. // So the join will resolve to just the absolute path. let deleted = diff --git a/bin/periphery/src/api/image.rs b/bin/periphery/src/api/image.rs index e226b7672..aa215a9e7 100644 --- a/bin/periphery/src/api/image.rs +++ b/bin/periphery/src/api/image.rs @@ -11,34 +11,26 @@ use komodo_client::entities::{ use periphery_client::api::image::*; use resolver_api::Resolve; -use crate::{ - docker::{docker_client, docker_login}, - State, -}; +use crate::docker::{docker_client, docker_login}; // -impl Resolve for State { - #[instrument(name = "InspectImage", level = "debug", skip(self))] - async fn resolve( - &self, - InspectImage { name }: InspectImage, - _: (), - ) -> anyhow::Result { - docker_client().inspect_image(&name).await +impl Resolve for InspectImage { + #[instrument(name = "InspectImage", level = "debug")] + async fn resolve(self, _: &super::Args) -> serror::Result { + Ok(docker_client().inspect_image(&self.name).await?) } } // -impl Resolve for State { - #[instrument(name = "ImageHistory", level = "debug", skip(self))] +impl Resolve for ImageHistory { + #[instrument(name = "ImageHistory", level = "debug")] async fn resolve( - &self, - ImageHistory { name }: ImageHistory, - _: (), - ) -> anyhow::Result> { - docker_client().image_history(&name).await + self, + _: &super::Args, + ) -> serror::Result> { + Ok(docker_client().image_history(&self.name).await?) } } @@ -53,17 +45,14 @@ fn pull_cache() -> &'static TimeoutCache { PULL_CACHE.get_or_init(Default::default) } -impl Resolve for State { - #[instrument(name = "PullImage", skip(self))] - async fn resolve( - &self, - PullImage { +impl Resolve for PullImage { + #[instrument(name = "PullImage", skip_all, fields(name = &self.name))] + async fn resolve(self, _: &super::Args) -> serror::Result { + let PullImage { name, account, token, - }: PullImage, - _: (), - ) -> anyhow::Result { + } = self; // Acquire the image lock let lock = pull_cache().get_lock(name.clone()).await; @@ -74,7 +63,7 @@ impl Resolve for State { // Early return from cache if lasted pulled with PULL_TIMEOUT if locked.last_ts + PULL_TIMEOUT > komodo_timestamp() { - return locked.clone_res(); + return locked.clone_res().map_err(Into::into); } let res = async { @@ -100,33 +89,25 @@ impl Resolve for State { // then immediately also use this same result. locked.set(&res, komodo_timestamp()); - res + res.map_err(Into::into) } } // -impl Resolve for State { - #[instrument(name = "DeleteImage", skip(self))] - async fn resolve( - &self, - DeleteImage { name }: DeleteImage, - _: (), - ) -> anyhow::Result { - let command = format!("docker image rm {name}"); +impl Resolve for DeleteImage { + #[instrument(name = "DeleteImage")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let command = format!("docker image rm {}", self.name); Ok(run_komodo_command("delete image", None, command, false).await) } } // -impl Resolve for State { - #[instrument(name = "PruneImages", skip(self))] - async fn resolve( - &self, - _: PruneImages, - _: (), - ) -> anyhow::Result { +impl Resolve for PruneImages { + #[instrument(name = "PruneImages")] + async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker image prune -a -f"); Ok(run_komodo_command("prune images", None, command, false).await) } diff --git a/bin/periphery/src/api/mod.rs b/bin/periphery/src/api/mod.rs index e4e9dea0e..536c3e887 100644 --- a/bin/periphery/src/api/mod.rs +++ b/bin/periphery/src/api/mod.rs @@ -2,24 +2,23 @@ use anyhow::Context; use command::run_komodo_command; use derive_variants::EnumVariants; use futures::TryFutureExt; -use komodo_client::entities::{update::Log, SystemCommand}; +use komodo_client::entities::{ + config::{DockerRegistry, GitProvider}, + update::Log, + SystemCommand, +}; use periphery_client::api::{ build::*, compose::*, container::*, git::*, image::*, network::*, stats::*, volume::*, GetDockerLists, GetDockerListsResponse, - GetHealth, GetVersion, GetVersionResponse, ListDockerRegistries, - ListGitProviders, ListSecrets, PruneSystem, RunCommand, + GetHealth, GetHealthResponse, GetVersion, GetVersionResponse, + ListDockerRegistries, ListGitProviders, ListSecrets, PruneSystem, + RunCommand, }; -use resolver_api::{derive::Resolver, Resolve, ResolveToString}; +use resolver_api::Resolve; +use response::Response; use serde::{Deserialize, Serialize}; -use crate::{ - config::{ - docker_registries_response, git_providers_response, - secrets_response, - }, - docker::docker_client, - State, -}; +use crate::{config::periphery_config, docker::docker_client}; mod build; mod compose; @@ -31,32 +30,29 @@ mod network; mod stats; mod volume; +pub struct Args; + #[derive( - Serialize, Deserialize, Debug, Clone, Resolver, EnumVariants, + Serialize, Deserialize, Debug, Clone, Resolve, EnumVariants, )] +#[args(Args)] +#[response(Response)] +#[error(serror::Error)] #[variant_derive(Debug)] #[serde(tag = "type", content = "params")] -#[resolver_target(State)] #[allow(clippy::enum_variant_names, clippy::large_enum_variant)] pub enum PeripheryRequest { GetVersion(GetVersion), - #[to_string_resolver] GetHealth(GetHealth), // Config (Read) - #[to_string_resolver] ListGitProviders(ListGitProviders), - #[to_string_resolver] ListDockerRegistries(ListDockerRegistries), - #[to_string_resolver] ListSecrets(ListSecrets), // Stats / Info (Read) - #[to_string_resolver] GetSystemInformation(GetSystemInformation), - #[to_string_resolver] GetSystemStats(GetSystemStats), - #[to_string_resolver] GetSystemProcesses(GetSystemProcesses), GetLatestCommit(GetLatestCommit), @@ -142,26 +138,24 @@ pub enum PeripheryRequest { // -impl ResolveToString for State { - #[instrument(name = "GetHealth", level = "debug", skip(self))] - async fn resolve_to_string( - &self, - _: GetHealth, - _: (), - ) -> anyhow::Result { - Ok(String::from("{}")) +impl Resolve for GetHealth { + #[instrument(name = "GetHealth", level = "debug", skip_all)] + async fn resolve( + self, + _: &Args, + ) -> serror::Result { + Ok(GetHealthResponse {}) } } // -impl Resolve for State { +impl Resolve for GetVersion { #[instrument(name = "GetVersion", level = "debug", skip(self))] async fn resolve( - &self, - _: GetVersion, - _: (), - ) -> anyhow::Result { + self, + _: &Args, + ) -> serror::Result { Ok(GetVersionResponse { version: env!("CARGO_PKG_VERSION").to_string(), }) @@ -170,56 +164,51 @@ impl Resolve for State { // -impl ResolveToString for State { - #[instrument( - name = "ListGitProviders", - level = "debug", - skip(self) - )] - async fn resolve_to_string( - &self, - _: ListGitProviders, - _: (), - ) -> anyhow::Result { - Ok(git_providers_response().clone()) +impl Resolve for ListGitProviders { + #[instrument(name = "ListGitProviders", level = "debug", skip_all)] + async fn resolve( + self, + _: &Args, + ) -> serror::Result> { + Ok(periphery_config().git_providers.clone()) } } -impl ResolveToString for State { +impl Resolve for ListDockerRegistries { #[instrument( name = "ListDockerRegistries", level = "debug", - skip(self) + skip_all )] - async fn resolve_to_string( - &self, - _: ListDockerRegistries, - _: (), - ) -> anyhow::Result { - Ok(docker_registries_response().clone()) + async fn resolve( + self, + _: &Args, + ) -> serror::Result> { + Ok(periphery_config().docker_registries.clone()) } } // -impl ResolveToString for State { - #[instrument(name = "ListSecrets", level = "debug", skip(self))] - async fn resolve_to_string( - &self, - _: ListSecrets, - _: (), - ) -> anyhow::Result { - Ok(secrets_response().clone()) +impl Resolve for ListSecrets { + #[instrument(name = "ListSecrets", level = "debug", skip_all)] + async fn resolve(self, _: &Args) -> serror::Result> { + Ok( + periphery_config() + .secrets + .keys() + .cloned() + .collect::>(), + ) } } -impl Resolve for State { - #[instrument(name = "GetDockerLists", level = "debug", skip(self))] +impl Resolve for GetDockerLists { + #[instrument(name = "GetDockerLists", level = "debug", skip_all)] async fn resolve( - &self, - GetDockerLists {}: GetDockerLists, - _: (), - ) -> anyhow::Result { + self, + _: &Args, + ) -> serror::Result { let docker = docker_client(); let containers = docker.list_containers().await.map_err(Into::into); @@ -232,7 +221,9 @@ impl Resolve for State { docker.list_networks(_containers).map_err(Into::into), docker.list_images(_containers).map_err(Into::into), docker.list_volumes(_containers).map_err(Into::into), - self.resolve(ListComposeProjects {}, ()).map_err(Into::into) + ListComposeProjects {} + .resolve(&Args) + .map_err(|e| e.error.into()) ); Ok(GetDockerListsResponse { containers, @@ -244,16 +235,13 @@ impl Resolve for State { } } -impl Resolve for State { - #[instrument(name = "RunCommand", skip(self))] - async fn resolve( - &self, - RunCommand { +impl Resolve for RunCommand { + #[instrument(name = "RunCommand")] + async fn resolve(self, _: &Args) -> serror::Result { + let RunCommand { command: SystemCommand { path, command }, - }: RunCommand, - _: (), - ) -> anyhow::Result { - tokio::spawn(async move { + } = self; + let res = tokio::spawn(async move { let command = if path.is_empty() { command } else { @@ -262,17 +250,14 @@ impl Resolve for State { run_komodo_command("run command", None, command, false).await }) .await - .context("failure in spawned task") + .context("failure in spawned task")?; + Ok(res) } } -impl Resolve for State { - #[instrument(name = "PruneSystem", skip(self))] - async fn resolve( - &self, - PruneSystem {}: PruneSystem, - _: (), - ) -> anyhow::Result { +impl Resolve for PruneSystem { + #[instrument(name = "PruneSystem", skip_all)] + async fn resolve(self, _: &Args) -> serror::Result { let command = String::from("docker system prune -a -f --volumes"); Ok(run_komodo_command("prune system", None, command, false).await) } diff --git a/bin/periphery/src/api/network.rs b/bin/periphery/src/api/network.rs index f13edcf43..3146b73c8 100644 --- a/bin/periphery/src/api/network.rs +++ b/bin/periphery/src/api/network.rs @@ -5,30 +5,23 @@ use komodo_client::entities::{ use periphery_client::api::network::*; use resolver_api::Resolve; -use crate::{docker::docker_client, State}; +use crate::docker::docker_client; // -impl Resolve for State { - #[instrument(name = "InspectNetwork", level = "debug", skip(self))] - async fn resolve( - &self, - InspectNetwork { name }: InspectNetwork, - _: (), - ) -> anyhow::Result { - docker_client().inspect_network(&name).await +impl Resolve for InspectNetwork { + #[instrument(name = "InspectNetwork", level = "debug")] + async fn resolve(self, _: &super::Args) -> serror::Result { + Ok(docker_client().inspect_network(&self.name).await?) } } // -impl Resolve for State { +impl Resolve for CreateNetwork { #[instrument(name = "CreateNetwork", skip(self))] - async fn resolve( - &self, - CreateNetwork { name, driver }: CreateNetwork, - _: (), - ) -> anyhow::Result { + async fn resolve(self, _: &super::Args) -> serror::Result { + let CreateNetwork { name, driver } = self; let driver = match driver { Some(driver) => format!(" -d {driver}"), None => String::new(), @@ -43,14 +36,10 @@ impl Resolve for State { // -impl Resolve for State { +impl Resolve for DeleteNetwork { #[instrument(name = "DeleteNetwork", skip(self))] - async fn resolve( - &self, - DeleteNetwork { name }: DeleteNetwork, - _: (), - ) -> anyhow::Result { - let command = format!("docker network rm {name}"); + async fn resolve(self, _: &super::Args) -> serror::Result { + let command = format!("docker network rm {}", self.name); Ok( run_komodo_command("delete network", None, command, false) .await, @@ -60,13 +49,9 @@ impl Resolve for State { // -impl Resolve for State { +impl Resolve for PruneNetworks { #[instrument(name = "PruneNetworks", skip(self))] - async fn resolve( - &self, - _: PruneNetworks, - _: (), - ) -> anyhow::Result { + async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker network prune -f"); Ok( run_komodo_command("prune networks", None, command, false) diff --git a/bin/periphery/src/api/stats.rs b/bin/periphery/src/api/stats.rs index f8df366d1..9f4429fbe 100644 --- a/bin/periphery/src/api/stats.rs +++ b/bin/periphery/src/api/stats.rs @@ -1,59 +1,48 @@ -use anyhow::Context; +use komodo_client::entities::stats::{ + SystemInformation, SystemProcess, SystemStats, +}; use periphery_client::api::stats::{ GetSystemInformation, GetSystemProcesses, GetSystemStats, }; -use resolver_api::ResolveToString; +use resolver_api::Resolve; -use crate::{stats::stats_client, State}; +use crate::stats::stats_client; -impl ResolveToString for State { +impl Resolve for GetSystemInformation { #[instrument( name = "GetSystemInformation", level = "debug", - skip(self) + skip_all )] - async fn resolve_to_string( - &self, - _: GetSystemInformation, - _: (), - ) -> anyhow::Result { - let info = &stats_client().read().await.info; - serde_json::to_string(info) - .context("failed to serialize response to string") + async fn resolve( + self, + _: &super::Args, + ) -> serror::Result { + Ok(stats_client().read().await.info.clone()) } } // -impl ResolveToString for State { - #[instrument(name = "GetSystemStats", level = "debug", skip(self))] - async fn resolve_to_string( - &self, - _: GetSystemStats, - _: (), - ) -> anyhow::Result { - let stats = &stats_client().read().await.stats; - serde_json::to_string(stats) - .context("failed to serialize response to string") +impl Resolve for GetSystemStats { + #[instrument(name = "GetSystemStats", level = "debug", skip_all)] + async fn resolve( + self, + _: &super::Args, + ) -> serror::Result { + Ok(stats_client().read().await.stats.clone()) } } // -impl ResolveToString for State { - #[instrument( - name = "GetSystemProcesses", - level = "debug", - skip(self) - )] - async fn resolve_to_string( - &self, - _: GetSystemProcesses, - _: (), - ) -> anyhow::Result { - let stats = &stats_client().read().await.get_processes(); - serde_json::to_string(&stats) - .context("failed to serialize response to string") +impl Resolve for GetSystemProcesses { + #[instrument(name = "GetSystemProcesses", level = "debug")] + async fn resolve( + self, + _: &super::Args, + ) -> serror::Result> { + Ok(stats_client().read().await.get_processes()) } } diff --git a/bin/periphery/src/api/volume.rs b/bin/periphery/src/api/volume.rs index 622fe6f4b..ef72a1d1e 100644 --- a/bin/periphery/src/api/volume.rs +++ b/bin/periphery/src/api/volume.rs @@ -3,31 +3,23 @@ use komodo_client::entities::{docker::volume::Volume, update::Log}; use periphery_client::api::volume::*; use resolver_api::Resolve; -use crate::{docker::docker_client, State}; +use crate::docker::docker_client; // -impl Resolve for State { - #[instrument(name = "InspectVolume", level = "debug", skip(self))] - async fn resolve( - &self, - InspectVolume { name }: InspectVolume, - _: (), - ) -> anyhow::Result { - docker_client().inspect_volume(&name).await +impl Resolve for InspectVolume { + #[instrument(name = "InspectVolume", level = "debug")] + async fn resolve(self, _: &super::Args) -> serror::Result { + Ok(docker_client().inspect_volume(&self.name).await?) } } // -impl Resolve for State { - #[instrument(name = "DeleteVolume", skip(self))] - async fn resolve( - &self, - DeleteVolume { name }: DeleteVolume, - _: (), - ) -> anyhow::Result { - let command = format!("docker volume rm {name}"); +impl Resolve for DeleteVolume { + #[instrument(name = "DeleteVolume")] + async fn resolve(self, _: &super::Args) -> serror::Result { + let command = format!("docker volume rm {}", self.name); Ok( run_komodo_command("delete volume", None, command, false).await, ) @@ -36,13 +28,9 @@ impl Resolve for State { // -impl Resolve for State { - #[instrument(name = "PruneVolumes", skip(self))] - async fn resolve( - &self, - _: PruneVolumes, - _: (), - ) -> anyhow::Result { +impl Resolve for PruneVolumes { + #[instrument(name = "PruneVolumes")] + async fn resolve(self, _: &super::Args) -> serror::Result { let command = String::from("docker volume prune -a -f"); Ok( run_komodo_command("prune volumes", None, command, false).await, diff --git a/bin/periphery/src/compose.rs b/bin/periphery/src/compose.rs index 784670262..2bb8877d6 100644 --- a/bin/periphery/src/compose.rs +++ b/bin/periphery/src/compose.rs @@ -25,7 +25,6 @@ use crate::{ config::periphery_config, docker::docker_login, helpers::{interpolate_variables, parse_extra_args}, - State, }; pub fn docker_compose() -> &'static str { @@ -588,37 +587,31 @@ pub async fn write_stack( .to_string(); let clone_or_pull_res = if stack.config.reclone { - State - .resolve( - CloneRepo { - args, - git_token, - environment: env_vars, - env_file_path, - skip_secret_interp: stack.config.skip_secret_interp, - // repo replacer only needed for on_clone / on_pull, - // which aren't available for stacks - replacers: Default::default(), - }, - (), - ) - .await + CloneRepo { + args, + git_token, + environment: env_vars, + env_file_path, + skip_secret_interp: stack.config.skip_secret_interp, + // repo replacer only needed for on_clone / on_pull, + // which aren't available for stacks + replacers: Default::default(), + } + .resolve(&crate::api::Args) + .await } else { - State - .resolve( - PullOrCloneRepo { - args, - git_token, - environment: env_vars, - env_file_path, - skip_secret_interp: stack.config.skip_secret_interp, - // repo replacer only needed for on_clone / on_pull, - // which aren't available for stacks - replacers: Default::default(), - }, - (), - ) - .await + PullOrCloneRepo { + args, + git_token, + environment: env_vars, + env_file_path, + skip_secret_interp: stack.config.skip_secret_interp, + // repo replacer only needed for on_clone / on_pull, + // which aren't available for stacks + replacers: Default::default(), + } + .resolve(&crate::api::Args) + .await }; let RepoActionResponse { @@ -630,7 +623,7 @@ pub async fn write_stack( Ok(res) => res, Err(e) => { let error = format_serror( - &e.context("failed to pull stack repo").into(), + &e.error.context("failed to pull stack repo").into(), ); res .logs() diff --git a/bin/periphery/src/config.rs b/bin/periphery/src/config.rs index 83c77015f..4fd2980f4 100644 --- a/bin/periphery/src/config.rs +++ b/bin/periphery/src/config.rs @@ -88,30 +88,3 @@ pub fn periphery_config() -> &'static PeripheryConfig { } }) } - -pub fn git_providers_response() -> &'static String { - static GIT_PROVIDERS_RESPONSE: OnceLock = OnceLock::new(); - GIT_PROVIDERS_RESPONSE.get_or_init(|| { - let config = periphery_config(); - serde_json::to_string(&config.git_providers).unwrap() - }) -} - -pub fn docker_registries_response() -> &'static String { - static DOCKER_REGISTRIES_RESPONSE: OnceLock = - OnceLock::new(); - DOCKER_REGISTRIES_RESPONSE.get_or_init(|| { - let config = periphery_config(); - serde_json::to_string(&config.docker_registries).unwrap() - }) -} - -pub fn secrets_response() -> &'static String { - static SECRETS_RESPONSE: OnceLock = OnceLock::new(); - SECRETS_RESPONSE.get_or_init(|| { - serde_json::to_string( - &periphery_config().secrets.keys().collect::>(), - ) - .unwrap() - }) -} diff --git a/bin/periphery/src/helpers.rs b/bin/periphery/src/helpers.rs index 790fd6a01..19ed9adb3 100644 --- a/bin/periphery/src/helpers.rs +++ b/bin/periphery/src/helpers.rs @@ -11,7 +11,7 @@ use komodo_client::{ use periphery_client::api::git::PullOrCloneRepo; use resolver_api::Resolve; -use crate::{config::periphery_config, State}; +use crate::config::periphery_config; pub fn git_token( domain: &str, @@ -140,21 +140,19 @@ pub async fn pull_or_clone_stack( } }; - State - .resolve( - PullOrCloneRepo { - args, - git_token, - environment: vec![], - env_file_path: stack.config.env_file_path.clone(), - skip_secret_interp: stack.config.skip_secret_interp, - // repo replacer only needed for on_clone / on_pull, - // which aren't available for stacks - replacers: Default::default(), - }, - (), - ) - .await?; + PullOrCloneRepo { + args, + git_token, + environment: vec![], + env_file_path: stack.config.env_file_path.clone(), + skip_secret_interp: stack.config.skip_secret_interp, + // repo replacer only needed for on_clone / on_pull, + // which aren't available for stacks + replacers: Default::default(), + } + .resolve(&crate::api::Args) + .await + .map_err(|e| e.error)?; Ok(root) } diff --git a/bin/periphery/src/main.rs b/bin/periphery/src/main.rs index 1cfdb1c37..e2272f257 100644 --- a/bin/periphery/src/main.rs +++ b/bin/periphery/src/main.rs @@ -16,8 +16,6 @@ mod router; mod ssl; mod stats; -struct State; - async fn app() -> anyhow::Result<()> { dotenvy::dotenv().ok(); let config = config::periphery_config(); @@ -26,7 +24,7 @@ async fn app() -> anyhow::Result<()> { info!("Komodo Periphery version: v{}", env!("CARGO_PKG_VERSION")); info!("{:?}", config.sanitized()); - stats::spawn_system_stats_polling_threads(); + stats::spawn_system_stats_polling_thread(); let socket_addr = SocketAddr::from_str(&format!("0.0.0.0:{}", config.port)) diff --git a/bin/periphery/src/router.rs b/bin/periphery/src/router.rs index daeda62bf..366fa29a9 100644 --- a/bin/periphery/src/router.rs +++ b/bin/periphery/src/router.rs @@ -10,13 +10,12 @@ use axum::{ routing::post, Router, }; -use axum_extra::{headers::ContentType, TypedHeader}; use derive_variants::ExtractVariant; -use resolver_api::Resolver; +use resolver_api::Resolve; use serror::{AddStatusCode, AddStatusCodeError, Json}; use uuid::Uuid; -use crate::{config::periphery_config, State}; +use crate::config::periphery_config; pub fn router() -> Router { Router::new() @@ -27,7 +26,7 @@ pub fn router() -> Router { async fn handler( Json(request): Json, -) -> serror::Result<(TypedHeader, String)> { +) -> serror::Result { let req_id = Uuid::new_v4(); let res = tokio::spawn(task(req_id, request)) @@ -38,28 +37,22 @@ async fn handler( warn!("request {req_id} spawn error: {e:#}"); } - Ok((TypedHeader(ContentType::json()), res??)) + res? } async fn task( req_id: Uuid, request: crate::api::PeripheryRequest, -) -> anyhow::Result { +) -> serror::Result { let variant = request.extract_variant(); - let res = - State - .resolve_request(request, ()) - .await - .map_err(|e| match e { - resolver_api::Error::Serialization(e) => { - anyhow!("{e:?}").context("response serialization error") - } - resolver_api::Error::Inner(e) => e, - }); + let res = request.resolve(&crate::api::Args).await.map(|res| res.0); if let Err(e) = &res { - warn!("request {req_id} | type: {variant:?} | error: {e:#}"); + warn!( + "request {req_id} | type: {variant:?} | error: {:#}", + e.error + ); } res diff --git a/bin/periphery/src/stats.rs b/bin/periphery/src/stats.rs index 76e520b2b..4181f3de9 100644 --- a/bin/periphery/src/stats.rs +++ b/bin/periphery/src/stats.rs @@ -16,21 +16,8 @@ pub fn stats_client() -> &'static RwLock { } /// This should be called before starting the server in main.rs. -/// Keeps the caches stats up to date -pub fn spawn_system_stats_polling_threads() { - tokio::spawn(async move { - let client = stats_client(); - loop { - let ts = wait_until_timelength( - async_timing_util::Timelength::FiveMinutes, - 0, - ) - .await; - let mut client = client.write().await; - client.refresh_lists(); - client.stats.refresh_list_ts = ts as i64; - } - }); +/// Keeps the cached stats up to date +pub fn spawn_system_stats_polling_thread() { tokio::spawn(async move { let polling_rate = periphery_config() .stats_polling_rate @@ -89,13 +76,8 @@ impl StatsClient { self.system.refresh_cpu_all(); self.system.refresh_memory(); self.system.refresh_processes(ProcessesToUpdate::All, true); - self.disks.refresh(); - self.networks.refresh(); - } - - fn refresh_lists(&mut self) { - self.disks.refresh_list(); - self.networks.refresh_list(); + self.disks.refresh(true); + self.networks.refresh(true); } pub fn get_system_stats(&self) -> SystemStats { diff --git a/client/core/rs/src/api/auth.rs b/client/core/rs/src/api/auth.rs index b15685c7c..c6ca3b20f 100644 --- a/client/core/rs/src/api/auth.rs +++ b/client/core/rs/src/api/auth.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::{derive::Request, HasResponse}; +use resolver_api::{HasResponse, Resolve}; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -22,10 +22,11 @@ pub struct JwtResponse { /// Response: [GetLoginOptionsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoAuthRequest)] #[response(GetLoginOptionsResponse)] +#[error(serror::Error)] pub struct GetLoginOptions {} /// The response for [GetLoginOptions]. @@ -53,10 +54,11 @@ pub struct GetLoginOptionsResponse { /// Note. This method is only available if the core api has `local_auth` enabled. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoAuthRequest)] #[response(CreateLocalUserResponse)] +#[error(serror::Error)] pub struct CreateLocalUser { /// The username for the new user. pub username: String, @@ -77,10 +79,11 @@ pub type CreateLocalUserResponse = JwtResponse; /// Note. This method is only available if the core api has `local_auth` enabled. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoAuthRequest)] #[response(LoginLocalUserResponse)] +#[error(serror::Error)] pub struct LoginLocalUser { /// The user's username pub username: String, @@ -99,10 +102,11 @@ pub type LoginLocalUserResponse = JwtResponse; /// Response: [ExchangeForJwtResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoAuthRequest)] #[response(ExchangeForJwtResponse)] +#[error(serror::Error)] pub struct ExchangeForJwt { /// The 'exchange token' pub token: String, @@ -118,10 +122,11 @@ pub type ExchangeForJwtResponse = JwtResponse; /// Response: [User]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoAuthRequest)] #[response(GetUserResponse)] +#[error(serror::Error)] pub struct GetUser {} #[typeshare] diff --git a/client/core/rs/src/api/execute/action.rs b/client/core/rs/src/api/execute/action.rs index 93b5978c0..b5f8a2fb6 100644 --- a/client/core/rs/src/api/execute/action.rs +++ b/client/core/rs/src/api/execute/action.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,12 +16,13 @@ use super::{BatchExecutionResponse, KomodoExecuteRequest}; PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RunAction { /// Id or name pub action: String, @@ -35,12 +36,13 @@ pub struct RunAction { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchRunAction { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. diff --git a/client/core/rs/src/api/execute/build.rs b/client/core/rs/src/api/execute/build.rs index 0fd0de62c..dadff102f 100644 --- a/client/core/rs/src/api/execute/build.rs +++ b/client/core/rs/src/api/execute/build.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -23,12 +23,13 @@ use super::{BatchExecutionResponse, KomodoExecuteRequest}; PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RunBuild { /// Can be build id or name pub build: String, @@ -44,12 +45,13 @@ pub struct RunBuild { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchRunBuild { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. @@ -76,12 +78,13 @@ pub struct BatchRunBuild { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct CancelBuild { /// Can be id or name pub build: String, diff --git a/client/core/rs/src/api/execute/deployment.rs b/client/core/rs/src/api/execute/deployment.rs index bdf83e533..a37833a40 100644 --- a/client/core/rs/src/api/execute/deployment.rs +++ b/client/core/rs/src/api/execute/deployment.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -22,12 +22,13 @@ use super::{BatchExecutionResponse, KomodoExecuteRequest}; Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct Deploy { /// Name or id pub deployment: String, @@ -49,12 +50,13 @@ pub struct Deploy { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchDeploy { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. @@ -79,12 +81,13 @@ pub struct BatchDeploy { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PullDeployment { /// Name or id pub deployment: String, @@ -102,12 +105,13 @@ pub struct PullDeployment { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StartDeployment { /// Name or id pub deployment: String, @@ -125,12 +129,13 @@ pub struct StartDeployment { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RestartDeployment { /// Name or id pub deployment: String, @@ -148,12 +153,13 @@ pub struct RestartDeployment { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PauseDeployment { /// Name or id pub deployment: String, @@ -173,12 +179,13 @@ pub struct PauseDeployment { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct UnpauseDeployment { /// Name or id pub deployment: String, @@ -196,12 +203,13 @@ pub struct UnpauseDeployment { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StopDeployment { /// Name or id pub deployment: String, @@ -224,12 +232,13 @@ pub struct StopDeployment { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DestroyDeployment { /// Name or id. pub deployment: String, @@ -249,12 +258,13 @@ pub struct DestroyDeployment { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchDestroyDeployment { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. diff --git a/client/core/rs/src/api/execute/procedure.rs b/client/core/rs/src/api/execute/procedure.rs index 950282af9..983d41427 100644 --- a/client/core/rs/src/api/execute/procedure.rs +++ b/client/core/rs/src/api/execute/procedure.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,12 +16,13 @@ use super::{BatchExecutionResponse, KomodoExecuteRequest}; PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RunProcedure { /// Id or name pub procedure: String, @@ -35,12 +36,13 @@ pub struct RunProcedure { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchRunProcedure { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. diff --git a/client/core/rs/src/api/execute/repo.rs b/client/core/rs/src/api/execute/repo.rs index 9d04ef358..0e0fed797 100644 --- a/client/core/rs/src/api/execute/repo.rs +++ b/client/core/rs/src/api/execute/repo.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -26,12 +26,13 @@ use super::{BatchExecutionResponse, KomodoExecuteRequest}; Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct CloneRepo { /// Id or name pub repo: String, @@ -47,12 +48,13 @@ pub struct CloneRepo { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchCloneRepo { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. @@ -82,12 +84,13 @@ pub struct BatchCloneRepo { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PullRepo { /// Id or name pub repo: String, @@ -103,12 +106,13 @@ pub struct PullRepo { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchPullRepo { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. @@ -142,12 +146,13 @@ pub struct BatchPullRepo { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct BuildRepo { /// Id or name pub repo: String, @@ -163,12 +168,13 @@ pub struct BuildRepo { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchBuildRepo { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. @@ -195,12 +201,13 @@ pub struct BatchBuildRepo { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct CancelRepoBuild { /// Can be id or name pub repo: String, diff --git a/client/core/rs/src/api/execute/server.rs b/client/core/rs/src/api/execute/server.rs index 87067ff40..76f76e038 100644 --- a/client/core/rs/src/api/execute/server.rs +++ b/client/core/rs/src/api/execute/server.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -22,12 +22,13 @@ use super::KomodoExecuteRequest; Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StartContainer { /// Name or id pub server: String, @@ -47,12 +48,13 @@ pub struct StartContainer { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RestartContainer { /// Name or id pub server: String, @@ -72,12 +74,13 @@ pub struct RestartContainer { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PauseContainer { /// Name or id pub server: String, @@ -99,12 +102,13 @@ pub struct PauseContainer { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct UnpauseContainer { /// Name or id pub server: String, @@ -124,12 +128,13 @@ pub struct UnpauseContainer { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StopContainer { /// Name or id pub server: String, @@ -154,12 +159,13 @@ pub struct StopContainer { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DestroyContainer { /// Name or id pub server: String, @@ -181,12 +187,13 @@ pub struct DestroyContainer { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StartAllContainers { /// Name or id pub server: String, @@ -202,12 +209,13 @@ pub struct StartAllContainers { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RestartAllContainers { /// Name or id pub server: String, @@ -223,12 +231,13 @@ pub struct RestartAllContainers { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PauseAllContainers { /// Name or id pub server: String, @@ -244,12 +253,13 @@ pub struct PauseAllContainers { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct UnpauseAllContainers { /// Name or id pub server: String, @@ -265,12 +275,13 @@ pub struct UnpauseAllContainers { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StopAllContainers { /// Name or id pub server: String, @@ -288,12 +299,13 @@ pub struct StopAllContainers { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PruneContainers { /// Id or name pub server: String, @@ -312,12 +324,13 @@ pub struct PruneContainers { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DeleteNetwork { /// Id or name. pub server: String, @@ -337,12 +350,13 @@ pub struct DeleteNetwork { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PruneNetworks { /// Id or name pub server: String, @@ -359,12 +373,13 @@ pub struct PruneNetworks { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DeleteImage { /// Id or name. pub server: String, @@ -384,12 +399,13 @@ pub struct DeleteImage { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PruneImages { /// Id or name pub server: String, @@ -406,12 +422,13 @@ pub struct PruneImages { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DeleteVolume { /// Id or name. pub server: String, @@ -431,12 +448,13 @@ pub struct DeleteVolume { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PruneVolumes { /// Id or name pub server: String, @@ -454,12 +472,13 @@ pub struct PruneVolumes { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PruneDockerBuilders { /// Id or name pub server: String, @@ -477,12 +496,13 @@ pub struct PruneDockerBuilders { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PruneBuildx { /// Id or name pub server: String, @@ -500,12 +520,13 @@ pub struct PruneBuildx { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PruneSystem { /// Id or name pub server: String, diff --git a/client/core/rs/src/api/execute/server_template.rs b/client/core/rs/src/api/execute/server_template.rs index 7971edecd..2f5a0afbd 100644 --- a/client/core/rs/src/api/execute/server_template.rs +++ b/client/core/rs/src/api/execute/server_template.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -11,10 +11,11 @@ use super::KomodoExecuteRequest; /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct LaunchServer { /// The name of the created server. pub name: String, diff --git a/client/core/rs/src/api/execute/stack.rs b/client/core/rs/src/api/execute/stack.rs index 0d233d607..5a731063b 100644 --- a/client/core/rs/src/api/execute/stack.rs +++ b/client/core/rs/src/api/execute/stack.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,12 +16,13 @@ use super::{BatchExecutionResponse, KomodoExecuteRequest}; PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DeployStack { /// Id or name pub stack: String, @@ -42,12 +43,13 @@ pub struct DeployStack { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchDeployStack { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. @@ -74,12 +76,13 @@ pub struct BatchDeployStack { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DeployStackIfChanged { /// Id or name pub stack: String, @@ -98,12 +101,13 @@ pub struct DeployStackIfChanged { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchDeployStackIfChanged { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. @@ -128,12 +132,13 @@ pub struct BatchDeployStackIfChanged { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PullStack { /// Id or name pub stack: String, @@ -151,12 +156,13 @@ pub struct PullStack { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StartStack { /// Id or name pub stack: String, @@ -174,12 +180,13 @@ pub struct StartStack { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RestartStack { /// Id or name pub stack: String, @@ -197,12 +204,13 @@ pub struct RestartStack { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct PauseStack { /// Id or name pub stack: String, @@ -222,12 +230,13 @@ pub struct PauseStack { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct UnpauseStack { /// Id or name pub stack: String, @@ -245,12 +254,13 @@ pub struct UnpauseStack { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct StopStack { /// Id or name pub stack: String, @@ -270,12 +280,13 @@ pub struct StopStack { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct DestroyStack { /// Id or name pub stack: String, @@ -298,12 +309,13 @@ pub struct DestroyStack { Debug, Clone, PartialEq, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(BatchExecutionResponse)] +#[error(serror::Error)] pub struct BatchDestroyStack { /// Id or name or wildcard pattern or regex. /// Supports multiline and comma delineated combinations of the above. diff --git a/client/core/rs/src/api/execute/sync.rs b/client/core/rs/src/api/execute/sync.rs index a7e6d7f97..076c5d842 100644 --- a/client/core/rs/src/api/execute/sync.rs +++ b/client/core/rs/src/api/execute/sync.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,12 +16,13 @@ use super::KomodoExecuteRequest; PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoExecuteRequest)] #[response(Update)] +#[error(serror::Error)] pub struct RunSync { /// Id or name pub sync: String, diff --git a/client/core/rs/src/api/read/action.rs b/client/core/rs/src/api/read/action.rs index 0f8aa8c46..476628c7f 100644 --- a/client/core/rs/src/api/read/action.rs +++ b/client/core/rs/src/api/read/action.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// Get a specific action. Response: [Action]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetActionResponse)] +#[error(serror::Error)] pub struct GetAction { /// Id or name #[serde(alias = "id", alias = "name")] @@ -32,10 +33,11 @@ pub type GetActionResponse = Action; /// List actions matching optional query. Response: [ListActionsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListActionsResponse)] +#[error(serror::Error)] pub struct ListActions { /// optional structured query to filter actions. #[serde(default)] @@ -50,10 +52,11 @@ pub type ListActionsResponse = Vec; /// List actions matching optional query. Response: [ListFullActionsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullActionsResponse)] +#[error(serror::Error)] pub struct ListFullActions { /// optional structured query to filter actions. #[serde(default)] @@ -68,10 +71,11 @@ pub type ListFullActionsResponse = Vec; /// Get current action state for the action. Response: [ActionActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetActionActionStateResponse)] +#[error(serror::Error)] pub struct GetActionActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -87,10 +91,11 @@ pub type GetActionActionStateResponse = ActionActionState; /// Response: [GetActionsSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetActionsSummaryResponse)] +#[error(serror::Error)] pub struct GetActionsSummary {} /// Response for [GetActionsSummary]. diff --git a/client/core/rs/src/api/read/alert.rs b/client/core/rs/src/api/read/alert.rs index 4f27ecad3..534845ca9 100644 --- a/client/core/rs/src/api/read/alert.rs +++ b/client/core/rs/src/api/read/alert.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -11,10 +11,11 @@ use super::KomodoReadRequest; /// Response: [ListAlertsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListAlertsResponse)] +#[error(serror::Error)] pub struct ListAlerts { /// Pass a custom mongo query to filter the alerts. /// @@ -62,10 +63,11 @@ pub struct ListAlertsResponse { /// Get an alert: Response: [Alert]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetAlertResponse)] +#[error(serror::Error)] pub struct GetAlert { pub id: String, } diff --git a/client/core/rs/src/api/read/alerter.rs b/client/core/rs/src/api/read/alerter.rs index ad45f803b..15ce143e7 100644 --- a/client/core/rs/src/api/read/alerter.rs +++ b/client/core/rs/src/api/read/alerter.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// Get a specific alerter. Response: [Alerter]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetAlerterResponse)] +#[error(serror::Error)] pub struct GetAlerter { /// Id or name #[serde(alias = "id", alias = "name")] @@ -32,10 +33,11 @@ pub type GetAlerterResponse = Alerter; /// List alerters matching optional query. Response: [ListAlertersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListAlertersResponse)] +#[error(serror::Error)] pub struct ListAlerters { /// Structured query to filter alerters. #[serde(default)] @@ -48,10 +50,11 @@ pub type ListAlertersResponse = Vec; /// List full alerters matching optional query. Response: [ListFullAlertersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullAlertersResponse)] +#[error(serror::Error)] pub struct ListFullAlerters { /// Structured query to filter alerters. #[serde(default)] @@ -67,10 +70,11 @@ pub type ListFullAlertersResponse = Vec; /// Response: [GetAlertersSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetAlertersSummaryResponse)] +#[error(serror::Error)] pub struct GetAlertersSummary {} /// Response for [GetAlertersSummary]. diff --git a/client/core/rs/src/api/read/build.rs b/client/core/rs/src/api/read/build.rs index cf2c5169c..e45c9a21f 100644 --- a/client/core/rs/src/api/read/build.rs +++ b/client/core/rs/src/api/read/build.rs @@ -1,7 +1,7 @@ use std::cmp::Ordering; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -17,10 +17,11 @@ use super::KomodoReadRequest; /// Get a specific build. Response: [Build]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetBuildResponse)] +#[error(serror::Error)] pub struct GetBuild { /// Id or name #[serde(alias = "id", alias = "name")] @@ -35,10 +36,11 @@ pub type GetBuildResponse = Build; /// List builds matching optional query. Response: [ListBuildsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListBuildsResponse)] +#[error(serror::Error)] pub struct ListBuilds { /// optional structured query to filter builds. #[serde(default)] @@ -53,10 +55,11 @@ pub type ListBuildsResponse = Vec; /// List builds matching optional query. Response: [ListFullBuildsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullBuildsResponse)] +#[error(serror::Error)] pub struct ListFullBuilds { /// optional structured query to filter builds. #[serde(default)] @@ -71,10 +74,11 @@ pub type ListFullBuildsResponse = Vec; /// Get current action state for the build. Response: [BuildActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetBuildActionStateResponse)] +#[error(serror::Error)] pub struct GetBuildActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -90,10 +94,11 @@ pub type GetBuildActionStateResponse = BuildActionState; /// Response: [GetBuildsSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetBuildsSummaryResponse)] +#[error(serror::Error)] pub struct GetBuildsSummary {} /// Response for [GetBuildsSummary]. @@ -121,10 +126,11 @@ pub struct GetBuildsSummaryResponse { /// Query for older pages by incrementing the page, starting at 0. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetBuildMonthlyStatsResponse)] +#[error(serror::Error)] pub struct GetBuildMonthlyStats { /// Query for older data by incrementing the page. /// `page: 0` is the default, and will return the most recent data. @@ -182,10 +188,11 @@ impl GetBuildMonthlyStatsResponse { /// Response: [ListBuildVersionsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListBuildVersionsResponse)] +#[error(serror::Error)] pub struct ListBuildVersions { /// Id or name #[serde(alias = "id", alias = "name")] @@ -216,10 +223,11 @@ pub struct BuildVersionResponseItem { /// Useful to offer suggestions. Response: [ListCommonBuildExtraArgsResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListCommonBuildExtraArgsResponse)] +#[error(serror::Error)] pub struct ListCommonBuildExtraArgs { /// optional structured query to filter builds. #[serde(default)] @@ -234,10 +242,11 @@ pub type ListCommonBuildExtraArgsResponse = Vec; /// Get whether a Build's target repo has a webhook for the build configured. Response: [GetBuildWebhookEnabledResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetBuildWebhookEnabledResponse)] +#[error(serror::Error)] pub struct GetBuildWebhookEnabled { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/read/builder.rs b/client/core/rs/src/api/read/builder.rs index 0dd7e09e7..4516fdec5 100644 --- a/client/core/rs/src/api/read/builder.rs +++ b/client/core/rs/src/api/read/builder.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// Get a specific builder by id or name. Response: [Builder]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetBuilderResponse)] +#[error(serror::Error)] pub struct GetBuilder { /// Id or name #[serde(alias = "id", alias = "name")] @@ -32,10 +33,11 @@ pub type GetBuilderResponse = Builder; /// List builders matching structured query. Response: [ListBuildersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListBuildersResponse)] +#[error(serror::Error)] pub struct ListBuilders { #[serde(default)] pub query: BuilderQuery, @@ -49,10 +51,11 @@ pub type ListBuildersResponse = Vec; /// List builders matching structured query. Response: [ListFullBuildersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullBuildersResponse)] +#[error(serror::Error)] pub struct ListFullBuilders { #[serde(default)] pub query: BuilderQuery, @@ -67,10 +70,11 @@ pub type ListFullBuildersResponse = Vec; /// Response: [GetBuildersSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetBuildersSummaryResponse)] +#[error(serror::Error)] pub struct GetBuildersSummary {} /// Response for [GetBuildersSummary]. diff --git a/client/core/rs/src/api/read/deployment.rs b/client/core/rs/src/api/read/deployment.rs index 291a02dda..7194a1d3d 100644 --- a/client/core/rs/src/api/read/deployment.rs +++ b/client/core/rs/src/api/read/deployment.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -20,10 +20,11 @@ use super::KomodoReadRequest; /// Get a specific deployment by name or id. Response: [Deployment]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetDeploymentResponse)] +#[error(serror::Error)] pub struct GetDeployment { /// Id or name #[serde(alias = "id", alias = "name")] @@ -39,10 +40,11 @@ pub type GetDeploymentResponse = Deployment; /// Response: [ListDeploymentsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDeploymentsResponse)] +#[error(serror::Error)] pub struct ListDeployments { /// optional structured query to filter deployments. #[serde(default)] @@ -58,10 +60,11 @@ pub type ListDeploymentsResponse = Vec; /// Response: [ListFullDeploymentsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullDeploymentsResponse)] +#[error(serror::Error)] pub struct ListFullDeployments { /// optional structured query to filter deployments. #[serde(default)] @@ -81,10 +84,11 @@ pub type ListFullDeploymentsResponse = Vec; /// to keep it up to date. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetDeploymentContainerResponse)] +#[error(serror::Error)] pub struct GetDeploymentContainer { /// Id or name #[serde(alias = "id", alias = "name")] @@ -107,10 +111,11 @@ pub struct GetDeploymentContainerResponse { /// Note. This call will hit the underlying server directly for most up to date log. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetDeploymentLogResponse)] +#[error(serror::Error)] pub struct GetDeploymentLog { /// Id or name #[serde(alias = "id", alias = "name")] @@ -140,10 +145,11 @@ pub type GetDeploymentLogResponse = Log; /// Note. This call will hit the underlying server directly for most up to date log. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(SearchDeploymentLogResponse)] +#[error(serror::Error)] pub struct SearchDeploymentLog { /// Id or name #[serde(alias = "id", alias = "name")] @@ -175,10 +181,11 @@ pub type SearchDeploymentLogResponse = Log; /// Note. This call will hit the underlying server directly for most up to date stats. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetDeploymentStatsResponse)] +#[error(serror::Error)] pub struct GetDeploymentStats { /// Id or name #[serde(alias = "id", alias = "name")] @@ -194,10 +201,11 @@ pub type GetDeploymentStatsResponse = ContainerStats; /// Response: [DeploymentActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(DeploymentActionState)] +#[error(serror::Error)] pub struct GetDeploymentActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -213,10 +221,11 @@ pub type GetDeploymentActionStateResponse = DeploymentActionState; /// Response: [GetDeploymentsSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetDeploymentsSummaryResponse)] +#[error(serror::Error)] pub struct GetDeploymentsSummary {} /// Response for [GetDeploymentsSummary]. @@ -243,10 +252,11 @@ pub struct GetDeploymentsSummaryResponse { /// Useful to offer suggestions. Response: [ListCommonDeploymentExtraArgsResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListCommonDeploymentExtraArgsResponse)] +#[error(serror::Error)] pub struct ListCommonDeploymentExtraArgs { /// optional structured query to filter deployments. #[serde(default)] diff --git a/client/core/rs/src/api/read/mod.rs b/client/core/rs/src/api/read/mod.rs index 409dc43d2..76e931d7a 100644 --- a/client/core/rs/src/api/read/mod.rs +++ b/client/core/rs/src/api/read/mod.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::{derive::Request, HasResponse}; +use resolver_api::{HasResponse, Resolve}; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -13,7 +13,6 @@ mod permission; mod procedure; mod provider; mod repo; -mod search; mod server; mod server_template; mod stack; @@ -35,7 +34,6 @@ pub use permission::*; pub use procedure::*; pub use provider::*; pub use repo::*; -pub use search::*; pub use server::*; pub use server_template::*; pub use stack::*; @@ -60,10 +58,11 @@ pub trait KomodoReadRequest: HasResponse {} /// Response: [GetVersionResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetVersionResponse)] +#[error(serror::Error)] pub struct GetVersion {} /// Response for [GetVersion]. @@ -80,10 +79,11 @@ pub struct GetVersionResponse { /// Response: [GetCoreInfoResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetCoreInfoResponse)] +#[error(serror::Error)] pub struct GetCoreInfo {} /// Response for [GetCoreInfo]. @@ -119,10 +119,11 @@ pub struct GetCoreInfoResponse { /// - providers on the optional Server or Builder #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListGitProvidersFromConfigResponse)] +#[error(serror::Error)] pub struct ListGitProvidersFromConfig { /// Accepts an optional Server or Builder target to expand the core list with /// providers available on that specific resource. @@ -143,10 +144,11 @@ pub type ListGitProvidersFromConfigResponse = Vec; /// - registries on the optional Server or Builder #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDockerRegistriesFromConfigResponse)] +#[error(serror::Error)] pub struct ListDockerRegistriesFromConfig { /// Accepts an optional Server or Builder target to expand the core list with /// providers available on that specific resource. @@ -162,10 +164,11 @@ pub type ListDockerRegistriesFromConfigResponse = Vec; /// Response: [ListSecretsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListSecretsResponse)] +#[error(serror::Error)] pub struct ListSecrets { /// Accepts an optional Server or Builder target to expand the core list with /// providers available on that specific resource. diff --git a/client/core/rs/src/api/read/permission.rs b/client/core/rs/src/api/read/permission.rs index 98cfa31cc..c812abc03 100644 --- a/client/core/rs/src/api/read/permission.rs +++ b/client/core/rs/src/api/read/permission.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoReadRequest; /// Response: [ListPermissionsResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListPermissionsResponse)] +#[error(serror::Error)] pub struct ListPermissions {} #[typeshare] @@ -31,10 +32,11 @@ pub type ListPermissionsResponse = Vec; /// Response: [PermissionLevel] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetPermissionLevelResponse)] +#[error(serror::Error)] pub struct GetPermissionLevel { /// The target to get user permission on. pub target: ResourceTarget, @@ -49,10 +51,11 @@ pub type GetPermissionLevelResponse = PermissionLevel; /// Response: [ListUserTargetPermissionsResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListUserTargetPermissionsResponse)] +#[error(serror::Error)] pub struct ListUserTargetPermissions { /// Specify either a user or a user group. pub user_target: UserTarget, diff --git a/client/core/rs/src/api/read/procedure.rs b/client/core/rs/src/api/read/procedure.rs index 9663cd6d7..fe13edcc7 100644 --- a/client/core/rs/src/api/read/procedure.rs +++ b/client/core/rs/src/api/read/procedure.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// Get a specific procedure. Response: [Procedure]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetProcedureResponse)] +#[error(serror::Error)] pub struct GetProcedure { /// Id or name #[serde(alias = "id", alias = "name")] @@ -32,10 +33,11 @@ pub type GetProcedureResponse = Procedure; /// List procedures matching optional query. Response: [ListProceduresResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListProceduresResponse)] +#[error(serror::Error)] pub struct ListProcedures { /// optional structured query to filter procedures. #[serde(default)] @@ -50,10 +52,11 @@ pub type ListProceduresResponse = Vec; /// List procedures matching optional query. Response: [ListFullProceduresResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullProceduresResponse)] +#[error(serror::Error)] pub struct ListFullProcedures { /// optional structured query to filter procedures. #[serde(default)] @@ -68,10 +71,11 @@ pub type ListFullProceduresResponse = Vec; /// Get current action state for the procedure. Response: [ProcedureActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetProcedureActionStateResponse)] +#[error(serror::Error)] pub struct GetProcedureActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -87,10 +91,11 @@ pub type GetProcedureActionStateResponse = ProcedureActionState; /// Response: [GetProceduresSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetProceduresSummaryResponse)] +#[error(serror::Error)] pub struct GetProceduresSummary {} /// Response for [GetProceduresSummary]. diff --git a/client/core/rs/src/api/read/provider.rs b/client/core/rs/src/api/read/provider.rs index fd7fb4984..c08b34dae 100644 --- a/client/core/rs/src/api/read/provider.rs +++ b/client/core/rs/src/api/read/provider.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -13,10 +13,11 @@ use super::KomodoReadRequest; /// Response: [GetGitProviderAccountResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetGitProviderAccountResponse)] +#[error(serror::Error)] pub struct GetGitProviderAccount { pub id: String, } @@ -30,10 +31,11 @@ pub type GetGitProviderAccountResponse = GitProviderAccount; /// Response: [ListGitProviderAccountsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListGitProviderAccountsResponse)] +#[error(serror::Error)] pub struct ListGitProviderAccounts { /// Optionally filter by accounts with a specific domain. pub domain: Option, @@ -50,10 +52,11 @@ pub type ListGitProviderAccountsResponse = Vec; /// Response: [GetDockerRegistryAccountResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetDockerRegistryAccountResponse)] +#[error(serror::Error)] pub struct GetDockerRegistryAccount { pub id: String, } @@ -67,10 +70,11 @@ pub type GetDockerRegistryAccountResponse = DockerRegistryAccount; /// Response: [ListDockerRegistryAccountsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDockerRegistryAccountsResponse)] +#[error(serror::Error)] pub struct ListDockerRegistryAccounts { /// Optionally filter by accounts with a specific domain. pub domain: Option, diff --git a/client/core/rs/src/api/read/repo.rs b/client/core/rs/src/api/read/repo.rs index 4f8e7b5f3..16f2521ef 100644 --- a/client/core/rs/src/api/read/repo.rs +++ b/client/core/rs/src/api/read/repo.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// Get a specific repo. Response: [Repo]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(Repo)] +#[error(serror::Error)] pub struct GetRepo { /// Id or name #[serde(alias = "id", alias = "name")] @@ -32,10 +33,11 @@ pub type GetRepoResponse = Repo; /// List repos matching optional query. Response: [ListReposResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListReposResponse)] +#[error(serror::Error)] pub struct ListRepos { /// optional structured query to filter repos. #[serde(default)] @@ -50,10 +52,11 @@ pub type ListReposResponse = Vec; /// List repos matching optional query. Response: [ListFullReposResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullReposResponse)] +#[error(serror::Error)] pub struct ListFullRepos { /// optional structured query to filter repos. #[serde(default)] @@ -68,10 +71,11 @@ pub type ListFullReposResponse = Vec; /// Get current action state for the repo. Response: [RepoActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetRepoActionStateResponse)] +#[error(serror::Error)] pub struct GetRepoActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -87,10 +91,11 @@ pub type GetRepoActionStateResponse = RepoActionState; /// Response: [GetReposSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetReposSummaryResponse)] +#[error(serror::Error)] pub struct GetReposSummary {} /// Response for [GetReposSummary] @@ -118,10 +123,11 @@ pub struct GetReposSummaryResponse { /// Get a target Repo's configured webhooks. Response: [GetRepoWebhooksEnabledResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetRepoWebhooksEnabledResponse)] +#[error(serror::Error)] pub struct GetRepoWebhooksEnabled { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/read/search.rs b/client/core/rs/src/api/read/search.rs deleted file mode 100644 index 67f1e2f77..000000000 --- a/client/core/rs/src/api/read/search.rs +++ /dev/null @@ -1,46 +0,0 @@ -use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; -use serde::{Deserialize, Serialize}; -use typeshare::typeshare; - -use crate::entities::{ - build::BuildListItem, deployment::DeploymentListItem, - procedure::ProcedureListItem, repo::RepoListItem, - server::ServerListItem, MongoDocument, ResourceTargetVariant, -}; - -use super::KomodoReadRequest; - -// - -/// Find resources matching a common query. Response: [FindResourcesResponse]. -#[typeshare] -#[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, -)] -#[empty_traits(KomodoReadRequest)] -#[response(FindResourcesResponse)] -pub struct FindResources { - /// The mongo query as JSON - #[serde(default)] - pub query: MongoDocument, - /// The resource variants to include in the response. - #[serde(default)] - pub resources: Vec, -} - -/// Response for [FindResources]. -#[typeshare] -#[derive(Serialize, Deserialize, Debug, Clone, Default)] -pub struct FindResourcesResponse { - /// The matching servers. - pub servers: Vec, - /// The matching deployments. - pub deployments: Vec, - /// The matching builds. - pub builds: Vec, - /// The matching repos. - pub repos: Vec, - /// The matching procedures. - pub procedures: Vec, -} diff --git a/client/core/rs/src/api/read/server.rs b/client/core/rs/src/api/read/server.rs index a9af84e17..3ab08fbf9 100644 --- a/client/core/rs/src/api/read/server.rs +++ b/client/core/rs/src/api/read/server.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -29,10 +29,11 @@ use super::KomodoReadRequest; /// Get a specific server. Response: [Server]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(Server)] +#[error(serror::Error)] pub struct GetServer { /// Id or name #[serde(alias = "id", alias = "name")] @@ -47,10 +48,11 @@ pub type GetServerResponse = Server; /// List servers matching optional query. Response: [ListServersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListServersResponse)] +#[error(serror::Error)] pub struct ListServers { /// optional structured query to filter servers. #[serde(default)] @@ -65,10 +67,11 @@ pub type ListServersResponse = Vec; /// List servers matching optional query. Response: [ListFullServersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullServersResponse)] +#[error(serror::Error)] pub struct ListFullServers { /// optional structured query to filter servers. #[serde(default)] @@ -83,10 +86,11 @@ pub type ListFullServersResponse = Vec; /// Get the state of the target server. Response: [GetServerStateResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetServerStateResponse)] +#[error(serror::Error)] pub struct GetServerState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -106,10 +110,11 @@ pub struct GetServerStateResponse { /// Get current action state for the servers. Response: [ServerActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ServerActionState)] +#[error(serror::Error)] pub struct GetServerActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -125,10 +130,11 @@ pub type GetServerActionStateResponse = ServerActionState; /// Response: [GetPeripheryVersionResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetPeripheryVersionResponse)] +#[error(serror::Error)] pub struct GetPeripheryVersion { /// Id or name #[serde(alias = "id", alias = "name")] @@ -148,10 +154,11 @@ pub struct GetPeripheryVersionResponse { /// List the docker networks on the server. Response: [ListDockerNetworksResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDockerNetworksResponse)] +#[error(serror::Error)] pub struct ListDockerNetworks { /// Id or name #[serde(alias = "id", alias = "name")] @@ -166,10 +173,11 @@ pub type ListDockerNetworksResponse = Vec; /// Inspect a docker network on the server. Response: [InspectDockerNetworkResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(InspectDockerNetworkResponse)] +#[error(serror::Error)] pub struct InspectDockerNetwork { /// Id or name #[serde(alias = "id", alias = "name")] @@ -187,10 +195,11 @@ pub type InspectDockerNetworkResponse = Network; /// Response: [ListDockerImagesResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDockerImagesResponse)] +#[error(serror::Error)] pub struct ListDockerImages { /// Id or name #[serde(alias = "id", alias = "name")] @@ -205,10 +214,11 @@ pub type ListDockerImagesResponse = Vec; /// Inspect a docker image on the server. Response: [Image]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(InspectDockerImageResponse)] +#[error(serror::Error)] pub struct InspectDockerImage { /// Id or name #[serde(alias = "id", alias = "name")] @@ -225,10 +235,11 @@ pub type InspectDockerImageResponse = Image; /// Get image history from the server. Response: [ListDockerImageHistoryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDockerImageHistoryResponse)] +#[error(serror::Error)] pub struct ListDockerImageHistory { /// Id or name #[serde(alias = "id", alias = "name")] @@ -247,10 +258,11 @@ pub type ListDockerImageHistoryResponse = /// Response: [ListDockerContainersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDockerContainersResponse)] +#[error(serror::Error)] pub struct ListDockerContainers { /// Id or name #[serde(alias = "id", alias = "name")] @@ -266,10 +278,11 @@ pub type ListDockerContainersResponse = Vec; /// Response: [ListDockerContainersResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListAllDockerContainersResponse)] +#[error(serror::Error)] pub struct ListAllDockerContainers { /// Filter by server id or name. #[serde(default)] @@ -284,10 +297,11 @@ pub type ListAllDockerContainersResponse = Vec; /// Inspect a docker container on the server. Response: [Container]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(InspectDockerContainerResponse)] +#[error(serror::Error)] pub struct InspectDockerContainer { /// Id or name #[serde(alias = "id", alias = "name")] @@ -307,10 +321,11 @@ pub type InspectDockerContainerResponse = Container; /// Note. This call will hit the underlying server directly for most up to date log. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetContainerLogResponse)] +#[error(serror::Error)] pub struct GetContainerLog { /// Id or name #[serde(alias = "id", alias = "name")] @@ -342,10 +357,11 @@ pub type GetContainerLogResponse = Log; /// Note. This call will hit the underlying server directly for most up to date log. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(SearchContainerLogResponse)] +#[error(serror::Error)] pub struct SearchContainerLog { /// Id or name #[serde(alias = "id", alias = "name")] @@ -376,10 +392,11 @@ pub type SearchContainerLogResponse = Log; /// Find the attached resource for a container. Either Deployment or Stack. Response: [GetResourceMatchingContainerResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetResourceMatchingContainerResponse)] +#[error(serror::Error)] pub struct GetResourceMatchingContainer { /// Id or name #[serde(alias = "id", alias = "name")] @@ -401,10 +418,11 @@ pub struct GetResourceMatchingContainerResponse { /// Response: [ListDockerVolumesResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListDockerVolumesResponse)] +#[error(serror::Error)] pub struct ListDockerVolumes { /// Id or name #[serde(alias = "id", alias = "name")] @@ -419,10 +437,11 @@ pub type ListDockerVolumesResponse = Vec; /// Inspect a docker volume on the server. Response: [Volume]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(InspectDockerVolumeResponse)] +#[error(serror::Error)] pub struct InspectDockerVolume { /// Id or name #[serde(alias = "id", alias = "name")] @@ -440,10 +459,11 @@ pub type InspectDockerVolumeResponse = Volume; /// Response: [ListComposeProjectsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListComposeProjectsResponse)] +#[error(serror::Error)] pub struct ListComposeProjects { /// Id or name #[serde(alias = "id", alias = "name")] @@ -459,10 +479,11 @@ pub type ListComposeProjectsResponse = Vec; /// Response: [SystemInformation]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetSystemInformationResponse)] +#[error(serror::Error)] pub struct GetSystemInformation { /// Id or name #[serde(alias = "id", alias = "name")] @@ -481,10 +502,11 @@ pub type GetSystemInformationResponse = SystemInformation; /// to keep it up to date. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetSystemStatsResponse)] +#[error(serror::Error)] pub struct GetSystemStats { /// Id or name #[serde(alias = "id", alias = "name")] @@ -504,10 +526,11 @@ pub type GetSystemStatsResponse = SystemStats; /// to keep it up to date. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListSystemProcessesResponse)] +#[error(serror::Error)] pub struct ListSystemProcesses { /// Id or name #[serde(alias = "id", alias = "name")] @@ -523,10 +546,11 @@ pub type ListSystemProcessesResponse = Vec; /// Response: [GetHistoricalServerStatsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetHistoricalServerStatsResponse)] +#[error(serror::Error)] pub struct GetHistoricalServerStats { /// Id or name #[serde(alias = "id", alias = "name")] @@ -555,10 +579,11 @@ pub struct GetHistoricalServerStatsResponse { /// Response: [GetServersSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetServersSummaryResponse)] +#[error(serror::Error)] pub struct GetServersSummary {} /// Response for [GetServersSummary]. diff --git a/client/core/rs/src/api/read/server_template.rs b/client/core/rs/src/api/read/server_template.rs index 2a52d1ff5..cd0be5b32 100644 --- a/client/core/rs/src/api/read/server_template.rs +++ b/client/core/rs/src/api/read/server_template.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// Get a specific server template by id or name. Response: [ServerTemplate]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetServerTemplateResponse)] +#[error(serror::Error)] pub struct GetServerTemplate { /// Id or name #[serde(alias = "id", alias = "name")] @@ -32,10 +33,11 @@ pub type GetServerTemplateResponse = ServerTemplate; /// List server templates matching structured query. Response: [ListServerTemplatesResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListServerTemplatesResponse)] +#[error(serror::Error)] pub struct ListServerTemplates { #[serde(default)] pub query: ServerTemplateQuery, @@ -49,10 +51,11 @@ pub type ListServerTemplatesResponse = Vec; /// List server templates matching structured query. Response: [ListFullServerTemplatesResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullServerTemplatesResponse)] +#[error(serror::Error)] pub struct ListFullServerTemplates { #[serde(default)] pub query: ServerTemplateQuery, @@ -67,10 +70,11 @@ pub type ListFullServerTemplatesResponse = Vec; /// Response: [GetServerTemplatesSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetServerTemplatesSummaryResponse)] +#[error(serror::Error)] pub struct GetServerTemplatesSummary {} /// Response for [GetServerTemplatesSummary]. diff --git a/client/core/rs/src/api/read/stack.rs b/client/core/rs/src/api/read/stack.rs index 3c3be1c6d..1a49beb7a 100644 --- a/client/core/rs/src/api/read/stack.rs +++ b/client/core/rs/src/api/read/stack.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -18,10 +18,11 @@ use super::KomodoReadRequest; /// Get a specific stack. Response: [Stack]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetStackResponse)] +#[error(serror::Error)] pub struct GetStack { /// Id or name #[serde(alias = "id", alias = "name")] @@ -36,10 +37,11 @@ pub type GetStackResponse = Stack; /// Lists a specific stacks services (the containers). Response: [ListStackServicesResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListStackServicesResponse)] +#[error(serror::Error)] pub struct ListStackServices { /// Id or name #[serde(alias = "id", alias = "name")] @@ -54,10 +56,11 @@ pub type ListStackServicesResponse = Vec; /// Get a stack service's log. Response: [GetStackServiceLogResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetStackServiceLogResponse)] +#[error(serror::Error)] pub struct GetStackServiceLog { /// Id or name #[serde(alias = "id", alias = "name")] @@ -89,10 +92,11 @@ pub type GetStackServiceLogResponse = Log; /// Note. This call will hit the underlying server directly for most up to date log. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(SearchStackServiceLogResponse)] +#[error(serror::Error)] pub struct SearchStackServiceLog { /// Id or name #[serde(alias = "id", alias = "name")] @@ -124,10 +128,11 @@ pub type SearchStackServiceLogResponse = Log; /// Useful to offer suggestions. Response: [ListCommonStackExtraArgsResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListCommonStackExtraArgsResponse)] +#[error(serror::Error)] pub struct ListCommonStackExtraArgs { /// optional structured query to filter stacks. #[serde(default)] @@ -143,10 +148,11 @@ pub type ListCommonStackExtraArgsResponse = Vec; /// Useful to offer suggestions. Response: [ListCommonStackBuildExtraArgsResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListCommonStackBuildExtraArgsResponse)] +#[error(serror::Error)] pub struct ListCommonStackBuildExtraArgs { /// optional structured query to filter stacks. #[serde(default)] @@ -161,10 +167,11 @@ pub type ListCommonStackBuildExtraArgsResponse = Vec; /// List stacks matching optional query. Response: [ListStacksResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListStacksResponse)] +#[error(serror::Error)] pub struct ListStacks { /// optional structured query to filter syncs. #[serde(default)] @@ -179,10 +186,11 @@ pub type ListStacksResponse = Vec; /// List stacks matching optional query. Response: [ListFullStacksResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullStacksResponse)] +#[error(serror::Error)] pub struct ListFullStacks { /// optional structured query to filter stacks. #[serde(default)] @@ -197,10 +205,11 @@ pub type ListFullStacksResponse = Vec; /// Get current action state for the stack. Response: [StackActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetStackActionStateResponse)] +#[error(serror::Error)] pub struct GetStackActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -216,10 +225,11 @@ pub type GetStackActionStateResponse = StackActionState; /// Response: [GetStacksSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetStacksSummaryResponse)] +#[error(serror::Error)] pub struct GetStacksSummary {} /// Response for [GetStacksSummary] @@ -245,10 +255,11 @@ pub struct GetStacksSummaryResponse { /// Get a target stack's configured webhooks. Response: [GetStackWebhooksEnabledResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetStackWebhooksEnabledResponse)] +#[error(serror::Error)] pub struct GetStackWebhooksEnabled { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/read/sync.rs b/client/core/rs/src/api/read/sync.rs index a49f3bd17..54623c887 100644 --- a/client/core/rs/src/api/read/sync.rs +++ b/client/core/rs/src/api/read/sync.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoReadRequest; /// Get a specific sync. Response: [ResourceSync]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ResourceSync)] +#[error(serror::Error)] pub struct GetResourceSync { /// Id or name #[serde(alias = "id", alias = "name")] @@ -33,10 +34,11 @@ pub type GetResourceSyncResponse = ResourceSync; /// List syncs matching optional query. Response: [ListResourceSyncsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListResourceSyncsResponse)] +#[error(serror::Error)] pub struct ListResourceSyncs { /// optional structured query to filter syncs. #[serde(default)] @@ -51,10 +53,11 @@ pub type ListResourceSyncsResponse = Vec; /// List syncs matching optional query. Response: [ListFullResourceSyncsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListFullResourceSyncsResponse)] +#[error(serror::Error)] pub struct ListFullResourceSyncs { /// optional structured query to filter syncs. #[serde(default)] @@ -69,10 +72,11 @@ pub type ListFullResourceSyncsResponse = Vec; /// Get current action state for the sync. Response: [ResourceSyncActionState]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetResourceSyncActionStateResponse)] +#[error(serror::Error)] pub struct GetResourceSyncActionState { /// Id or name #[serde(alias = "id", alias = "name")] @@ -88,10 +92,11 @@ pub type GetResourceSyncActionStateResponse = ResourceSyncActionState; /// Response: [GetResourceSyncsSummaryResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetResourceSyncsSummaryResponse)] +#[error(serror::Error)] pub struct GetResourceSyncsSummary {} /// Response for [GetResourceSyncsSummary] @@ -117,10 +122,11 @@ pub struct GetResourceSyncsSummaryResponse { /// Get a target Sync's configured webhooks. Response: [GetSyncWebhooksEnabledResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetSyncWebhooksEnabledResponse)] +#[error(serror::Error)] pub struct GetSyncWebhooksEnabled { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/read/tag.rs b/client/core/rs/src/api/read/tag.rs index 169b703c2..f90a101a4 100644 --- a/client/core/rs/src/api/read/tag.rs +++ b/client/core/rs/src/api/read/tag.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -12,10 +12,11 @@ use super::KomodoReadRequest; /// Get data for a specific tag. Response [Tag]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetTagResponse)] +#[error(serror::Error)] pub struct GetTag { /// Id or name #[serde(alias = "id", alias = "name")] @@ -31,10 +32,11 @@ pub type GetTagResponse = Tag; /// Response: [ListTagsResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListTagsResponse)] +#[error(serror::Error)] pub struct ListTags { pub query: Option, } diff --git a/client/core/rs/src/api/read/toml.rs b/client/core/rs/src/api/read/toml.rs index afcef36b3..16f037a6c 100644 --- a/client/core/rs/src/api/read/toml.rs +++ b/client/core/rs/src/api/read/toml.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -21,10 +21,11 @@ pub struct TomlResponse { /// Response: [TomlResponse]. #[typeshare] #[derive( - Debug, Clone, Default, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Default, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ExportAllResourcesToTomlResponse)] +#[error(serror::Error)] pub struct ExportAllResourcesToToml { /// Tag name or id. Empty array will not filter by tag. #[serde(default)] @@ -40,10 +41,11 @@ pub type ExportAllResourcesToTomlResponse = TomlResponse; /// Response: [TomlResponse]. #[typeshare] #[derive( - Debug, Clone, Default, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Default, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ExportResourcesToTomlResponse)] +#[error(serror::Error)] pub struct ExportResourcesToToml { /// The targets to include in the export. #[serde(default)] diff --git a/client/core/rs/src/api/read/update.rs b/client/core/rs/src/api/read/update.rs index f153a871f..e1f9a0e05 100644 --- a/client/core/rs/src/api/read/update.rs +++ b/client/core/rs/src/api/read/update.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetUpdateResponse)] +#[error(serror::Error)] pub struct GetUpdate { /// The update id. pub id: String, @@ -32,10 +33,11 @@ pub type GetUpdateResponse = Update; /// More recent updates will be returned first. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListUpdatesResponse)] +#[error(serror::Error)] pub struct ListUpdates { /// An optional mongo query to filter the updates. pub query: Option, diff --git a/client/core/rs/src/api/read/user.rs b/client/core/rs/src/api/read/user.rs index dbf51b957..bd9f73154 100644 --- a/client/core/rs/src/api/read/user.rs +++ b/client/core/rs/src/api/read/user.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -11,10 +11,11 @@ use super::KomodoReadRequest; /// Response: [ListApiKeysResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListApiKeysResponse)] +#[error(serror::Error)] pub struct ListApiKeys {} #[typeshare] @@ -28,10 +29,11 @@ pub type ListApiKeysResponse = Vec; /// Response: [ListApiKeysForServiceUserResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListApiKeysForServiceUserResponse)] +#[error(serror::Error)] pub struct ListApiKeysForServiceUser { /// Id or username #[serde(alias = "id", alias = "username")] @@ -48,10 +50,11 @@ pub type ListApiKeysForServiceUserResponse = Vec; /// Response: [FindUserResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(FindUserResponse)] +#[error(serror::Error)] pub struct FindUser { /// Id or username #[serde(alias = "id", alias = "username")] @@ -68,10 +71,11 @@ pub type FindUserResponse = User; /// Response: [ListUsersResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListUsersResponse)] +#[error(serror::Error)] pub struct ListUsers {} #[typeshare] @@ -83,10 +87,11 @@ pub type ListUsersResponse = Vec; /// Response: [GetUsernameResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetUsernameResponse)] +#[error(serror::Error)] pub struct GetUsername { /// The id of the user. pub user_id: String, diff --git a/client/core/rs/src/api/read/user_group.rs b/client/core/rs/src/api/read/user_group.rs index 76103fa3b..9188fba32 100644 --- a/client/core/rs/src/api/read/user_group.rs +++ b/client/core/rs/src/api/read/user_group.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -11,10 +11,11 @@ use super::KomodoReadRequest; /// Response: [UserGroup]. #[typeshare] #[derive( - Debug, Clone, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetUserGroupResponse)] +#[error(serror::Error)] pub struct GetUserGroup { /// Name or Id pub user_group: String, @@ -31,10 +32,11 @@ pub type GetUserGroupResponse = UserGroup; /// and users can see user groups to which they belong. #[typeshare] #[derive( - Debug, Clone, Default, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Default, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListUserGroupsResponse)] +#[error(serror::Error)] pub struct ListUserGroups {} #[typeshare] diff --git a/client/core/rs/src/api/read/variable.rs b/client/core/rs/src/api/read/variable.rs index d034f073c..5ec082758 100644 --- a/client/core/rs/src/api/read/variable.rs +++ b/client/core/rs/src/api/read/variable.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoReadRequest; /// secret variables will have their values obscured. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(GetVariableResponse)] +#[error(serror::Error)] pub struct GetVariable { /// The name of the variable to get. pub name: String, @@ -35,10 +36,11 @@ pub type GetVariableResponse = Variable; /// secret variables will have their values obscured. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Default, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Default, Resolve, EmptyTraits, )] #[empty_traits(KomodoReadRequest)] #[response(ListVariablesResponse)] +#[error(serror::Error)] pub struct ListVariables {} #[typeshare] diff --git a/client/core/rs/src/api/user.rs b/client/core/rs/src/api/user.rs index 3c18e5de5..d130275a3 100644 --- a/client/core/rs/src/api/user.rs +++ b/client/core/rs/src/api/user.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::{derive::Request, HasResponse}; +use resolver_api::{HasResponse, Resolve}; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -13,10 +13,11 @@ pub trait KomodoUserRequest: HasResponse {} /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoUserRequest)] #[response(PushRecentlyViewedResponse)] +#[error(serror::Error)] pub struct PushRecentlyViewed { /// The target to push. pub resource: ResourceTarget, @@ -32,10 +33,11 @@ pub type PushRecentlyViewedResponse = NoData; /// Response: [NoData] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoUserRequest)] #[response(SetLastSeenUpdateResponse)] +#[error(serror::Error)] pub struct SetLastSeenUpdate {} #[typeshare] @@ -50,10 +52,11 @@ pub type SetLastSeenUpdateResponse = NoData; /// to get the secret later. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoUserRequest)] #[response(CreateApiKeyResponse)] +#[error(serror::Error)] pub struct CreateApiKey { /// The name for the api key. pub name: String, @@ -84,10 +87,11 @@ pub struct CreateApiKeyResponse { /// Response: [NoData] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoUserRequest)] #[response(DeleteApiKeyResponse)] +#[error(serror::Error)] pub struct DeleteApiKey { /// The key which the user intends to delete. pub key: String, diff --git a/client/core/rs/src/api/write/action.rs b/client/core/rs/src/api/write/action.rs index 47768ad43..f10fdba38 100644 --- a/client/core/rs/src/api/write/action.rs +++ b/client/core/rs/src/api/write/action.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,10 +16,11 @@ use super::KomodoWriteRequest; /// Create a action. Response: [Action]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Action)] +#[error(serror::Error)] pub struct CreateAction { /// The name given to newly created action. pub name: String, @@ -34,10 +35,11 @@ pub struct CreateAction { /// of the action at the given `id`. Response: [Action]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Action)] +#[error(serror::Error)] pub struct CopyAction { /// The name of the new action. pub name: String, @@ -51,10 +53,11 @@ pub struct CopyAction { /// Response: [Action] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Action)] +#[error(serror::Error)] pub struct DeleteAction { /// The id or name of the action to delete. pub id: String, @@ -72,10 +75,11 @@ pub struct DeleteAction { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Action)] +#[error(serror::Error)] pub struct UpdateAction { /// The id of the action to update. pub id: String, @@ -89,10 +93,12 @@ pub struct UpdateAction { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameAction { /// The id or name of the Action to rename. pub id: String, @@ -104,10 +110,11 @@ pub struct RenameAction { /// passed in request. Response: [CreateActionWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateActionWebhookResponse)] +#[error(serror::Error)] pub struct CreateActionWebhook { /// Id or name #[serde(alias = "id", alias = "name")] @@ -123,10 +130,11 @@ pub type CreateActionWebhookResponse = NoData; /// passed in request. Response: [DeleteActionWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteActionWebhookResponse)] +#[error(serror::Error)] pub struct DeleteActionWebhook { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/write/alerter.rs b/client/core/rs/src/api/write/alerter.rs index 7072f813b..e983c2754 100644 --- a/client/core/rs/src/api/write/alerter.rs +++ b/client/core/rs/src/api/write/alerter.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoWriteRequest; /// Create an alerter. Response: [Alerter]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Alerter)] +#[error(serror::Error)] pub struct CreateAlerter { /// The name given to newly created alerter. pub name: String, @@ -33,10 +34,11 @@ pub struct CreateAlerter { /// of the alerter at the given `id`. Response: [Alerter]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Alerter)] +#[error(serror::Error)] pub struct CopyAlerter { /// The name of the new alerter. pub name: String, @@ -50,10 +52,11 @@ pub struct CopyAlerter { /// Response: [Alerter] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Alerter)] +#[error(serror::Error)] pub struct DeleteAlerter { /// The id or name of the alerter to delete. pub id: String, @@ -69,10 +72,11 @@ pub struct DeleteAlerter { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Alerter)] +#[error(serror::Error)] pub struct UpdateAlerter { /// The id of the alerter to update. pub id: String, @@ -86,10 +90,12 @@ pub struct UpdateAlerter { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameAlerter { /// The id or name of the Alerter to rename. pub id: String, diff --git a/client/core/rs/src/api/write/api_key.rs b/client/core/rs/src/api/write/api_key.rs index 95a016d20..0317a622f 100644 --- a/client/core/rs/src/api/write/api_key.rs +++ b/client/core/rs/src/api/write/api_key.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,10 +16,11 @@ use super::KomodoWriteRequest; /// Response: [CreateApiKeyResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateApiKeyForServiceUserResponse)] +#[error(serror::Error)] pub struct CreateApiKeyForServiceUser { /// Must be service user pub user_id: String, @@ -40,10 +41,11 @@ pub type CreateApiKeyForServiceUserResponse = CreateApiKeyResponse; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteApiKeyForServiceUserResponse)] +#[error(serror::Error)] pub struct DeleteApiKeyForServiceUser { pub key: String, } diff --git a/client/core/rs/src/api/write/build.rs b/client/core/rs/src/api/write/build.rs index c293787b5..c81c08d7c 100644 --- a/client/core/rs/src/api/write/build.rs +++ b/client/core/rs/src/api/write/build.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,10 +16,11 @@ use super::KomodoWriteRequest; /// Create a build. Response: [Build]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Build)] +#[error(serror::Error)] pub struct CreateBuild { /// The name given to newly created build. pub name: String, @@ -34,10 +35,11 @@ pub struct CreateBuild { /// of the build at the given `id`. Response: [Build]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Build)] +#[error(serror::Error)] pub struct CopyBuild { /// The name of the new build. pub name: String, @@ -51,10 +53,11 @@ pub struct CopyBuild { /// Response: [Build] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Build)] +#[error(serror::Error)] pub struct DeleteBuild { /// The id or name of the build to delete. pub id: String, @@ -72,10 +75,11 @@ pub struct DeleteBuild { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Build)] +#[error(serror::Error)] pub struct UpdateBuild { /// The id of the build to update. pub id: String, @@ -89,10 +93,12 @@ pub struct UpdateBuild { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameBuild { /// The id or name of the Build to rename. pub id: String, @@ -105,10 +111,11 @@ pub struct RenameBuild { /// Trigger a refresh of the cached latest hash and message. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(NoData)] +#[error(serror::Error)] pub struct RefreshBuildCache { /// Id or name #[serde(alias = "id", alias = "name")] @@ -121,10 +128,11 @@ pub struct RefreshBuildCache { /// passed in request. Response: [CreateBuildWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateBuildWebhookResponse)] +#[error(serror::Error)] pub struct CreateBuildWebhook { /// Id or name #[serde(alias = "id", alias = "name")] @@ -140,10 +148,11 @@ pub type CreateBuildWebhookResponse = NoData; /// passed in request. Response: [CreateBuildWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteBuildWebhookResponse)] +#[error(serror::Error)] pub struct DeleteBuildWebhook { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/write/builder.rs b/client/core/rs/src/api/write/builder.rs index f929946a9..5b5e3a2b7 100644 --- a/client/core/rs/src/api/write/builder.rs +++ b/client/core/rs/src/api/write/builder.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoWriteRequest; /// Create a builder. Response: [Builder]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Builder)] +#[error(serror::Error)] pub struct CreateBuilder { /// The name given to newly created builder. pub name: String, @@ -33,10 +34,11 @@ pub struct CreateBuilder { /// of the builder at the given `id`. Response: [Builder] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Builder)] +#[error(serror::Error)] pub struct CopyBuilder { /// The name of the new builder. pub name: String, @@ -50,10 +52,11 @@ pub struct CopyBuilder { /// Response: [Builder] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Builder)] +#[error(serror::Error)] pub struct DeleteBuilder { /// The id or name of the builder to delete. pub id: String, @@ -71,10 +74,11 @@ pub struct DeleteBuilder { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Builder)] +#[error(serror::Error)] pub struct UpdateBuilder { /// The id of the builder to update. pub id: String, @@ -88,10 +92,12 @@ pub struct UpdateBuilder { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameBuilder { /// The id or name of the Builder to rename. pub id: String, diff --git a/client/core/rs/src/api/write/deployment.rs b/client/core/rs/src/api/write/deployment.rs index b7c6e6505..a62a1e3b6 100644 --- a/client/core/rs/src/api/write/deployment.rs +++ b/client/core/rs/src/api/write/deployment.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoWriteRequest; /// Create a deployment. Response: [Deployment]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Deployment)] +#[error(serror::Error)] pub struct CreateDeployment { /// The name given to newly created deployment. pub name: String, @@ -33,10 +34,11 @@ pub struct CreateDeployment { /// of the deployment at the given `id`. Response: [Deployment] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Deployment)] +#[error(serror::Error)] pub struct CopyDeployment { /// The name of the new deployment. pub name: String, @@ -49,10 +51,11 @@ pub struct CopyDeployment { /// Create a Deployment from an existing container. Response: [Deployment]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Deployment)] +#[error(serror::Error)] pub struct CreateDeploymentFromContainer { /// The name or id of the existing container. pub name: String, @@ -69,10 +72,11 @@ pub struct CreateDeploymentFromContainer { /// the deployment clean up. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Deployment)] +#[error(serror::Error)] pub struct DeleteDeployment { /// The id or name of the deployment to delete. pub id: String, @@ -93,10 +97,11 @@ pub struct DeleteDeployment { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Deployment)] +#[error(serror::Error)] pub struct UpdateDeployment { /// The deployment id to update. pub id: String, @@ -112,10 +117,12 @@ pub struct UpdateDeployment { /// `docker rename ...`. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameDeployment { /// The id of the deployment to rename. pub id: String, diff --git a/client/core/rs/src/api/write/description.rs b/client/core/rs/src/api/write/description.rs index eb99542bd..7d88c27f5 100644 --- a/client/core/rs/src/api/write/description.rs +++ b/client/core/rs/src/api/write/description.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -11,10 +11,11 @@ use super::KomodoWriteRequest; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateDescriptionResponse)] +#[error(serror::Error)] pub struct UpdateDescription { /// The target resource to set description for. pub target: ResourceTarget, diff --git a/client/core/rs/src/api/write/permissions.rs b/client/core/rs/src/api/write/permissions.rs index 19dc28e00..5c710eda5 100644 --- a/client/core/rs/src/api/write/permissions.rs +++ b/client/core/rs/src/api/write/permissions.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -14,10 +14,11 @@ use super::KomodoWriteRequest; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdatePermissionOnTargetResponse)] +#[error(serror::Error)] pub struct UpdatePermissionOnTarget { /// Specify the user or user group. pub user_target: UserTarget, @@ -36,10 +37,11 @@ pub type UpdatePermissionOnTargetResponse = NoData; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdatePermissionOnResourceTypeResponse)] +#[error(serror::Error)] pub struct UpdatePermissionOnResourceType { /// Specify the user or user group. pub user_target: UserTarget, @@ -58,10 +60,11 @@ pub type UpdatePermissionOnResourceTypeResponse = NoData; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateUserBasePermissionsResponse)] +#[error(serror::Error)] pub struct UpdateUserBasePermissions { /// The target user. pub user_id: String, @@ -80,10 +83,11 @@ pub type UpdateUserBasePermissionsResponse = NoData; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateUserAdminResponse)] +#[error(serror::Error)] pub struct UpdateUserAdmin { /// The target user. pub user_id: String, diff --git a/client/core/rs/src/api/write/procedure.rs b/client/core/rs/src/api/write/procedure.rs index e818c844a..7fe423eb9 100644 --- a/client/core/rs/src/api/write/procedure.rs +++ b/client/core/rs/src/api/write/procedure.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoWriteRequest; /// Create a procedure. Response: [Procedure]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateProcedureResponse)] +#[error(serror::Error)] pub struct CreateProcedure { /// The name given to newly created build. pub name: String, @@ -36,10 +37,11 @@ pub type CreateProcedureResponse = Procedure; /// of the procedure at the given `id`. Response: [Procedure]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CopyProcedureResponse)] +#[error(serror::Error)] pub struct CopyProcedure { /// The name of the new procedure. pub name: String, @@ -56,10 +58,11 @@ pub type CopyProcedureResponse = Procedure; /// Response: [Procedure] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteProcedureResponse)] +#[error(serror::Error)] pub struct DeleteProcedure { /// The id or name of the procedure to delete. pub id: String, @@ -80,10 +83,11 @@ pub type DeleteProcedureResponse = Procedure; /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateProcedureResponse)] +#[error(serror::Error)] pub struct UpdateProcedure { /// The id of the procedure to update. pub id: String, @@ -100,10 +104,12 @@ pub type UpdateProcedureResponse = Procedure; /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameProcedure { /// The id or name of the Procedure to rename. pub id: String, diff --git a/client/core/rs/src/api/write/provider.rs b/client/core/rs/src/api/write/provider.rs index f3e99a6ad..415618195 100644 --- a/client/core/rs/src/api/write/provider.rs +++ b/client/core/rs/src/api/write/provider.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -11,10 +11,11 @@ use super::KomodoWriteRequest; /// Response: [GitProviderAccount]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateGitProviderAccountResponse)] +#[error(serror::Error)] pub struct CreateGitProviderAccount { /// The initial account config. Anything in the _id field will be ignored, /// as this is generated on creation. @@ -30,10 +31,11 @@ pub type CreateGitProviderAccountResponse = GitProviderAccount; /// Response: [GitProviderAccount]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateGitProviderAccountResponse)] +#[error(serror::Error)] pub struct UpdateGitProviderAccount { /// The id of the git provider account to update. pub id: String, @@ -50,10 +52,11 @@ pub type UpdateGitProviderAccountResponse = GitProviderAccount; /// Response: [DeleteGitProviderAccountResponse]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteGitProviderAccountResponse)] +#[error(serror::Error)] pub struct DeleteGitProviderAccount { /// The id of the git provider to delete pub id: String, @@ -68,10 +71,11 @@ pub type DeleteGitProviderAccountResponse = GitProviderAccount; /// Response: [DockerRegistryAccount]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateDockerRegistryAccountResponse)] +#[error(serror::Error)] pub struct CreateDockerRegistryAccount { pub account: _PartialDockerRegistryAccount, } @@ -85,10 +89,11 @@ pub type CreateDockerRegistryAccountResponse = DockerRegistryAccount; /// Response: [DockerRegistryAccount]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateDockerRegistryAccountResponse)] +#[error(serror::Error)] pub struct UpdateDockerRegistryAccount { /// The id of the docker registry to update pub id: String, @@ -105,10 +110,11 @@ pub type UpdateDockerRegistryAccountResponse = DockerRegistryAccount; /// Response: [DockerRegistryAccount]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteDockerRegistryAccountResponse)] +#[error(serror::Error)] pub struct DeleteDockerRegistryAccount { /// The id of the docker registry account to delete pub id: String, diff --git a/client/core/rs/src/api/write/repo.rs b/client/core/rs/src/api/write/repo.rs index 3008fc9f6..ec08c43a0 100644 --- a/client/core/rs/src/api/write/repo.rs +++ b/client/core/rs/src/api/write/repo.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,10 +16,11 @@ use super::KomodoWriteRequest; /// Create a repo. Response: [Repo]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Repo)] +#[error(serror::Error)] pub struct CreateRepo { /// The name given to newly created repo. pub name: String, @@ -34,10 +35,11 @@ pub struct CreateRepo { /// of the repo at the given `id`. Response: [Repo]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Repo)] +#[error(serror::Error)] pub struct CopyRepo { /// The name of the new repo. pub name: String, @@ -51,10 +53,11 @@ pub struct CopyRepo { /// Response: [Repo] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Repo)] +#[error(serror::Error)] pub struct DeleteRepo { /// The id or name of the repo to delete. pub id: String, @@ -75,10 +78,11 @@ pub struct DeleteRepo { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Repo)] +#[error(serror::Error)] pub struct UpdateRepo { /// The id of the repo to update. pub id: String, @@ -92,10 +96,12 @@ pub struct UpdateRepo { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameRepo { /// The id or name of the Repo to rename. pub id: String, @@ -108,10 +114,11 @@ pub struct RenameRepo { /// Trigger a refresh of the cached latest hash and message. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(NoData)] +#[error(serror::Error)] pub struct RefreshRepoCache { /// Id or name #[serde(alias = "id", alias = "name")] @@ -132,10 +139,11 @@ pub enum RepoWebhookAction { /// passed in request. Response: [CreateRepoWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateRepoWebhookResponse)] +#[error(serror::Error)] pub struct CreateRepoWebhook { /// Id or name #[serde(alias = "id", alias = "name")] @@ -153,10 +161,11 @@ pub type CreateRepoWebhookResponse = NoData; /// passed in request. Response: [DeleteRepoWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteRepoWebhookResponse)] +#[error(serror::Error)] pub struct DeleteRepoWebhook { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/write/server.rs b/client/core/rs/src/api/write/server.rs index f4dce6ece..d36727be2 100644 --- a/client/core/rs/src/api/write/server.rs +++ b/client/core/rs/src/api/write/server.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoWriteRequest; /// Create a server. Response: [Server]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Server)] +#[error(serror::Error)] pub struct CreateServer { /// The name given to newly created server. pub name: String, @@ -33,10 +34,11 @@ pub struct CreateServer { /// Response: [Server] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Server)] +#[error(serror::Error)] pub struct DeleteServer { /// The id or name of the server to delete. pub id: String, @@ -54,10 +56,11 @@ pub struct DeleteServer { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Server)] +#[error(serror::Error)] pub struct UpdateServer { /// The id or name of the server to update. pub id: String, @@ -71,10 +74,12 @@ pub struct UpdateServer { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameServer { /// The id or name of the Server to rename. pub id: String, @@ -90,10 +95,12 @@ pub struct RenameServer { /// `docker network create {name}` #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct CreateNetwork { /// Server Id or name pub server: String, diff --git a/client/core/rs/src/api/write/server_template.rs b/client/core/rs/src/api/write/server_template.rs index 5ddc4b21f..59e8e2346 100644 --- a/client/core/rs/src/api/write/server_template.rs +++ b/client/core/rs/src/api/write/server_template.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -15,10 +15,11 @@ use super::KomodoWriteRequest; /// Create a server template. Response: [ServerTemplate]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ServerTemplate)] +#[error(serror::Error)] pub struct CreateServerTemplate { /// The name given to newly created server template. pub name: String, @@ -33,10 +34,11 @@ pub struct CreateServerTemplate { /// of the server template at the given `id`. Response: [ServerTemplate] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ServerTemplate)] +#[error(serror::Error)] pub struct CopyServerTemplate { /// The name of the new server template. pub name: String, @@ -50,10 +52,11 @@ pub struct CopyServerTemplate { /// Response: [ServerTemplate] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ServerTemplate)] +#[error(serror::Error)] pub struct DeleteServerTemplate { /// The id or name of the server template to delete. pub id: String, @@ -71,10 +74,11 @@ pub struct DeleteServerTemplate { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ServerTemplate)] +#[error(serror::Error)] pub struct UpdateServerTemplate { /// The id of the server template to update. pub id: String, @@ -88,10 +92,12 @@ pub struct UpdateServerTemplate { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameServerTemplate { /// The id or name of the ServerTemplate to rename. pub id: String, diff --git a/client/core/rs/src/api/write/stack.rs b/client/core/rs/src/api/write/stack.rs index 8cbd0e1ca..587c3b36e 100644 --- a/client/core/rs/src/api/write/stack.rs +++ b/client/core/rs/src/api/write/stack.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -16,10 +16,11 @@ use super::KomodoWriteRequest; /// Create a stack. Response: [Stack]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Stack)] +#[error(serror::Error)] pub struct CreateStack { /// The name given to newly created stack. pub name: String, @@ -34,10 +35,11 @@ pub struct CreateStack { /// of the stack at the given `id`. Response: [Stack]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Stack)] +#[error(serror::Error)] pub struct CopyStack { /// The name of the new stack. pub name: String, @@ -51,10 +53,11 @@ pub struct CopyStack { /// Response: [Stack] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Stack)] +#[error(serror::Error)] pub struct DeleteStack { /// The id or name of the stack to delete. pub id: String, @@ -75,10 +78,11 @@ pub struct DeleteStack { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Stack)] +#[error(serror::Error)] pub struct UpdateStack { /// The id of the Stack to update. pub id: String, @@ -91,10 +95,12 @@ pub struct UpdateStack { /// Rename the stack at id to the given name. Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameStack { /// The id of the stack to rename. pub id: String, @@ -107,10 +113,12 @@ pub struct RenameStack { /// Update file contents in Files on Server or Git Repo mode. Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct WriteStackFileContents { /// The name or id of the target Stack. #[serde(alias = "id", alias = "name")] @@ -130,10 +138,11 @@ pub struct WriteStackFileContents { /// - The latest json, and for repos, the remote contents, hash, and message. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(NoData)] +#[error(serror::Error)] pub struct RefreshStackCache { /// Id or name #[serde(alias = "id", alias = "name")] @@ -153,10 +162,11 @@ pub enum StackWebhookAction { /// passed in request. Response: [CreateStackWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateStackWebhookResponse)] +#[error(serror::Error)] pub struct CreateStackWebhook { /// Id or name #[serde(alias = "id", alias = "name")] @@ -174,10 +184,11 @@ pub type CreateStackWebhookResponse = NoData; /// passed in request. Response: [DeleteStackWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteStackWebhookResponse)] +#[error(serror::Error)] pub struct DeleteStackWebhook { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/write/sync.rs b/client/core/rs/src/api/write/sync.rs index 04816e7bc..2389f1b64 100644 --- a/client/core/rs/src/api/write/sync.rs +++ b/client/core/rs/src/api/write/sync.rs @@ -1,6 +1,6 @@ use clap::Parser; use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -17,10 +17,11 @@ use super::KomodoWriteRequest; /// Create a sync. Response: [ResourceSync]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ResourceSync)] +#[error(serror::Error)] pub struct CreateResourceSync { /// The name given to newly created sync. pub name: String, @@ -35,10 +36,11 @@ pub struct CreateResourceSync { /// of the sync at the given `id`. Response: [ResourceSync]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ResourceSync)] +#[error(serror::Error)] pub struct CopyResourceSync { /// The name of the new sync. pub name: String, @@ -52,10 +54,11 @@ pub struct CopyResourceSync { /// Response: [ResourceSync] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ResourceSync)] +#[error(serror::Error)] pub struct DeleteResourceSync { /// The id or name of the sync to delete. pub id: String, @@ -73,10 +76,11 @@ pub struct DeleteResourceSync { /// field changes occur from out of date local state. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ResourceSync)] +#[error(serror::Error)] pub struct UpdateResourceSync { /// The id of the sync to update. pub id: String, @@ -90,10 +94,12 @@ pub struct UpdateResourceSync { /// Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct RenameResourceSync { /// The id or name of the ResourceSync to rename. pub id: String, @@ -106,10 +112,11 @@ pub struct RenameResourceSync { /// Trigger a refresh of the computed diff logs for view. Response: [ResourceSync] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(ResourceSync)] +#[error(serror::Error)] pub struct RefreshResourceSyncPending { /// Id or name #[serde(alias = "id", alias = "name")] @@ -121,10 +128,12 @@ pub struct RefreshResourceSyncPending { /// Rename the stack at id to the given name. Response: [Update]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct WriteSyncFileContents { /// The name or id of the target Sync. #[serde(alias = "id", alias = "name")] @@ -150,12 +159,14 @@ pub struct WriteSyncFileContents { PartialEq, Serialize, Deserialize, - Request, + Resolve, EmptyTraits, Parser, )] #[empty_traits(KomodoWriteRequest)] #[response(Update)] +#[error(serror::Error)] +#[error(serror::Error)] pub struct CommitSync { /// Id or name #[serde(alias = "id", alias = "name")] @@ -175,10 +186,11 @@ pub enum SyncWebhookAction { /// passed in request. Response: [CreateSyncWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateSyncWebhookResponse)] +#[error(serror::Error)] pub struct CreateSyncWebhook { /// Id or name #[serde(alias = "id", alias = "name")] @@ -196,10 +208,11 @@ pub type CreateSyncWebhookResponse = NoData; /// passed in request. Response: [DeleteSyncWebhookResponse] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteSyncWebhookResponse)] +#[error(serror::Error)] pub struct DeleteSyncWebhook { /// Id or name #[serde(alias = "id", alias = "name")] diff --git a/client/core/rs/src/api/write/tags.rs b/client/core/rs/src/api/write/tags.rs index 2e5baa4d5..fcefd57cc 100644 --- a/client/core/rs/src/api/write/tags.rs +++ b/client/core/rs/src/api/write/tags.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -12,10 +12,11 @@ use super::KomodoWriteRequest; /// Create a tag. Response: [Tag]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Tag)] +#[error(serror::Error)] pub struct CreateTag { /// The name of the tag. pub name: String, @@ -28,10 +29,11 @@ pub struct CreateTag { /// Note. Will also remove this tag from all attached resources. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Tag)] +#[error(serror::Error)] pub struct DeleteTag { /// The id of the tag to delete. pub id: String, @@ -42,10 +44,11 @@ pub struct DeleteTag { /// Rename a tag at id. Response: [Tag]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(Tag)] +#[error(serror::Error)] pub struct RenameTag { /// The id of the tag to rename. pub id: String, @@ -59,10 +62,11 @@ pub struct RenameTag { /// Response: [NoData] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateTagsOnResourceResponse)] +#[error(serror::Error)] pub struct UpdateTagsOnResource { pub target: ResourceTarget, /// Tag Ids diff --git a/client/core/rs/src/api/write/user.rs b/client/core/rs/src/api/write/user.rs index 00e647e41..442e25960 100644 --- a/client/core/rs/src/api/write/user.rs +++ b/client/core/rs/src/api/write/user.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -13,10 +13,11 @@ use super::KomodoWriteRequest; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateUserUsernameResponse)] +#[error(serror::Error)] pub struct UpdateUserUsername { pub username: String, } @@ -30,10 +31,11 @@ pub type UpdateUserUsernameResponse = NoData; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateUserPasswordResponse)] +#[error(serror::Error)] pub struct UpdateUserPassword { pub password: String, } @@ -51,10 +53,11 @@ pub type UpdateUserPasswordResponse = NoData; /// Response: [NoData]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteUserResponse)] +#[error(serror::Error)] pub struct DeleteUser { /// User id or username #[serde(alias = "username", alias = "id")] @@ -70,10 +73,11 @@ pub type DeleteUserResponse = User; /// Response: [User]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateServiceUserResponse)] +#[error(serror::Error)] pub struct CreateServiceUser { /// The username for the service user. pub username: String, @@ -90,10 +94,11 @@ pub type CreateServiceUserResponse = User; /// Response: [User]. #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateServiceUserDescriptionResponse)] +#[error(serror::Error)] pub struct UpdateServiceUserDescription { /// The service user's username pub username: String, diff --git a/client/core/rs/src/api/write/user_group.rs b/client/core/rs/src/api/write/user_group.rs index dbb5f722a..b7e8b37ca 100644 --- a/client/core/rs/src/api/write/user_group.rs +++ b/client/core/rs/src/api/write/user_group.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -10,10 +10,11 @@ use super::KomodoWriteRequest; /// **Admin only.** Create a user group. Response: [UserGroup] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UserGroup)] +#[error(serror::Error)] pub struct CreateUserGroup { /// The name to assign to the new UserGroup pub name: String, @@ -24,10 +25,11 @@ pub struct CreateUserGroup { /// **Admin only.** Rename a user group. Response: [UserGroup] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UserGroup)] +#[error(serror::Error)] pub struct RenameUserGroup { /// The id of the UserGroup pub id: String, @@ -40,10 +42,11 @@ pub struct RenameUserGroup { /// **Admin only.** Delete a user group. Response: [UserGroup] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UserGroup)] +#[error(serror::Error)] pub struct DeleteUserGroup { /// The id of the UserGroup pub id: String, @@ -54,10 +57,11 @@ pub struct DeleteUserGroup { /// **Admin only.** Add a user to a user group. Response: [UserGroup] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UserGroup)] +#[error(serror::Error)] pub struct AddUserToUserGroup { /// The name or id of UserGroup that user should be added to. pub user_group: String, @@ -70,10 +74,11 @@ pub struct AddUserToUserGroup { /// **Admin only.** Remove a user from a user group. Response: [UserGroup] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UserGroup)] +#[error(serror::Error)] pub struct RemoveUserFromUserGroup { /// The name or id of UserGroup that user should be removed from. pub user_group: String, @@ -87,10 +92,11 @@ pub struct RemoveUserFromUserGroup { /// Response: [UserGroup] #[typeshare] #[derive( - Serialize, Deserialize, Debug, Clone, Request, EmptyTraits, + Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UserGroup)] +#[error(serror::Error)] pub struct SetUsersInUserGroup { /// Id or name. pub user_group: String, diff --git a/client/core/rs/src/api/write/variable.rs b/client/core/rs/src/api/write/variable.rs index 2f55f637e..ebd45e4ec 100644 --- a/client/core/rs/src/api/write/variable.rs +++ b/client/core/rs/src/api/write/variable.rs @@ -1,5 +1,5 @@ use derive_empty_traits::EmptyTraits; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use typeshare::typeshare; @@ -10,10 +10,11 @@ use super::KomodoWriteRequest; /// **Admin only.** Create variable. Response: [Variable]. #[typeshare] #[derive( - Debug, Clone, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(CreateVariableResponse)] +#[error(serror::Error)] pub struct CreateVariable { /// The name of the variable to create. pub name: String, @@ -36,10 +37,11 @@ pub type CreateVariableResponse = Variable; /// **Admin only.** Update variable value. Response: [Variable]. #[typeshare] #[derive( - Debug, Clone, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateVariableValueResponse)] +#[error(serror::Error)] pub struct UpdateVariableValue { /// The name of the variable to update. pub name: String, @@ -55,10 +57,11 @@ pub type UpdateVariableValueResponse = Variable; /// **Admin only.** Update variable description. Response: [Variable]. #[typeshare] #[derive( - Debug, Clone, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateVariableDescriptionResponse)] +#[error(serror::Error)] pub struct UpdateVariableDescription { /// The name of the variable to update. pub name: String, @@ -74,10 +77,11 @@ pub type UpdateVariableDescriptionResponse = Variable; /// **Admin only.** Update whether variable is secret. Response: [Variable]. #[typeshare] #[derive( - Debug, Clone, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(UpdateVariableIsSecretResponse)] +#[error(serror::Error)] pub struct UpdateVariableIsSecret { /// The name of the variable to update. pub name: String, @@ -93,10 +97,11 @@ pub type UpdateVariableIsSecretResponse = Variable; /// **Admin only.** Delete a variable. Response: [Variable]. #[typeshare] #[derive( - Debug, Clone, Serialize, Deserialize, Request, EmptyTraits, + Debug, Clone, Serialize, Deserialize, Resolve, EmptyTraits, )] #[empty_traits(KomodoWriteRequest)] #[response(DeleteVariableResponse)] +#[error(serror::Error)] pub struct DeleteVariable { pub name: String, } diff --git a/client/core/rs/src/request.rs b/client/core/rs/src/request.rs index ed7d497d1..5cf752255 100644 --- a/client/core/rs/src/request.rs +++ b/client/core/rs/src/request.rs @@ -15,10 +15,14 @@ use crate::{ impl KomodoClient { #[cfg(not(feature = "blocking"))] - pub async fn auth( + pub async fn auth( &self, request: T, - ) -> anyhow::Result { + ) -> anyhow::Result + where + T: Serialize + KomodoAuthRequest, + T::Response: DeserializeOwned, + { self .post( "/auth", @@ -31,10 +35,11 @@ impl KomodoClient { } #[cfg(feature = "blocking")] - pub fn auth( - &self, - request: T, - ) -> anyhow::Result { + pub fn auth(&self, request: T) -> anyhow::Result + where + T: Serialize + KomodoAuthRequest, + T::Response: DeserializeOwned, + { self.post( "/auth", json!({ @@ -45,10 +50,14 @@ impl KomodoClient { } #[cfg(not(feature = "blocking"))] - pub async fn user( + pub async fn user( &self, request: T, - ) -> anyhow::Result { + ) -> anyhow::Result + where + T: Serialize + KomodoUserRequest, + T::Response: DeserializeOwned, + { self .post( "/auth", @@ -61,10 +70,11 @@ impl KomodoClient { } #[cfg(feature = "blocking")] - pub fn user( - &self, - request: T, - ) -> anyhow::Result { + pub fn user(&self, request: T) -> anyhow::Result + where + T: Serialize + KomodoUserRequest, + T::Response: DeserializeOwned, + { self.post( "/auth", json!({ @@ -75,10 +85,14 @@ impl KomodoClient { } #[cfg(not(feature = "blocking"))] - pub async fn read( + pub async fn read( &self, request: T, - ) -> anyhow::Result { + ) -> anyhow::Result + where + T: Serialize + KomodoReadRequest, + T::Response: DeserializeOwned, + { self .post( "/read", @@ -91,10 +105,11 @@ impl KomodoClient { } #[cfg(feature = "blocking")] - pub fn read( - &self, - request: T, - ) -> anyhow::Result { + pub fn read(&self, request: T) -> anyhow::Result + where + T: Serialize + KomodoReadRequest, + T::Response: DeserializeOwned, + { self.post( "/read", json!({ @@ -105,10 +120,14 @@ impl KomodoClient { } #[cfg(not(feature = "blocking"))] - pub async fn write( + pub async fn write( &self, request: T, - ) -> anyhow::Result { + ) -> anyhow::Result + where + T: Serialize + KomodoWriteRequest, + T::Response: DeserializeOwned, + { self .post( "/write", @@ -121,10 +140,11 @@ impl KomodoClient { } #[cfg(feature = "blocking")] - pub fn write( - &self, - request: T, - ) -> anyhow::Result { + pub fn write(&self, request: T) -> anyhow::Result + where + T: Serialize + KomodoWriteRequest, + T::Response: DeserializeOwned, + { self.post( "/write", json!({ @@ -135,10 +155,14 @@ impl KomodoClient { } #[cfg(not(feature = "blocking"))] - pub async fn execute( + pub async fn execute( &self, request: T, - ) -> anyhow::Result { + ) -> anyhow::Result + where + T: Serialize + KomodoExecuteRequest, + T::Response: DeserializeOwned, + { self .post( "/execute", @@ -151,10 +175,11 @@ impl KomodoClient { } #[cfg(feature = "blocking")] - pub fn execute( - &self, - request: T, - ) -> anyhow::Result { + pub fn execute(&self, request: T) -> anyhow::Result + where + T: Serialize + KomodoExecuteRequest, + T::Response: DeserializeOwned, + { self.post( "/execute", json!({ diff --git a/client/core/ts/package.json b/client/core/ts/package.json index 4626d1f35..ed83c59a6 100644 --- a/client/core/ts/package.json +++ b/client/core/ts/package.json @@ -1,6 +1,6 @@ { "name": "komodo_client", - "version": "1.16.12", + "version": "1.16.13", "description": "Komodo client package", "homepage": "https://komo.do", "main": "dist/lib.js", diff --git a/client/core/ts/src/responses.ts b/client/core/ts/src/responses.ts index d1a1092e5..558408cdf 100644 --- a/client/core/ts/src/responses.ts +++ b/client/core/ts/src/responses.ts @@ -36,9 +36,6 @@ export type ReadResponses = { GetUserGroup: Types.GetUserGroupResponse; ListUserGroups: Types.ListUserGroupsResponse; - // ==== SEARCH ==== - FindResources: Types.FindResourcesResponse; - // ==== PROCEDURE ==== GetProceduresSummary: Types.GetProceduresSummaryResponse; GetProcedure: Types.GetProcedureResponse; diff --git a/client/core/ts/src/types.ts b/client/core/ts/src/types.ts index 724bc5fe8..fca539c59 100644 --- a/client/core/ts/src/types.ts +++ b/client/core/ts/src/types.ts @@ -4747,28 +4747,6 @@ export interface ExportResourcesToToml { include_variables?: boolean; } -/** Find resources matching a common query. Response: [FindResourcesResponse]. */ -export interface FindResources { - /** The mongo query as JSON */ - query?: MongoDocument; - /** The resource variants to include in the response. */ - resources?: ResourceTarget["type"][]; -} - -/** Response for [FindResources]. */ -export interface FindResourcesResponse { - /** The matching servers. */ - servers: ServerListItem[]; - /** The matching deployments. */ - deployments: DeploymentListItem[]; - /** The matching builds. */ - builds: BuildListItem[]; - /** The matching repos. */ - repos: RepoListItem[]; - /** The matching procedures. */ - procedures: ProcedureListItem[]; -} - /** * **Admin only.** * Find a user. @@ -7459,7 +7437,6 @@ export type ReadRequest = | { type: "ListUserTargetPermissions", params: ListUserTargetPermissions } | { type: "GetUserGroup", params: GetUserGroup } | { type: "ListUserGroups", params: ListUserGroups } - | { type: "FindResources", params: FindResources } | { type: "GetProceduresSummary", params: GetProceduresSummary } | { type: "GetProcedure", params: GetProcedure } | { type: "GetProcedureActionState", params: GetProcedureActionState } diff --git a/client/periphery/rs/src/api/build.rs b/client/periphery/rs/src/api/build.rs index 219b4370d..2e45f2df3 100644 --- a/client/periphery/rs/src/api/build.rs +++ b/client/periphery/rs/src/api/build.rs @@ -1,9 +1,10 @@ use komodo_client::entities::update::Log; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(BuildResponse)] +#[error(serror::Error)] pub struct Build { pub build: komodo_client::entities::build::Build, /// Override registry token with one sent from core. @@ -20,12 +21,14 @@ pub type BuildResponse = Vec; // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PruneBuilders {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PruneBuildx {} diff --git a/client/periphery/rs/src/api/compose.rs b/client/periphery/rs/src/api/compose.rs index 770e18043..0758a404b 100644 --- a/client/periphery/rs/src/api/compose.rs +++ b/client/periphery/rs/src/api/compose.rs @@ -3,7 +3,7 @@ use komodo_client::entities::{ update::Log, FileContents, SearchCombinator, }; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use super::git::RepoActionResponse; @@ -13,16 +13,18 @@ use super::git::RepoActionResponse; /// /// Incoming from docker like: /// [{"Name":"project_name","Status":"running(1)","ConfigFiles":"/root/compose/compose.yaml,/root/compose/compose2.yaml"}] -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct ListComposeProjects {} // /// Get the compose contents on the host, for stacks using /// `files_on_host`. -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(GetComposeContentsOnHostResponse)] +#[error(serror::Error)] pub struct GetComposeContentsOnHost { /// The name of the stack pub name: String, @@ -39,8 +41,9 @@ pub struct GetComposeContentsOnHostResponse { // /// The stack folder must already exist for this to work -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct GetComposeServiceLog { /// The name of the project pub project: String, @@ -61,8 +64,9 @@ fn default_tail() -> u64 { // /// The stack folder must already exist for this to work -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct GetComposeServiceLogSearch { /// The name of the project pub project: String, @@ -86,8 +90,9 @@ pub struct GetComposeServiceLogSearch { /// Write the compose contents to the file on the host, for stacks using /// `files_on_host`. -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct WriteComposeContentsToHost { /// The name of the stack pub name: String, @@ -104,8 +109,9 @@ pub struct WriteComposeContentsToHost { /// Write and commit compose contents. /// Only works with git repo based stacks. -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(RepoActionResponse)] +#[error(serror::Error)] pub struct WriteCommitComposeContents { /// The stack to write to. pub stack: Stack, @@ -123,8 +129,9 @@ pub struct WriteCommitComposeContents { /// Rewrites the compose directory, pulls any images, takes down existing containers, /// and runs docker compose up. Response: [ComposePullResponse] -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(ComposePullResponse)] +#[error(serror::Error)] pub struct ComposePull { /// The stack to deploy pub stack: Stack, @@ -145,8 +152,9 @@ pub struct ComposePullResponse { // /// docker compose up. -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(ComposeUpResponse)] +#[error(serror::Error)] pub struct ComposeUp { /// The stack to deploy pub stack: Stack, @@ -187,8 +195,9 @@ pub struct ComposeUpResponse { // /// General compose command runner -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct ComposeExecution { /// The compose project name to run the execution on. /// Usually its he name of the stack / folder under the `stack_dir`. diff --git a/client/periphery/rs/src/api/container.rs b/client/periphery/rs/src/api/container.rs index cead5e7f8..658e3ccfe 100644 --- a/client/periphery/rs/src/api/container.rs +++ b/client/periphery/rs/src/api/container.rs @@ -4,21 +4,23 @@ use komodo_client::entities::{ update::Log, SearchCombinator, TerminationSignal, }; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Container)] +#[error(serror::Error)] pub struct InspectContainer { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct GetContainerLog { pub name: String, #[serde(default = "default_tail")] @@ -34,8 +36,9 @@ fn default_tail() -> u64 { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct GetContainerLogSearch { pub name: String, pub terms: Vec, @@ -50,16 +53,18 @@ pub struct GetContainerLogSearch { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(ContainerStats)] +#[error(serror::Error)] pub struct GetContainerStats { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct GetContainerStatsList {} // @@ -68,8 +73,9 @@ pub struct GetContainerStatsList {} // ACTIONS // ======= -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct Deploy { pub deployment: Deployment, pub stop_signal: Option, @@ -83,40 +89,45 @@ pub struct Deploy { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct StartContainer { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct RestartContainer { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PauseContainer { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct UnpauseContainer { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct StopContainer { pub name: String, pub signal: Option, @@ -125,8 +136,9 @@ pub struct StopContainer { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct RemoveContainer { pub name: String, pub signal: Option, @@ -135,8 +147,9 @@ pub struct RemoveContainer { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct RenameContainer { pub curr_name: String, pub new_name: String, @@ -144,36 +157,42 @@ pub struct RenameContainer { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PruneContainers {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct StartAllContainers {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct RestartAllContainers {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct PauseAllContainers {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct UnpauseAllContainers {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct StopAllContainers {} diff --git a/client/periphery/rs/src/api/git.rs b/client/periphery/rs/src/api/git.rs index 3302da1c1..6eb29f19e 100644 --- a/client/periphery/rs/src/api/git.rs +++ b/client/periphery/rs/src/api/git.rs @@ -3,17 +3,19 @@ use std::path::PathBuf; use komodo_client::entities::{ update::Log, CloneArgs, EnvironmentVar, LatestCommit, }; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(LatestCommit)] +#[error(serror::Error)] pub struct GetLatestCommit { pub name: String, } -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(RepoActionResponse)] +#[error(serror::Error)] pub struct CloneRepo { pub args: CloneArgs, #[serde(default)] @@ -35,8 +37,9 @@ fn default_env_file_path() -> String { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(RepoActionResponse)] +#[error(serror::Error)] pub struct PullRepo { pub args: CloneArgs, #[serde(default)] @@ -55,8 +58,9 @@ pub struct PullRepo { // /// Either pull or clone depending on whether it exists. -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(RepoActionResponse)] +#[error(serror::Error)] pub struct PullOrCloneRepo { pub args: CloneArgs, #[serde(default)] @@ -86,8 +90,9 @@ pub struct RepoActionResponse { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct RenameRepo { pub curr_name: String, pub new_name: String, @@ -95,8 +100,9 @@ pub struct RenameRepo { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct DeleteRepo { pub name: String, } diff --git a/client/periphery/rs/src/api/image.rs b/client/periphery/rs/src/api/image.rs index 08c1b6da7..f5a1c2123 100644 --- a/client/periphery/rs/src/api/image.rs +++ b/client/periphery/rs/src/api/image.rs @@ -2,29 +2,32 @@ use komodo_client::entities::{ docker::image::{Image, ImageHistoryResponseItem}, update::Log, }; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; // -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Image)] +#[error(serror::Error)] pub struct InspectImage { pub name: String, } // -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct ImageHistory { pub name: String, } // -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PullImage { /// The name of the image. pub name: String, @@ -36,8 +39,9 @@ pub struct PullImage { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct DeleteImage { /// Id or name pub name: String, @@ -45,6 +49,7 @@ pub struct DeleteImage { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PruneImages {} diff --git a/client/periphery/rs/src/api/mod.rs b/client/periphery/rs/src/api/mod.rs index dade628f6..ed9616ab3 100644 --- a/client/periphery/rs/src/api/mod.rs +++ b/client/periphery/rs/src/api/mod.rs @@ -8,7 +8,7 @@ use komodo_client::entities::{ update::Log, SystemCommand, }; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; use serror::Serror; @@ -23,8 +23,9 @@ pub mod volume; // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(GetHealthResponse)] +#[error(serror::Error)] pub struct GetHealth {} #[derive(Serialize, Deserialize, Debug, Clone)] @@ -32,8 +33,9 @@ pub struct GetHealthResponse {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(GetVersionResponse)] +#[error(serror::Error)] pub struct GetVersion {} #[derive(Serialize, Deserialize, Debug, Clone)] @@ -42,8 +44,9 @@ pub struct GetVersionResponse { } /// Returns all containers, networks, images, compose projects -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(GetDockerListsResponse)] +#[error(serror::Error)] pub struct GetDockerLists {} #[derive(Serialize, Deserialize, Debug, Clone)] @@ -57,36 +60,41 @@ pub struct GetDockerListsResponse { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(ListGitProvidersResponse)] +#[error(serror::Error)] pub struct ListGitProviders {} pub type ListGitProvidersResponse = Vec; // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(ListDockerRegistriesResponse)] +#[error(serror::Error)] pub struct ListDockerRegistries {} pub type ListDockerRegistriesResponse = Vec; // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] +#[error(serror::Error)] pub struct ListSecrets {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PruneSystem {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct RunCommand { pub command: SystemCommand, } diff --git a/client/periphery/rs/src/api/network.rs b/client/periphery/rs/src/api/network.rs index 67b76547e..21e2de3b4 100644 --- a/client/periphery/rs/src/api/network.rs +++ b/client/periphery/rs/src/api/network.rs @@ -1,21 +1,23 @@ use komodo_client::entities::{ docker::network::Network, update::Log, }; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Network)] +#[error(serror::Error)] pub struct InspectNetwork { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct CreateNetwork { pub name: String, pub driver: Option, @@ -23,8 +25,9 @@ pub struct CreateNetwork { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct DeleteNetwork { /// Id or name pub name: String, @@ -32,6 +35,7 @@ pub struct DeleteNetwork { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PruneNetworks {} diff --git a/client/periphery/rs/src/api/stats.rs b/client/periphery/rs/src/api/stats.rs index 06fa5a4fe..fcaf072f9 100644 --- a/client/periphery/rs/src/api/stats.rs +++ b/client/periphery/rs/src/api/stats.rs @@ -1,23 +1,28 @@ use komodo_client::entities::stats::{ SystemInformation, SystemProcess, SystemStats, }; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(SystemInformation)] +#[error(serror::Error)] pub struct GetSystemInformation {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(SystemStats)] +#[error(serror::Error)] pub struct GetSystemStats {} // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Vec)] -pub struct GetSystemProcesses {} \ No newline at end of file +#[error(serror::Error)] +pub struct GetSystemProcesses {} + +// diff --git a/client/periphery/rs/src/api/volume.rs b/client/periphery/rs/src/api/volume.rs index 39ac64084..462b1806d 100644 --- a/client/periphery/rs/src/api/volume.rs +++ b/client/periphery/rs/src/api/volume.rs @@ -1,19 +1,21 @@ use komodo_client::entities::{docker::volume::Volume, update::Log}; -use resolver_api::derive::Request; +use resolver_api::Resolve; use serde::{Deserialize, Serialize}; // -#[derive(Debug, Clone, Serialize, Deserialize, Request)] +#[derive(Debug, Clone, Serialize, Deserialize, Resolve)] #[response(Volume)] +#[error(serror::Error)] pub struct InspectVolume { pub name: String, } // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct DeleteVolume { /// Id or name pub name: String, @@ -21,6 +23,7 @@ pub struct DeleteVolume { // -#[derive(Serialize, Deserialize, Debug, Clone, Request)] +#[derive(Serialize, Deserialize, Debug, Clone, Resolve)] #[response(Log)] +#[error(serror::Error)] pub struct PruneVolumes {} diff --git a/client/periphery/rs/src/lib.rs b/client/periphery/rs/src/lib.rs index 8480491f3..8fc127771 100644 --- a/client/periphery/rs/src/lib.rs +++ b/client/periphery/rs/src/lib.rs @@ -3,8 +3,8 @@ use std::{sync::OnceLock, time::Duration}; use anyhow::Context; use reqwest::StatusCode; use resolver_api::HasResponse; +use serde::{de::DeserializeOwned, Serialize}; use serde_json::json; -use serror::deserialize_error; pub mod api; @@ -45,10 +45,14 @@ impl PeripheryClient { level = "debug", skip(self) )] - pub async fn request( + pub async fn request( &self, request: T, - ) -> anyhow::Result { + ) -> anyhow::Result + where + T: std::fmt::Debug + Serialize + HasResponse, + T::Response: DeserializeOwned, + { tracing::debug!("running health check"); self.health_check().await?; tracing::debug!("health check passed. running inner request"); @@ -64,11 +68,15 @@ impl PeripheryClient { } #[tracing::instrument(level = "debug", skip(self))] - async fn request_inner( + async fn request_inner( &self, request: T, timeout: Option, - ) -> anyhow::Result { + ) -> anyhow::Result + where + T: std::fmt::Debug + Serialize + HasResponse, + T::Response: DeserializeOwned, + { let req_type = T::req_type(); tracing::trace!( "sending request | type: {req_type} | body: {request:?}" @@ -104,8 +112,7 @@ impl PeripheryClient { tracing::debug!("got response text, deserializing error"); - let error = deserialize_error(text) - .context(format!("request to periphery failed | {status}")); + let error = serror::deserialize_error(text).context(status); Err(error) } diff --git a/frontend/public/client/responses.d.ts b/frontend/public/client/responses.d.ts index da8f01725..fcece45ea 100644 --- a/frontend/public/client/responses.d.ts +++ b/frontend/public/client/responses.d.ts @@ -28,7 +28,6 @@ export type ReadResponses = { ListUserTargetPermissions: Types.ListUserTargetPermissionsResponse; GetUserGroup: Types.GetUserGroupResponse; ListUserGroups: Types.ListUserGroupsResponse; - FindResources: Types.FindResourcesResponse; GetProceduresSummary: Types.GetProceduresSummaryResponse; GetProcedure: Types.GetProcedureResponse; GetProcedureActionState: Types.GetProcedureActionStateResponse; diff --git a/frontend/public/client/types.d.ts b/frontend/public/client/types.d.ts index 191dba903..7dbf49d53 100644 --- a/frontend/public/client/types.d.ts +++ b/frontend/public/client/types.d.ts @@ -4529,26 +4529,6 @@ export interface ExportResourcesToToml { /** Whether to include variables */ include_variables?: boolean; } -/** Find resources matching a common query. Response: [FindResourcesResponse]. */ -export interface FindResources { - /** The mongo query as JSON */ - query?: MongoDocument; - /** The resource variants to include in the response. */ - resources?: ResourceTarget["type"][]; -} -/** Response for [FindResources]. */ -export interface FindResourcesResponse { - /** The matching servers. */ - servers: ServerListItem[]; - /** The matching deployments. */ - deployments: DeploymentListItem[]; - /** The matching builds. */ - builds: BuildListItem[]; - /** The matching repos. */ - repos: RepoListItem[]; - /** The matching procedures. */ - procedures: ProcedureListItem[]; -} /** * **Admin only.** * Find a user. @@ -7146,9 +7126,6 @@ export type ReadRequest = { } | { type: "ListUserGroups"; params: ListUserGroups; -} | { - type: "FindResources"; - params: FindResources; } | { type: "GetProceduresSummary"; params: GetProceduresSummary; diff --git a/lib/cache/README.md b/lib/cache/README.md new file mode 100644 index 000000000..653591d92 --- /dev/null +++ b/lib/cache/README.md @@ -0,0 +1,5 @@ +# Cache module + +Contains a thread-safe async timeout cache implementation. +Used to cache outputs in memory for a limited time period. +Can be used to avoid re-running the underlying process which generated an output in too short a timeframe. \ No newline at end of file diff --git a/lib/command/README.md b/lib/command/README.md new file mode 100644 index 000000000..e69b14595 --- /dev/null +++ b/lib/command/README.md @@ -0,0 +1,3 @@ +# Command module + +Helpers to run shell commands as child processes, and collect the outputs. \ No newline at end of file diff --git a/lib/environment_file/README.md b/lib/environment_file/README.md new file mode 100644 index 000000000..dd01f956d --- /dev/null +++ b/lib/environment_file/README.md @@ -0,0 +1,8 @@ +# Environment file module + +Helpers for parsing variables from file contents. + +Used to parse secrets from the files specified in env variable ending in `_FILE`. + +Compatible with docker compose secrets, +see [https://docs.docker.com/compose/how-tos/use-secrets/](https://docs.docker.com/compose/how-tos/use-secrets/). \ No newline at end of file diff --git a/lib/formatting/README.md b/lib/formatting/README.md new file mode 100644 index 000000000..81804cb9a --- /dev/null +++ b/lib/formatting/README.md @@ -0,0 +1,3 @@ +# Formatting module + +Used to pretty-format logs using HTML for better display from the UI. \ No newline at end of file diff --git a/lib/git/README.md b/lib/git/README.md new file mode 100644 index 000000000..eaca0afaf --- /dev/null +++ b/lib/git/README.md @@ -0,0 +1,3 @@ +# Git module + +Helpers for cloning, pulling, and committing to git repos. \ No newline at end of file diff --git a/lib/logger/README.md b/lib/logger/README.md new file mode 100644 index 000000000..fadc2a455 --- /dev/null +++ b/lib/logger/README.md @@ -0,0 +1,3 @@ +# Logger module + +Helpers to configure standardized application logging / opentelemetry output. \ No newline at end of file diff --git a/lib/logger/src/otel.rs b/lib/logger/src/otel.rs index 91a4eff23..a9b756cc3 100644 --- a/lib/logger/src/otel.rs +++ b/lib/logger/src/otel.rs @@ -23,11 +23,8 @@ fn resource(service_name: String) -> Resource { pub fn tracer(endpoint: &str, service_name: String) -> Tracer { let provider = opentelemetry_sdk::trace::TracerProvider::builder() - .with_config( - opentelemetry_sdk::trace::Config::default() - .with_sampler(Sampler::AlwaysOn) - .with_resource(resource(service_name.clone())), - ) + .with_resource(resource(service_name.clone())) + .with_sampler(Sampler::AlwaysOn) .with_batch_exporter( opentelemetry_otlp::SpanExporter::builder() .with_tonic() diff --git a/lib/response/Cargo.toml b/lib/response/Cargo.toml new file mode 100644 index 000000000..da71475d8 --- /dev/null +++ b/lib/response/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "response" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true + +[dependencies] +serde_json.workspace = true +serror.workspace = true +anyhow.workspace = true +serde.workspace = true +axum.workspace = true \ No newline at end of file diff --git a/lib/response/src/lib.rs b/lib/response/src/lib.rs new file mode 100644 index 000000000..a2fb05fd7 --- /dev/null +++ b/lib/response/src/lib.rs @@ -0,0 +1,76 @@ +use anyhow::Context; +use axum::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; +use serde::Serialize; +use serror::serialize_error; + +pub struct Response(pub axum::response::Response); + +impl From for Response +where + T: Serialize, +{ + fn from(value: T) -> Response { + let res = match serde_json::to_string(&value) + .context("failed to serialize response body") + { + Ok(body) => axum::response::Response::builder() + .header( + CONTENT_TYPE, + HeaderValue::from_static("application/json"), + ) + .body(axum::body::Body::from(body)) + .unwrap(), + Err(e) => axum::response::Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .header( + CONTENT_TYPE, + HeaderValue::from_static("application/json"), + ) + .body(axum::body::Body::from(serialize_error(&e))) + .unwrap(), + }; + Response(res) + } +} + +pub enum JsonString { + Ok(String), + Err(serde_json::Error), +} + +impl From for JsonString +where + T: Serialize, +{ + fn from(value: T) -> JsonString { + match serde_json::to_string(&value) { + Ok(body) => JsonString::Ok(body), + Err(e) => JsonString::Err(e), + } + } +} + +impl JsonString { + pub fn into_response(self) -> axum::response::Response { + match self { + JsonString::Ok(body) => axum::response::Response::builder() + .header( + CONTENT_TYPE, + HeaderValue::from_static("application/json"), + ) + .body(axum::body::Body::from(body)) + .unwrap(), + JsonString::Err(error) => axum::response::Response::builder() + .status(StatusCode::INTERNAL_SERVER_ERROR) + .header( + CONTENT_TYPE, + HeaderValue::from_static("application/json"), + ) + .body(axum::body::Body::from(serialize_error( + &anyhow::Error::from(error) + .context("failed to serialize response body"), + ))) + .unwrap(), + } + } +} diff --git a/runfile.toml b/runfile.toml index b511b49a2..2c90caf72 100644 --- a/runfile.toml +++ b/runfile.toml @@ -46,12 +46,12 @@ cmd = """ docker compose -p komodo-dev -f test.compose.yaml build""" [test-core] -description = "runs core --release pointing to .komodo/core.config.toml" -cmd = "KOMODO_CONFIG_PATH=.komodo/core.config.toml cargo run -p komodo_core --release" +description = "runs core --release pointing to .dev/core.config.toml" +cmd = "KOMODO_CONFIG_PATH=.dev/core.config.toml cargo run -p komodo_core --release" [test-periphery] -description = "runs periphery --release pointing to test.periphery.config.toml" -cmd = "PERIPHERY_CONFIG_PATH=test.periphery.config.toml cargo run -p komodo_periphery --release" +description = "runs periphery --release pointing to .dev/periphery.config.toml" +cmd = "PERIPHERY_CONFIG_PATH=.dev/periphery.config.toml cargo run -p komodo_periphery --release" [create-multiarch-builder] cmd = "docker buildx create --name builder --use --bootstrap" diff --git a/test.core.config.toml b/test.core.config.toml deleted file mode 100644 index c7271bcef..000000000 --- a/test.core.config.toml +++ /dev/null @@ -1,43 +0,0 @@ -############################ -# 🦎 KOMODO CORE CONFIG 🦎 # -############################ -title = "Komodo Test" -host = "http://localhost:9121" -port = 9121 -passkey = "a_random_passkey" -sync_directory = ".komodo/syncs" -repo_directory = ".komodo/core-repos" -action_directory = ".komodo/actions" -frontend_path = "frontend/dist" -first_server = "http://localhost:8121" - -disable_confirm_dialog = true -enable_new_users = true - -# database.address = "localhost:27017" -database.address = "test-ferretdb.orb.local" -# database.username = "admin" -# database.password = "admin" -# database.uri = "mongodb://admin:admin@localhost:27017/komodo" # For ferretdb-postgres - -local_auth = true -jwt_secret = "your_random_secret" -jwt_ttl = "2-wk" - -oidc_enabled = false -# oidc_provider = "http://server.authentik2.orb.local:9000/application/o/komodo" -# oidc_client_id = "komodo" -# oidc_client_secret = "komodo" - -webhook_secret = "a_random_webhook_secret" - -monitoring_interval = "5-sec" -resource_poll_interval = "1-min" - -########### -# LOGGING # -########### -logging.level = "info" -logging.stdio = "standard" -# logging.otlp_endpoint = "http://grafana.orb.local:4317" -logging.opentelemetry_service_name = "Komodo" diff --git a/test.periphery.config.toml b/test.periphery.config.toml deleted file mode 100644 index c31a74340..000000000 --- a/test.periphery.config.toml +++ /dev/null @@ -1,24 +0,0 @@ -################################# -# 🦎 KOMODO PERIPHERY CONFIG 🦎 # -################################# -port = 8121 -repo_dir = ".komodo/repos" -stack_dir = ".komodo/stacks" -stats_polling_rate = "1-sec" -legacy_compose_cli = false -include_disk_mounts = [] - -############ -# Security # -############ -ssl_enabled = true -ssl_key_file = ".komodo/ssl/key.pem" -ssl_cert_file = ".komodo/ssl/cert.pem" - -########### -# LOGGING # -########### -logging.level = "info" -logging.stdio = "standard" -# logging.otlp_endpoint = "http://grafana.orb.local:4317" -logging.opentelemetry_service_name = "Periphery" \ No newline at end of file