Compare commits

..
Author SHA1 Message Date
08e55b5180 2.0.0 (#889)
* modularize openapi structs

* most of execute openapi

* server / stack exec openapi

* most of write openapi

* more write openapi

* add the write openapi definitions

* fmt and bump mogh auth

* gen types

* remove user api, mogh auth handles api keys

* clean up

* cache latest image digests and use this for image update alerting / auto update

* deploy 2.0.0-dev-108

* deploy 2.0.0-dev-109

* add back legacy Action "exec" method calling convention

* typegen

* improve config quick links styling

* improve config styling a bit more

* deploy 2.0.0-dev-110

* finish version upgrades page, and use prism theme oneLight and oneDark

* stack level terminal page

* fix Action import from terminal

* clean up dangling action api keys on startup

* improve swarm service / stack state inference

* deploy 2.0.0-dev-111

* add api docs link

* bump ts types and fix SwarmConfig type name conflict

* use mogh auth for passkey conversion

* align dashboard icon with sidebar

* bump frontend node version

* bump node version in all the dockerfiles

* fix tailwind config module

* fix require to import

* skip auto update for images with pinned digest

* dev 112

* cleanup

* batch check for updates, add check to table multi select actions

* dev-113

* mogh_server = "1.2.0"

* serve static ui index.html with ETag / no-cache

* check update available against all digests for image

* deploy 2.0.0-dev-114

* bump rust to 1.93.0

* bump mogh server and auth

* configure session allow cross site

* deploy 2.0.0-dev-115

* add new config value to example config

* Stack check for update prefers check against deployed service image

* deploy 2.0.0-dev-116

* only send auto updated alert after verifying the deploy was successful

* 2.0.0 UI (#1220)

* new ui using mantine

* resources page

* prog on resource page

* resources and resource layouts

* confirm button and modal

* tweaks

* update details

* topbar updates

* add skeletons for resource implementations

* add resource tables

* add tags to recents cards

* resource page table scrolling

* table component + tags filter

* export toml

* New Resource button

* Fix update details capture closing

* tweaks

* omni search

* refine config

* config tweaks

* implement more configs / resource selector

* add profile page

* provider / account selectors

* container table page

* build config

* deployment config

* fix deployment build version selector

* fix secrets selector

* resource sync config

* mobile topbar and updates

* update details fz sm

* stack config

* terminals page

* create terminal in prog

* create terminal menu

* finish create terminal menu

* terminal pages working

* stack tabs / info

* add executions

* add server header info

* confirm pubkey modal

* improve resource header styling

* FileSource component

* stack service table, move icons.ts

* basic procedure config

* tweak procedure config

* container / image pages

* network / volume pages

* clean up docker resource pages

*  basic log / terminal ui

* reusable log section

* styling

* clean up resource components

* delete in resource header

* log auto select stderr

* fix some bgs

* stack logs with service selector

* stack terminals

* add deployment executions

* use correct icon

* useResource hooks

* build info

* build info

* tweaks

* server tabs

* fix terminal section target

* prog on server tabs

* server stats

* light theme

* start on historical stats

* stack service page

* resource sync tabs

* sync tabs

* more topbar icons

* add settings basic

* add topbar alerts

* tweak stream selector behavior

* tweak alert icon topbar

* improve styling smaller screen

* schedules page and other progress

* onboarding keys

* improve schedule page descriptions

* improve update notifications

* schedule timezone selector

* tag color selector

* finish settings / providers

* use shared-text-update component so settings tables aren't janky

* updates page

* refine updates page

* alert page

* standardize borders

* theme and swarm

* swarm tabs

* swarm node page

* swarm config page

* swarm pages

* swarm task and secret pages

* swarm stack page

* fix stack log service selector in swarm mode

* standard inspect section

* swarm inspect tab

* server and swarm resources tab

* add disable confirm dialog (modal) option for executions

* stack update available indicator

* deployment update available

* add template switch to resource headers

* ResourceHeader + rename

* set editing name onclick

* repo tabs

* server stats table

* refine a bit

* refine deployment / stack header info

* show server stats dashboard. dashboard tables

* action last run in config

* SettingsUsers page

* user page etc

* manage api key

* user base permissions

* color the table multi select

* user group page

* UserAddUserGroup

* active includes deployments / stacks

* improve small screen view

* fix docker pages execution showing

* clean up

* rename frontend to UI

* align profile page styling

* config maintenance windows

* finish maintenance windows

* builder config

* add batch execute dropdown / confirm menu

* batch execute styling

* deploy 2.0.0-dev-117

* improve stats card light theme

* add update page

* improve mobile

* terminal group nowrap

* mobile improvements

* allow unused again

* improve mobile font sizing

* improve mobile updates / alerts

* mobile tabs

* alert page

* add server version mismatch color

* new resource, clearable selector

* Fix build show info tab

* copy resources

* keyboard shortcuts

* server resource header version mismatch

* fix type errors

* container page server multi select

* confirm button clear timeout

* hash compare force uses first 8 for short hash

* fix log height

* copy webhooks

* responsive tweaks

* add icons to server stat sections

* add historical server stats charts

* server stat current card shows usage numbers

* refine current stats more

* fix shortcuts interfering with monaco brave

* clean up unused

* remove v1 frontend

* bump rust version to 1.93.1 and dep versions

* deploy 2.0.0-dev-118

* bump chef rust version

* improve login no auth configured and passkey pending

* Load Average is first historical stat

* procedure / action webhook branch mobile style

* dashboard active styling

* hide actions when none

* Select Template

* execution buttons disabled when loading

* Fix config input issue

* improve tab styling

* rename ConfigSwitch onChange -> onCheckedChange

* ensure section headers consistent spacing

* edit swarm join command

* fix batch executions width

* stack stopped and deployment exited warning instead of critical

* smaller more consistent gaps

* add close button to update / alert details drawer

* stack and deployment state color include update available. Ensure server version mismatch color applied everywhere

* deploy 2.0.0-dev-120

* topbar user dropdown shows user avatar if available

* post link redirect should be to profile

* deploy 2.0.0-dev-121

* improve profile delete styling

* standard api key modal size

* improve login styling

* fix login github / google icon color

* fix some wrapping stuff in tables and tag text disappear

* fix terminal height - same as logs

* single delete terminal

* Update / Alert table filter selector formatted

* tweaks for lg size

* taller data table and blue omnisearch

* refine lg screen size view

* fix sidebar margin right

* "never" -> "Never"

* Add hoverable disk info

* improve disk usage hover card styling

* rename for clarity

* thinner topbar

* config sidebar save

* setters use maps instead of mutations

* notification green contents written success

* fmt

* read request are trace

* deploy 2.0.0-dev-122

* debug level core <> periphery auth identifiers logs

* mogh auth server 1.2.10

* mogh auth 1.2.11

* deploy 2.0.0-dev-123

* Deploy / DeployStack updates invalidate corresponding list query

* confirm action / save modals don't need ConfirmButton

* deploy 2.0.0-dev-124

* proper base64url decode

* fix init admin user

* improve mobile friendly tabs width, and onboardng key copy

* rename resource invalidates

* better maxheight for mobile friendly tabs

* km cli needs to install crypto provider for tls ws

* better responsive confirm save width

* confirm modal better responsiveness

* Fix api key modal too thin

* better api key create

* improve batch execs

* improve tabs

* sidebar more compact

* add missing Repo header Info

* Fix Pull repo

* fix repo Links config

* improve procedure config UI including run stack service

* improve deployment network, restart, termination signal config

* fix confirm update showing entries which have not changed

* example execute terminal uses bash

* Update deployments description

* move build server

* [Docs] Update connect-servers.mdx (#1256)

* Update connect-servers.mdx

* Update connect-servers.mdx

* clean up connect servers

* feat(ci): build (#1018)

* fmt

* fix: more verbose logging (#1017)

* use .with_context for stack run directory canonicalize log

* fix failing doc test

* Improve server resource header hover info

* service selector support swarm stack icon

* fix stack terminals sometimes not disabled when it should be

* add terminal create and delete messages

* bump packages and add Mogh Tech copyright

* revamp docsite

* soften the borders

* clean up stack config

* fix config group header too much gap

* procedure stage menu easier to reach

* deploy 2.0.0-dev-125

* increase universal resource polling

* improve server stats

* fix data table

* down node is critical

* cli add print core info

* add docker swarm feature card

* improve toml resource repetition using macros, and fix Swarm toml support

* swarm config: configure alerting options

* add swarm header info

* Implement New Swarm Config and New Swarm Secret

* brighten tag colors

* continue docs revamp

* fmt

* set more refetch intervals to keep display data fresh

* fix copy not showing copy source when there are no templates

* rename onboarding key fix_existing_servers to privileged

* fix Privileged spelling

* fmt

* more docs improvement

* improve docs intro

* update the curl instructions with easier call method

* fix clippy lints

* refresh the server cache after every server connection successful login

* deploy 2.0.0-dev-126

* add polling to dashboard summary data

* tweak tag opacity

* improve server stat table disk hover

* fix change historical stat length collapse stats

* KOMODO_DISABLE_INIT_RESOURCES

* stack Project Missing should be red

* Fix files on host stacks showing down after reboot until refresh cache

* silence user level write logs SetLastSeenUpdate and PushRecentlyViewed

* See the v2 migration guide

* Improve api logging using more fields

* deploy 2.0.0-dev-127

* cli create api key on database, can use with docker exec into core container

* deploy 2.0.0-dev-128

* advanced km create api-key options

* create onboarding key with cli

* tweak

* onboarding keys use tag name

* deploy 2.0.0-dev-129

* stack procedure stages can select specific services to apply to (default all services)

* docsite dockerfile

* ./docusaurus.config.ts

* need to yarn build

* fix broken link

* improve alert (dropdown) icons

* fix docs sidebar collapse

* add local search functionality

* docs search nice

* homepage features navigate to docs

* only show build cancel when canCancel

* more confirm button confirmprops red

* fix docsite buttons and list more features

* fix sidebar cmd click opens in new tab (is a link)

* execute withBorder

* cleaner execute section

* swap docker compose and deploy containers

* make docs logo align with app

* better mobile button layout

* linked logins / 2fa confirm red styling

* bump rust version to 1.94.0

* move bollard::secret to bollard::config

* deploy 2.0.0-dev-130

* feat: add compose_cmd_wrapper_include for selective command wrapping (#1124)

- Add compose_cmd_wrapper_include field to StackConfig.
- Fix wrapper placeholder text not displaying in MonacoEditor.

* align configuration by removing bold label stuff

* confirm update monaco readonly

* dockerfile binaries / ui images default to :2

* fmt

* refresh server cache lint

* fmt

* disabling send alerts should also disable in UI

* clean up disabled alerting

* deploy 2.0.0-dev-131

* mogh_pki 1.1.3 safer copy from slice

* fix dev container issues on 2.0.0 (#1238)

* fix node version and yarn build in dev container

* ensure keys directories exist and are writable in dev container

* update dev container  image (necessary to fix compiler error)

* fix CORS error in dev container

* more dev container fixes

KOMODO_SESSION_ALLOW_CROSS_SITE: true needed to properly run on Firefox

* fix devcontainer port

* update config pages to use "Webhooks" consistently and fix acronym casing (#1239)

Co-authored-by: Maxwell Becker <49575486+mbecker20@users.noreply.github.com>

* update local dev setup instructions (#1240)

* example mongo deploy dev

* check binary URL (#1116)

* install script improve failed download binary log

* align cli installer with periphery installer

* improve terminal page create experience

* deploy 2.0.0-dev-132

* improve mobile updates with full size query

* fix some internal table wrapping

* fix schedule expression examples

* Add swarm updates / alerts filter

* filter by update available uses location hash instead of localstorage

* sync commit preserve meta "deploy" and "after"

* UI fixes and tweaks

* deploy 2.0.0-dev-133

* add error to warn log

* Stack / deployment should inherit specific Swarm permissions

* Fix inline span formatting (used in logs / errors)

* refactor resource sync pending deploy for better display

* deploy 2.0.0-dev-134

* improve terminal error handling

* deploy 2.0.0-dev-135

* dedicated docs page for v2

* warning about failing to include init: true

* Limit Periphery IPs in Advanced config

* refine setup guide

* 2.0.0

---------

Co-authored-by: Shlee <github@shl.ee>
Co-authored-by: Yujia Qiao <code@rapiz.me>
Co-authored-by: ChanningHe <52875777+ChanningHe@users.noreply.github.com>
Co-authored-by: Steven Loria <sloria1@pm.me>
Co-authored-by: Andreas Brett <andreasbrett@users.noreply.github.com>
2026-03-24 05:50:10 -07:00
746 changed files with 44810 additions and 46802 deletions
+9 -3
View File
@@ -1,6 +1,6 @@
services:
dev:
image: mcr.microsoft.com/devcontainers/rust:1-1-bullseye
image: mcr.microsoft.com/devcontainers/rust:1-bookworm
volumes:
# Mount the root folder that contains .git
- ../:/workspace:cached
@@ -10,13 +10,19 @@ services:
- stacks:/etc/komodo/stacks
command: sleep infinity
ports:
- "9121:9121"
- "9120:9120"
environment:
KOMODO_HOST: http://localhost:9120
KOMODO_FIRST_SERVER: http://localhost:8120
KOMODO_DATABASE_ADDRESS: db
KOMODO_ENABLE_NEW_USERS: true
KOMODO_LOCAL_AUTH: true
KOMODO_JWT_SECRET: a_random_secret
VITE_KOMODO_HOST: http://localhost:9120
KOMODO_CORS_ALLOWED_ORIGINS: http://localhost:5173
KOMODO_CORS_ALLOW_CREDENTIALS: true
PERIPHERY_SSL_ENABLED: false
KOMODO_SESSION_ALLOW_CROSS_SITE: true
links:
- db
# ...
@@ -30,4 +36,4 @@ volumes:
data:
repo-cache:
repos:
stacks:
stacks:
+9 -3
View File
@@ -10,7 +10,7 @@
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "20.12.2"
"version": "22.12.0"
},
"ghcr.io/devcontainers-community/features/deno:1": {
@@ -28,7 +28,8 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
9121
5173,
9120
],
// Use 'postCreateCommand' to run commands after the container is created.
@@ -36,11 +37,16 @@
"runServices": [
"db"
]
],
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
"remoteEnv": {
// Avoid out of memory error when running `yarn build`
"NODE_OPTIONS": "--max-old-space-size=4096"
}
}
+7 -1
View File
@@ -1,3 +1,9 @@
#!/bin/sh
cargo install typeshare-cli
cargo install typeshare-cli
sudo mkdir -p /etc/komodo/keys
sudo chown -R $(whoami) /etc/komodo
sudo mkdir -p /config/keys
sudo chown -R $(whoami) /config
-123
View File
@@ -1,123 +0,0 @@
# Komodo AI Coding Instructions
## Project Overview
Komodo is a distributed system for building and deploying software across multiple servers. It consists of:
- **Core** (`bin/core`): Central Rust/Axum API server managing resources, scheduling, monitoring
- **Periphery** (`bin/periphery`): Rust agent deployed to managed servers, executes docker/git operations
- **Frontend** (`frontend`): React/TypeScript SPA using TanStack Query and shadcn/ui components
- **Client Libraries** (`client/core/{rs,ts}`): Type-safe API clients generated from shared entity definitions
## Architecture Fundamentals
### Core ↔ Periphery Communication
- Core and Periphery communicate via **WebSocket connections** that can be initiated from either direction
- Periphery can connect outbound to Core (common) or Core can connect inbound to Periphery (less common)
- Connection management is in `bin/core/src/connection` and `bin/periphery/src/connection`
- Use `state::periphery_connections()` in Core to send requests to Periphery agents
### Type Generation Pipeline
**Critical**: Rust entities are the source of truth for all types across the stack:
1. Entities defined in `client/core/rs/src/entities/` with `#[typeshare]` macro
2. Run `node ./client/core/ts/generate_types.mjs` (uses typeshare CLI) → generates `client/core/ts/src/types.ts`
3. TypeScript client is built and linked to frontend via `yarn link komodo_client`
4. Frontend imports types: `import { Types } from "komodo_client"`
**When modifying entities**: Always run the `gen-client` (alias: `gc`) runfile task to regenerate TypeScript types.
### API Pattern (Resolver-Based)
Core and Periphery use `mogh_resolver::Resolve` trait for RPC-style endpoints:
- Request enums in `client/*/rs/src/api/` implement `Resolve<Response = T, Error = E>`
- Core handlers in `bin/core/src/api/{read,write,execute}/*.rs`
- Periphery handlers in `bin/periphery/src/api/*.rs`
- Router maps `/{variant}` paths to enum variants automatically
Example request handling:
```rust
#[derive(Serialize, Deserialize, Resolve)]
#[response(MyResponse)]
#[error(anyhow::Error)]
pub struct MyRequest { /* fields */ }
```
### State Management
Core maintains singleton state via `OnceLock` patterns in `bin/core/src/state.rs`:
- `db_client()`: MongoDB connection
- `periphery_connections()`: WebSocket connections to Periphery agents
- `action_states()`, `build_states()`, etc.: In-memory caches using `CloneCache<K, V>`
- Always use `state::*()` accessors, never instantiate directly
## Development Workflows
### Running Services Locally
```bash
# Start all local services (MongoDB, Core, Periphery, Frontend)
run dc # dev-core: runs Core in release mode with .dev/core.config.toml
run dp # dev-periphery: runs Periphery with .dev/periphery.config.toml
# Or use docker-compose for full stack
run dev-compose # deploys dev.compose.yaml with ferretdb
```
### Frontend Development
```bash
cd frontend && yarn dev # Vite dev server on port 5173
# After changing Rust entities, regenerate client:
run gc # gen-client: typeshare → build TS client → copy to frontend/public/client
```
### Building & Deploying
- Deno scripts in `action/` automate version bumps and deployments
- `run dk` (deploy-komodo): Full build and deploy via Komodo Action
- `run bk` (build-komodo): Build binaries without deploying
## Code Conventions
### Error Handling
- Use `anyhow::Result<T>` for application code
- Use `serror` for serializable errors sent over API boundaries
- Response helper: `response::Response::from(value)` for JSON responses
- Never use `unwrap()` in production code paths; prefer `context()` for error messages
### Naming & Structure
- **Entities**: CamelCase structs in `client/core/rs/src/entities/*/mod.rs`
- **API requests**: CamelCase request types in `client/core/rs/src/api/*.rs`
- **Config derivations**: Use `derive_builder::Builder` for config update structs
- **Variants**: `derive_variants::EnumVariants` generates `TypeVariant` enums for discriminants
### Frontend Patterns
- **API hooks**: `useRead("GetStack", { stack: "my-stack" })` for queries
- **Mutations**: `useWrite("UpdateStack", { onSuccess: () => invalidate(...) })`
- **Components**: Located in `frontend/src/components/`, use shadcn/ui primitives from `@ui/*`
- **State**: Prefer TanStack Query for server state, Jotai atoms for local UI state
## Key Files & Directories
### Must-Read for Context
- [readme.md](readme.md): Project overview and deployment info
- [runfile.toml](runfile.toml): All development task definitions
- [bin/core/src/state.rs](bin/core/src/state.rs): Core's global state singletons
- [client/core/rs/src/entities/mod.rs](client/core/rs/src/entities/mod.rs): All entity definitions
### Common Touch Points
- **Adding new resource type**: Update entities, Core API handlers, Frontend pages/components
- **Changing API**: Modify request/response types in `client/core/rs/src/api/`, regenerate client
- **Frontend features**: Usually start in `frontend/src/pages/`, add hooks in `frontend/src/lib/hooks/`
- **Periphery capabilities**: Add to `periphery_client::api` enum and implement handler in `bin/periphery/src/api/`
## Configuration
- Core config: `config/core.config.toml` (see `client/core/rs/src/entities/config/core.rs` for schema)
- Periphery config: `config/periphery.config.toml`
- Environment variable pattern: Settings support `_FILE` suffix for Docker secrets (see `lib/environment_file`)
## Testing & Validation
- No comprehensive test suite currently
- Validate changes by running locally with `run dc` / `run dp`
- Check Core logs for startup errors (connection to DB, Periphery agents)
- Frontend errors appear in browser console and TanStack Query devtools
## Common Pitfalls
- **Forgetting to regenerate TS types** after Rust entity changes → frontend type errors
- **Not using `state::*()` accessors** → panics from uninitialized `OnceLock`
- **Breaking WebSocket message format** between Core/Periphery → connection failures
- **Yarn link issues**: If frontend can't find `komodo_client`, re-run `run link-client`
+56
View File
@@ -0,0 +1,56 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: Build & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
+14 -14
View File
@@ -106,62 +106,62 @@
"problemMatcher": []
},
{
"label": "Init Frontend Client",
"label": "Init UI Client",
"type": "shell",
"command": "yarn link komodo_client && yarn install",
"options": {
"cwd": "${workspaceFolder}/frontend",
"cwd": "${workspaceFolder}/ui",
},
"problemMatcher": []
},
{
"label": "Init Frontend",
"label": "Init UI",
"dependsOn": [
"Build TS Client Types",
"Init TS Client",
"Init Frontend Client"
"Init UI Client"
],
"dependsOrder": "sequence",
"problemMatcher": []
},
{
"label": "Build Frontend",
"label": "Build UI",
"type": "shell",
"command": "yarn build",
"options": {
"cwd": "${workspaceFolder}/frontend",
"cwd": "${workspaceFolder}/ui",
},
"problemMatcher": []
},
{
"label": "Prepare Frontend For Run",
"label": "Prepare UI For Run",
"type": "shell",
"command": "cp -r ./client/core/ts/dist/. frontend/public/client/.",
"command": "cp -r ./client/core/ts/dist/. ui/public/client/.",
"options": {
"cwd": "${workspaceFolder}",
},
"dependsOn": [
"Build TS Client Types",
"Build Frontend"
"Build UI"
],
"dependsOrder": "sequence",
"problemMatcher": []
},
{
"label": "Run Frontend",
"label": "Run UI",
"type": "shell",
"command": "yarn dev",
"options": {
"cwd": "${workspaceFolder}/frontend",
"cwd": "${workspaceFolder}/ui",
},
"dependsOn": ["Prepare Frontend For Run"],
"dependsOn": ["Prepare UI For Run"],
"problemMatcher": []
},
{
"label": "Init",
"dependsOn": [
"Build Backend",
"Init Frontend"
"Init UI"
],
"dependsOrder": "sequence",
"problemMatcher": []
@@ -171,7 +171,7 @@
"dependsOn": [
"Run Core",
"Run Periphery",
"Run Frontend"
"Run UI"
],
"problemMatcher": []
},
Generated
+540 -317
View File
File diff suppressed because it is too large Load Diff
+38 -38
View File
@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
version = "2.0.0-dev-114"
version = "2.0.0"
edition = "2024"
authors = ["mbecker20 <becker.maxh@gmail.com>"]
license = "GPL-3.0-or-later"
@@ -36,34 +36,34 @@ slack = { version = "2.0.0", package = "slack_client_rs", default-features = fal
mogh_error = { version = "1.0.3", default-features = false }
derive_default_builder = "0.1.8"
async_timing_util = "1.1.0"
mogh_auth_client = "1.2.1"
mogh_auth_server = "1.2.4"
mogh_secret_file = "1.0.0"
mogh_validations = "1.0.0"
mogh_rate_limit = "1.0.0"
mogh_auth_client = "1.2.2"
mogh_auth_server = "1.2.13"
mogh_secret_file = "1.0.1"
mogh_validations = "1.0.1"
mogh_rate_limit = "1.0.1"
partial_derive2 = "0.4.5"
mongo_indexed = "2.0.2"
mogh_resolver = "1.0.0"
mogh_config = "1.0.1"
mogh_logger = "1.3.0"
mogh_server = "1.3.0"
mogh_config = "1.0.4"
mogh_logger = "1.3.2"
mogh_server = "1.4.5"
toml_pretty = "2.0.0"
mogh_cache = "1.1.1"
mogh_pki = "1.1.0"
mogh_pki = "1.1.3"
mungos = "3.2.2"
svi = "1.2.0"
# ASYNC
reqwest = { version = "0.13.1", default-features = false, features = ["json", "stream", "form", "query", "rustls"] }
tokio = { version = "1.49.0", features = ["full"] }
reqwest = { version = "0.13.2", default-features = false, features = ["json", "stream", "form", "query", "rustls"] }
tokio = { version = "1.50.0", features = ["full"] }
tokio-util = { version = "0.7.18", features = ["io", "codec"] }
tokio-stream = { version = "0.1.18", features = ["sync"] }
pin-project-lite = "0.2.16"
futures-util = "0.3.31"
arc-swap = "1.8.0"
pin-project-lite = "0.2.17"
futures-util = "0.3.32"
arc-swap = "1.9.0"
# SERVER
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-native-roots"] }
tokio-tungstenite = { version = "0.29.0", features = ["rustls-tls-native-roots"] }
axum = { version = "0.8.8", features = ["ws", "json", "macros"] }
axum-extra = { version = "0.12.5", features = ["typed-header"] }
@@ -75,36 +75,36 @@ utoipa = "5.4.0"
ipnetwork = { version = "0.21.1", features = ["serde"] }
indexmap = { version = "2.13.0", features = ["serde"] }
serde = { version = "1.0.227", features = ["derive"] }
strum = { version = "0.27.2", features = ["derive"] }
strum = { version = "0.28.0", features = ["derive"] }
bson = { version = "2.15.0" } # must keep in sync with mongodb version
toml = "0.9.11"
toml = "1.1.0"
serde_yaml_ng = "0.10.0"
serde_json = "1.0.148"
serde_qs = "0.15.0"
url = "2.5.7"
serde_json = "1.0.149"
serde_qs = "1.1.0"
url = "2.5.8"
# ERROR
anyhow = "1.0.100"
anyhow = "1.0.102"
thiserror = "2.0.18"
# LOGGING
tracing = "0.1.44"
# CONFIG
clap = { version = "4.5.54", features = ["derive"] }
clap = { version = "4.5.60", features = ["derive"] }
dotenvy = "0.15.7"
envy = "0.4.2"
# CRYPTO / AUTH
uuid = { version = "1.19.0", features = ["v4", "fast-rng", "serde"] }
rustls = { version = "0.23.36", features = ["aws-lc-rs"] }
data-encoding = "2.9.0"
uuid = { version = "1.21.0", features = ["v4", "fast-rng", "serde"] }
rustls = { version = "0.23.37", features = ["aws-lc-rs"] }
data-encoding = "2.10.0"
urlencoding = "2.1.3"
bcrypt = "0.18.0"
bcrypt = "0.19.0"
hmac = "0.12.1"
sha1 = "0.10.6"
sha2 = "0.10.9"
rand = "0.9.2"
rand = "0.10.0"
hex = "0.4.3"
# SYSTEM
@@ -112,27 +112,27 @@ hickory-resolver = "0.25.2"
portable-pty = "0.9.0"
shell-escape = "0.1.5"
crossterm = "0.29.0"
bollard = "0.20.0"
sysinfo = "0.37.1"
bollard = "0.20.2"
sysinfo = "0.38.4"
shlex = "1.3.0"
# CLOUD
aws-config = "1.8.12"
aws-sdk-ec2 = "1.200.0"
aws-credential-types = "1.2.11"
aws-config = "1.8.15"
aws-sdk-ec2 = "1.220.0"
aws-credential-types = "1.2.14"
## CRON
english-to-cron = "0.1.7"
chrono-tz = "0.10.4"
chrono = "0.4.43"
chrono = "0.4.44"
croner = "3.0.1"
# MISC
async-compression = { version = "0.4.37", features = ["tokio", "gzip"] }
async-compression = { version = "0.4.41", features = ["tokio", "gzip"] }
derive_builder = "0.20.2"
comfy-table = "7.2.2"
typeshare = "1.0.5"
wildcard = "0.3.0"
colored = "3.0.0"
bytes = "1.11.0"
regex = "1.12.2"
colored = "3.1.1"
bytes = "1.11.1"
regex = "1.12.3"
+1 -1
View File
@@ -1,7 +1,7 @@
## Builds the Komodo Core, Periphery, and Util binaries
## for a specific architecture. Requires OpenSSL 3 or later.
FROM rust:1.93.0-bookworm AS builder
FROM rust:1.94.0-bookworm AS builder
RUN cargo install cargo-strip
WORKDIR /builder
+1 -1
View File
@@ -3,7 +3,7 @@
## Uses chef for dependency caching to help speed up back-to-back builds.
FROM lukemathwalker/cargo-chef:latest-rust-1.90.0-bookworm AS chef
FROM lukemathwalker/cargo-chef:latest-rust-1.94.0-bookworm AS chef
WORKDIR /builder
# Plan just the RECIPE to see if things have changed
+2
View File
@@ -32,7 +32,9 @@ tracing.workspace = true
colored.workspace = true
dotenvy.workspace = true
anyhow.workspace = true
bcrypt.workspace = true
chrono.workspace = true
rustls.workspace = true
tokio.workspace = true
serde.workspace = true
clap.workspace = true
+1 -1
View File
@@ -1,4 +1,4 @@
FROM rust:1.93.0-bullseye AS builder
FROM rust:1.94.0-bullseye AS builder
RUN cargo install cargo-strip
WORKDIR /builder
+1 -1
View File
@@ -1,7 +1,7 @@
## Assumes the latest binaries for x86_64 and aarch64 are already built (by binaries.Dockerfile).
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
ARG X86_64_BINARIES=${BINARIES_IMAGE}-x86_64
ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
+1 -1
View File
@@ -1,6 +1,6 @@
## Assumes the latest binaries for the required arch are already built (by binaries.Dockerfile).
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
# This is required to work with COPY --from
FROM ${BINARIES_IMAGE} AS binaries
+13
View File
@@ -0,0 +1,13 @@
use anyhow::Context as _;
use komodo_client::api::read::GetCoreInfo;
pub async fn handle() -> anyhow::Result<()> {
let client = super::komodo_client().await?;
let info = client.read(GetCoreInfo {}).await?;
println!(
"{}",
serde_json::to_string_pretty(&info)
.context("Failed to serialize core info to JSON")?
);
Ok(())
}
+101
View File
@@ -0,0 +1,101 @@
use anyhow::Context as _;
use database::bson::doc;
use komodo_client::{
api::read::FindUser,
entities::{
api_key::ApiKey, config::cli::args::create::CreateApiKey,
komodo_timestamp, random_string,
},
};
use serde_json::json;
use crate::config::cli_config;
pub async fn create(
CreateApiKey {
name,
for_user,
expires,
use_api,
}: &CreateApiKey,
) -> anyhow::Result<()> {
let expires = if let Some(expires_days) = expires {
// now + expires in ms
komodo_timestamp() + expires_days * 24 * 60 * 60 * 1000
} else {
0
};
if *use_api {
// USE API
let client = crate::command::komodo_client().await?;
let keys = if let Some(username) = for_user {
// For service user
let user = client
.read(FindUser {
user: username.to_string(),
})
.await?;
client
.write(
komodo_client::api::write::CreateApiKeyForServiceUser {
user_id: user.id,
name: name.clone().unwrap_or_default(),
expires,
},
)
.await?
} else {
// For self
client
.auth_manage(komodo_client::api::auth::manage::CreateApiKey {
name: name.clone().unwrap_or_default(),
expires: expires as u64,
})
.await?
};
println!(
"{}",
serde_json::to_string_pretty(&keys)
.context("Failed to serialize api keys to JSON")?
);
} else {
// USE DATABASE
let db = database::Client::new(&cli_config().database).await?;
let user = db
.users
.find_one(doc! { "username": for_user })
.await
.context("Failed to query database for user")?
.context("No user found with given username")?;
let key = format!("K_{}_K", random_string(40));
let secret = format!("S_{}_S", random_string(40));
let hashed_secret = bcrypt::hash(&secret, 10)
.context("Failed at hashing secret string")?;
db.api_keys
.insert_one(&ApiKey {
name: name.clone().unwrap_or_default(),
user_id: user.id,
key: key.clone(),
secret: hashed_secret.clone(),
created_at: komodo_timestamp(),
expires,
})
.await?;
println!(
"{}",
serde_json::to_string_pretty(
&json!({ "key": key, "secret": secret })
)
.context("Failed to serialize api keys to JSON")?
);
}
Ok(())
}
+13
View File
@@ -0,0 +1,13 @@
use komodo_client::entities::config::cli::args::create::CreateCommand;
mod api_key;
mod onboarding_key;
pub async fn handle(command: &CreateCommand) -> anyhow::Result<()> {
match command {
CreateCommand::ApiKey(api_key) => api_key::create(api_key).await,
CreateCommand::OnboardingKey(onboarding_key) => {
onboarding_key::create(onboarding_key).await
}
}
}
@@ -0,0 +1,46 @@
use anyhow::Context as _;
use komodo_client::entities::{
config::cli::args::create::CreateOnboardingKey, komodo_timestamp,
};
pub async fn create(
CreateOnboardingKey {
name,
expires,
private_key,
tags,
privileged,
copy_server,
create_builder,
}: &CreateOnboardingKey,
) -> anyhow::Result<()> {
let expires = if let Some(expires_days) = expires {
// now + expires in ms
komodo_timestamp() + expires_days * 24 * 60 * 60 * 1000
} else {
0
};
// USE API
let client = crate::command::komodo_client().await?;
let key = client
.write(komodo_client::api::write::CreateOnboardingKey {
name: name.clone().unwrap_or_default(),
expires,
private_key: private_key.clone(),
tags: tags.clone(),
privileged: *privileged,
copy_server: copy_server.clone().unwrap_or_default(),
create_builder: *create_builder,
})
.await?;
println!(
"{}",
serde_json::to_string_pretty(&key)
.context("Failed to serialize onboarding key to JSON")?
);
Ok(())
}
+2
View File
@@ -15,6 +15,8 @@ use wildcard::Wildcard;
use crate::config::cli_config;
pub mod container;
pub mod core_info;
pub mod create;
pub mod database;
pub mod execute;
pub mod list;
+81 -39
View File
@@ -26,7 +26,7 @@ pub async fn handle_connect(
recreate,
}: &Connect,
) -> anyhow::Result<()> {
handle_terminal_forwarding(async {
handle_terminal_forwarding(server, async {
super::komodo_client()
.await?
.connect_server_terminal(
@@ -56,25 +56,28 @@ pub async fn handle_exec(
}: &Exec,
) -> anyhow::Result<()> {
let server = get_server(server.clone(), container).await?;
handle_terminal_forwarding(async {
super::komodo_client()
.await?
.connect_container_terminal(
server,
container.to_string(),
None,
Some(InitTerminal {
command: Some(shell.to_string()),
recreate: if *recreate {
TerminalRecreateMode::Always
} else {
TerminalRecreateMode::DifferentCommand
},
mode: Some(ContainerTerminalMode::Exec),
}),
)
.await
})
handle_terminal_forwarding(
&format!("{server}/{container}"),
async {
super::komodo_client()
.await?
.connect_container_terminal(
server,
container.to_string(),
None,
Some(InitTerminal {
command: Some(shell.to_string()),
recreate: if *recreate {
TerminalRecreateMode::Always
} else {
TerminalRecreateMode::DifferentCommand
},
mode: Some(ContainerTerminalMode::Exec),
}),
)
.await
},
)
.await
}
@@ -86,25 +89,28 @@ pub async fn handle_attach(
}: &Attach,
) -> anyhow::Result<()> {
let server = get_server(server.clone(), container).await?;
handle_terminal_forwarding(async {
super::komodo_client()
.await?
.connect_container_terminal(
server,
container.to_string(),
None,
Some(InitTerminal {
command: None,
recreate: if *recreate {
TerminalRecreateMode::Always
} else {
TerminalRecreateMode::DifferentCommand
},
mode: Some(ContainerTerminalMode::Attach),
}),
)
.await
})
handle_terminal_forwarding(
&format!("{server}/{container}-attach"),
async {
super::komodo_client()
.await?
.connect_container_terminal(
server,
container.to_string(),
None,
Some(InitTerminal {
command: None,
recreate: if *recreate {
TerminalRecreateMode::Always
} else {
TerminalRecreateMode::DifferentCommand
},
mode: Some(ContainerTerminalMode::Attach),
}),
)
.await
},
)
.await
}
@@ -163,6 +169,7 @@ async fn get_server(
async fn handle_terminal_forwarding<
C: Future<Output = anyhow::Result<TerminalWebsocket>>,
>(
label: &str,
connect: C,
) -> anyhow::Result<()> {
// Need to forward multiple sources into ws write
@@ -247,6 +254,16 @@ async fn handle_terminal_forwarding<
let forward_read = async {
let mut stdout = tokio::io::stdout();
// Write connection message
if let Err(e) = write_connection_message(&mut stdout, label)
.await
.context("Failed to write text to stdout")
{
cancel.cancel();
return Some(e);
}
while let Some(msg) =
future_or_cancel(ws_read.receive_stdout(), &cancel).await
{
@@ -297,6 +314,31 @@ async fn handle_terminal_forwarding<
std::process::exit(0)
}
async fn write_connection_message(
stdout: &mut tokio::io::Stdout,
label: &str,
) -> anyhow::Result<()> {
// Use message without ansi for correct length
let message_clean = format!("# Connected to {label} (km) #");
let bounder = "=".repeat(message_clean.chars().count());
let message = format!(
"# {} to {} {} #",
"Connected".green().bold(),
label.bold(),
"(km)".dimmed()
);
stdout
.write_all(
format!("\n{bounder}\r\n{message}\r\n{bounder}\r\n").as_bytes(),
)
.await?;
let _ = stdout.flush().await;
Ok(())
}
fn resize_message() -> anyhow::Result<TerminalStdinMessage> {
let (cols, rows) = crossterm::terminal::size()
.context("Failed to get terminal size")?;
+7
View File
@@ -12,6 +12,9 @@ mod config;
async fn app() -> anyhow::Result<()> {
dotenvy::dotenv().ok();
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("Failed to install default crypto provider");
mogh_logger::init(&config::cli_config().cli_logging)?;
let args = config::cli_args();
let env = config::cli_env();
@@ -42,6 +45,7 @@ async fn app() -> anyhow::Result<()> {
}
Ok(())
}
args::Command::CoreInfo => command::core_info::handle().await,
args::Command::Container(container) => {
command::container::handle(container).await
}
@@ -52,6 +56,9 @@ async fn app() -> anyhow::Result<()> {
args::Command::Execute(args) => {
command::execute::handle(&args.execution, args.yes).await
}
args::Command::Create { command } => {
command::create::handle(command).await
}
args::Command::Update { command } => {
command::update::handle(command).await
}
+6 -6
View File
@@ -1,7 +1,7 @@
## All in one, multi stage compile + runtime Docker build for your architecture.
# Build Core
FROM rust:1.93.0-trixie AS core-builder
FROM rust:1.94.0-trixie AS core-builder
RUN cargo install cargo-strip
WORKDIR /builder
@@ -17,13 +17,13 @@ RUN cargo build -p komodo_core --release && \
cargo build -p komodo_cli --release && \
cargo strip
# Build Frontend
FROM node:22.12-alpine AS frontend-builder
# Build UI
FROM node:22.12-alpine AS ui-builder
WORKDIR /builder
COPY ./frontend ./frontend
COPY ./ui ./ui
COPY ./client/core/ts ./client
RUN cd client && yarn && yarn build && yarn link
RUN cd frontend && yarn link komodo_client && yarn && yarn build
RUN cd ui && yarn link komodo_client && yarn && yarn build
# Final Image
FROM debian:trixie-slim
@@ -37,7 +37,7 @@ WORKDIR /app
# Copy
COPY ./config/core.config.toml /config/.default.config.toml
COPY --from=frontend-builder /builder/frontend/dist /app/frontend
COPY --from=ui-builder /builder/ui/dist /app/ui
COPY --from=core-builder /builder/target/release/core /usr/local/bin/core
COPY --from=core-builder /builder/target/release/km /usr/local/bin/km
COPY --from=denoland/deno:bin /deno /usr/local/bin/deno
+5 -5
View File
@@ -2,15 +2,15 @@
## Sets up the necessary runtime container dependencies for Komodo Core.
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
ARG FRONTEND_IMAGE=ghcr.io/moghtech/komodo-frontend:latest
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
ARG UI_IMAGE=ghcr.io/moghtech/komodo-ui:2
ARG X86_64_BINARIES=${BINARIES_IMAGE}-x86_64
ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
# This is required to work with COPY --from
FROM ${X86_64_BINARIES} AS x86_64
FROM ${AARCH64_BINARIES} AS aarch64
FROM ${FRONTEND_IMAGE} AS frontend
FROM ${UI_IMAGE} AS ui
# Final Image
FROM debian:trixie-slim
@@ -33,9 +33,9 @@ COPY --from=x86_64 /km /app/km/linux/amd64
COPY --from=aarch64 /km /app/km/linux/arm64
RUN mv /app/km/${TARGETPLATFORM} /usr/local/bin/km && rm -r /app/km
# Copy default config / static frontend / deno binary
# Copy default config / static ui / deno binary
COPY ./config/core.config.toml /config/.default.config.toml
COPY --from=frontend /frontend /app/frontend
COPY --from=ui /ui /app/ui
COPY --from=denoland/deno:bin /deno /usr/local/bin/deno
# Set $DENO_DIR and preload external Deno deps
+6 -6
View File
@@ -1,18 +1,18 @@
## Assumes the latest binaries for the required arch are already built (by binaries.Dockerfile).
## Sets up the necessary runtime container dependencies for Komodo Core.
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
# This is required to work with COPY --from
FROM ${BINARIES_IMAGE} AS binaries
# Build Frontend
FROM node:22.12-alpine AS frontend-builder
# Build UI
FROM node:22.12-alpine AS ui-builder
WORKDIR /builder
COPY ./frontend ./frontend
COPY ./ui ./ui
COPY ./client/core/ts ./client
RUN cd client && yarn && yarn build && yarn link
RUN cd frontend && yarn link komodo_client && yarn && yarn build
RUN cd ui && yarn link komodo_client && yarn && yarn build
FROM debian:trixie-slim
@@ -22,7 +22,7 @@ RUN sh ./debian-deps.sh && rm ./debian-deps.sh
# Copy
COPY ./config/core.config.toml /config/.default.config.toml
COPY --from=frontend-builder /builder/frontend/dist /app/frontend
COPY --from=ui-builder /builder/ui/dist /app/ui
COPY --from=binaries /core /usr/local/bin/core
COPY --from=binaries /km /usr/local/bin/km
COPY --from=denoland/deno:bin /deno /usr/local/bin/deno
+9 -7
View File
@@ -20,7 +20,9 @@ pub async fn send_alert(
let link = resource_link(ResourceTargetVariant::Swarm, id);
match alert.level {
SeverityLevel::Ok => {
format!("{level} | Swarm **{name}** is now **healthy**\n{link}")
format!(
"{level} | Swarm **{name}** is now **healthy**\n{link}"
)
}
SeverityLevel::Critical => {
let err = err
@@ -140,7 +142,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("\nserver: **{server}**")
} else {
format!("")
String::new()
};
format!(
"📦 Deployment **{name}** is now **{to}**{target}\nprevious: **{from}**\n{link}"
@@ -161,7 +163,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("\nserver: **{server}**")
} else {
format!("")
String::new()
};
format!(
"⬆ Deployment **{name}** has an update available{target}\nimage: **{image}**\n{link}"
@@ -182,7 +184,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("\nserver: **{server}**")
} else {
format!("")
String::new()
};
format!(
"⬆ Deployment **{name}** was updated automatically ⏫{target}\nimage: **{image}**\n{link}"
@@ -205,7 +207,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("\nserver: **{server}**")
} else {
format!("")
String::new()
};
format!(
"🥞 Stack **{name}** is now {to}{target}\nprevious: **{from}**\n{link}"
@@ -227,7 +229,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("\nserver: **{server}**")
} else {
format!("")
String::new()
};
format!(
"⬆ Stack **{name}** has an update available{target}\nservice: **{service}**\nimage: **{image}**\n{link}"
@@ -251,7 +253,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("\nserver: **{server}**")
} else {
format!("")
String::new()
};
format!(
"⬆ Stack **{name}** was updated automatically ⏫{target}\n{images_label}: **{images}**\n{link}"
+9 -7
View File
@@ -262,7 +262,9 @@ fn standard_alert_content(alert: &Alert) -> String {
.as_ref()
.map(|e| format!("\nerror: {e}"))
.unwrap_or_default();
format!("{level} | Swarm {name} is unhealthy ❌\n{link}{err}")
format!(
"{level} | Swarm {name} is unhealthy ❌\n{link}{err}"
)
}
_ => unreachable!(),
}
@@ -371,7 +373,7 @@ fn standard_alert_content(alert: &Alert) -> String {
} else if let Some(server) = server_name {
format!("\nserver: {server}")
} else {
format!("")
String::new()
};
format!(
"📦Deployment {name} is now {to_state}{target}\nprevious: {from}\n{link}",
@@ -392,7 +394,7 @@ fn standard_alert_content(alert: &Alert) -> String {
} else if let Some(server) = server_name {
format!("\nserver: {server}")
} else {
format!("")
String::new()
};
format!(
"⬆ Deployment {name} has an update available{target}\nimage: {image}\n{link}",
@@ -413,7 +415,7 @@ fn standard_alert_content(alert: &Alert) -> String {
} else if let Some(server) = server_name {
format!("\nserver: {server}")
} else {
format!("")
String::new()
};
format!(
"⬆ Deployment {name} was updated automatically{target}\nimage: {image}\n{link}",
@@ -436,7 +438,7 @@ fn standard_alert_content(alert: &Alert) -> String {
} else if let Some(server) = server_name {
format!("\nserver: {server}")
} else {
format!("")
String::new()
};
format!(
"🥞 Stack {name} is now {to_state}{target}\nprevious: {from}\n{link}",
@@ -458,7 +460,7 @@ fn standard_alert_content(alert: &Alert) -> String {
} else if let Some(server) = server_name {
format!("\nserver: {server}")
} else {
format!("")
String::new()
};
format!(
"⬆ Stack {name} has an update available{target}\nservice: {service}\nimage: {image}\n{link}",
@@ -482,7 +484,7 @@ fn standard_alert_content(alert: &Alert) -> String {
} else if let Some(server) = server_name {
format!("\nserver: {server}")
} else {
format!("")
String::new()
};
format!(
"⬆ Stack {name} was updated automatically ⏫{target}\n{images_label}: {images_str}\n{link}",
+6 -6
View File
@@ -289,7 +289,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("server: *{server}*\n")
} else {
format!("")
String::new()
};
let blocks = vec![
Block::header(text.clone()),
@@ -317,7 +317,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("server: *{server}*\n")
} else {
format!("")
String::new()
};
let blocks = vec![
Block::header(text.clone()),
@@ -345,7 +345,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("server: *{server}*\n")
} else {
format!("")
String::new()
};
let blocks = vec![
Block::header(text.clone()),
@@ -374,7 +374,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("server: *{server}*\n")
} else {
format!("")
String::new()
};
let blocks = vec![
Block::header(text.clone()),
@@ -402,7 +402,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("server: *{server}*\n")
} else {
format!("")
String::new()
};
let blocks = vec![
Block::header(text.clone()),
@@ -435,7 +435,7 @@ pub async fn send_alert(
} else if let Some(server) = server_name {
format!("server: *{server}*\n")
} else {
format!("")
String::new()
};
let blocks = vec![
Block::header(text.clone()),
+8 -4
View File
@@ -66,14 +66,14 @@ impl Resolve<ExecuteArgs> for BatchRunAction {
"BatchRunAction",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchRunAction>(&self.pattern, user)
@@ -87,7 +87,7 @@ impl Resolve<ExecuteArgs> for RunAction {
"RunAction",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
action = self.action,
@@ -95,7 +95,11 @@ impl Resolve<ExecuteArgs> for RunAction {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let mut action = get_check_permissions::<Action>(
&self.action,
+12 -4
View File
@@ -28,7 +28,7 @@ impl Resolve<ExecuteArgs> for TestAlerter {
"TestAlerter",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
alerter = self.alerter,
@@ -36,7 +36,11 @@ impl Resolve<ExecuteArgs> for TestAlerter {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let alerter = get_check_permissions::<Alerter>(
&self.alerter,
@@ -94,7 +98,7 @@ impl Resolve<ExecuteArgs> for SendAlert {
"SendAlert",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
request = format!("{self:?}"),
@@ -102,7 +106,11 @@ impl Resolve<ExecuteArgs> for SendAlert {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let alerters = list_full_for_user::<Alerter>(
Default::default(),
+15 -7
View File
@@ -75,14 +75,14 @@ impl Resolve<ExecuteArgs> for BatchRunBuild {
"BatchRunBuild",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchRunBuild>(&self.pattern, user)
@@ -96,7 +96,7 @@ impl Resolve<ExecuteArgs> for RunBuild {
"RunBuild",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
build = self.build,
@@ -104,7 +104,11 @@ impl Resolve<ExecuteArgs> for RunBuild {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let mut build = get_check_permissions::<Build>(
&self.build,
@@ -522,7 +526,7 @@ impl Resolve<ExecuteArgs> for CancelBuild {
"CancelBuild",
skip(user, update),
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
build = self.build,
@@ -530,7 +534,11 @@ impl Resolve<ExecuteArgs> for CancelBuild {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let build = get_check_permissions::<Build>(
&self.build,
@@ -626,7 +634,7 @@ async fn handle_post_build_redeploy(build_id: &str) {
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
}
+71 -39
View File
@@ -32,7 +32,7 @@ use crate::{
swarm::swarm_request,
update::update_update,
},
monitor::{update_cache_for_server, update_cache_for_swarm},
monitor::{refresh_server_cache, refresh_swarm_cache},
resource::{self, setup_deployment_execution},
state::action_states,
};
@@ -55,14 +55,14 @@ impl Resolve<ExecuteArgs> for BatchDeploy {
"BatchDeploy",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchDeploy>(&self.pattern, user)
@@ -76,7 +76,7 @@ impl Resolve<ExecuteArgs> for Deploy {
"Deploy",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -86,7 +86,11 @@ impl Resolve<ExecuteArgs> for Deploy {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (mut deployment, swarm_or_server) =
setup_deployment_execution(
@@ -111,7 +115,7 @@ impl Resolve<ExecuteArgs> for Deploy {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
// This block resolves the attached Build to an actual versioned image
@@ -222,7 +226,7 @@ impl Resolve<ExecuteArgs> for Deploy {
.await
{
Ok(logs) => {
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
update.logs.extend(logs)
}
Err(e) => {
@@ -246,7 +250,7 @@ impl Resolve<ExecuteArgs> for Deploy {
.await
{
Ok(log) => {
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.logs.push(log)
}
Err(e) => {
@@ -401,7 +405,7 @@ pub async fn pull_deployment_inner(
Err(e) => Log::error("Pull image", format_serror(&e.into())),
};
update_cache_for_server(server, true).await;
refresh_server_cache(server, true).await;
anyhow::Ok(log)
}
.await;
@@ -418,7 +422,7 @@ impl Resolve<ExecuteArgs> for PullDeployment {
"PullDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -426,7 +430,11 @@ impl Resolve<ExecuteArgs> for PullDeployment {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (deployment, swarm_or_server) = setup_deployment_execution(
&self.deployment,
@@ -454,7 +462,7 @@ impl Resolve<ExecuteArgs> for PullDeployment {
action_state.update(|state| state.pulling = true)?;
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let log = pull_deployment_inner(deployment, &server).await?;
@@ -472,7 +480,7 @@ impl Resolve<ExecuteArgs> for StartDeployment {
"StartDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -480,7 +488,11 @@ impl Resolve<ExecuteArgs> for StartDeployment {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (deployment, swarm_or_server) = setup_deployment_execution(
&self.deployment,
@@ -509,7 +521,7 @@ impl Resolve<ExecuteArgs> for StartDeployment {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let log = match periphery_client(&server)
@@ -527,7 +539,7 @@ impl Resolve<ExecuteArgs> for StartDeployment {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -540,7 +552,7 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
"RestartDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -548,7 +560,11 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (deployment, swarm_or_server) = setup_deployment_execution(
&self.deployment,
@@ -577,7 +593,7 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let log = match periphery_client(&server)
@@ -597,7 +613,7 @@ impl Resolve<ExecuteArgs> for RestartDeployment {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -610,7 +626,7 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
"PauseDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -618,7 +634,11 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (deployment, swarm_or_server) = setup_deployment_execution(
&self.deployment,
@@ -647,7 +667,7 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let log = match periphery_client(&server)
@@ -665,7 +685,7 @@ impl Resolve<ExecuteArgs> for PauseDeployment {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -678,7 +698,7 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
"UnpauseDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -686,7 +706,11 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (deployment, swarm_or_server) = setup_deployment_execution(
&self.deployment,
@@ -715,7 +739,7 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let log = match periphery_client(&server)
@@ -735,7 +759,7 @@ impl Resolve<ExecuteArgs> for UnpauseDeployment {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -748,7 +772,7 @@ impl Resolve<ExecuteArgs> for StopDeployment {
"StopDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -758,7 +782,11 @@ impl Resolve<ExecuteArgs> for StopDeployment {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (deployment, swarm_or_server) = setup_deployment_execution(
&self.deployment,
@@ -787,7 +815,7 @@ impl Resolve<ExecuteArgs> for StopDeployment {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let log = match periphery_client(&server)
@@ -813,7 +841,7 @@ impl Resolve<ExecuteArgs> for StopDeployment {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -837,14 +865,14 @@ impl Resolve<ExecuteArgs> for BatchDestroyDeployment {
"BatchDestroyDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchDestroyDeployment>(
@@ -861,7 +889,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
"DestroyDeployment",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
deployment = self.deployment,
@@ -871,7 +899,11 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (deployment, swarm_or_server) = setup_deployment_execution(
&self.deployment,
@@ -895,7 +927,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let log = match swarm_or_server {
@@ -910,7 +942,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
.await
{
Ok(log) => {
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
log
}
Err(e) => Log::error(
@@ -938,7 +970,7 @@ impl Resolve<ExecuteArgs> for DestroyDeployment {
.await
{
Ok(log) => {
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
log
}
Err(e) => Log::error(
+30 -10
View File
@@ -54,14 +54,18 @@ impl Resolve<ExecuteArgs> for ClearRepoCache {
"ClearRepoCache",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id
)
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
if !user.admin {
return Err(
@@ -133,14 +137,18 @@ impl Resolve<ExecuteArgs> for BackupCoreDatabase {
"BackupCoreDatabase",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
)
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
if !user.admin {
return Err(
@@ -186,14 +194,18 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
"GlobalAutoUpdate",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
)
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
if !user.admin {
return Err(
@@ -368,14 +380,18 @@ impl Resolve<ExecuteArgs> for RotateAllServerKeys {
"RotateAllServerKeys",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
)
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
if !user.admin {
return Err(
@@ -482,7 +498,7 @@ impl Resolve<ExecuteArgs> for RotateCoreKeys {
"RotateCoreKeys",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
force = self.force,
@@ -490,7 +506,11 @@ impl Resolve<ExecuteArgs> for RotateCoreKeys {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
if !user.admin {
return Err(
+51 -21
View File
@@ -49,9 +49,9 @@ mod sync;
use super::Variant;
pub struct ExecuteArgs {
/// The execution id.
/// Unique for every /execute call.
pub id: Uuid,
/// The task id.
/// Unique for every '/execute' call.
pub task_id: Uuid,
pub user: User,
pub update: Update,
}
@@ -60,7 +60,7 @@ pub struct ExecuteArgs {
#[derive(
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
)]
#[strum_discriminants(name(ExecuteRequestVariant), derive(Display))]
#[strum_discriminants(name(ExecuteRequestMethod), derive(Display))]
#[args(ExecuteArgs)]
#[response(JsonString)]
#[error(mogh_error::Error)]
@@ -249,11 +249,19 @@ pub fn inner_handler(
async move {
let log = match handle.await {
Ok(Err(e)) => {
warn!("/execute request {task_id} task error: {e:#}",);
warn!(
api = "Execute",
task_id = task_id.to_string(),
"/execute request task error: {e:#}",
);
Log::error("Task Error", format_serror(&e.into()))
}
Err(e) => {
warn!("/execute request {task_id} spawn error: {e:?}",);
warn!(
api = "Execute",
task_id = task_id.to_string(),
"/execute request spawn error: {e:?}",
);
Log::error("Spawn Error", format!("{e:#?}"))
}
_ => return,
@@ -268,7 +276,7 @@ pub fn inner_handler(
find_one_by_id(&db_client().updates, &update_id)
.await
.context("Failed to query to db")?
.context("No update exists with given id")?;
.context("No Update exists with given id")?;
update.logs.push(log);
update.finalize();
update_update(update).await
@@ -277,7 +285,10 @@ pub fn inner_handler(
if let Err(e) = res {
warn!(
"Failed to update update with task error log | {e:#}"
api = "Execute",
task_id = task_id.to_string(),
update_id,
"Failed to modify Update with task error log | {e:#}"
);
}
}
@@ -293,25 +304,44 @@ async fn task(
user: User,
update: Update,
) -> anyhow::Result<String> {
let variant: ExecuteRequestVariant = (&request).into();
let method: ExecuteRequestMethod = (&request).into();
let user_id = user.id.clone();
let username = user.username.clone();
info!(
"EXECUTE REQUEST {id} | METHOD: {variant} | USER: {} ({})",
user.username, user.id
api = "Execute",
task_id = id.to_string(),
method = method.to_string(),
user_id,
username,
"EXECUTE REQUEST",
);
let res =
match request.resolve(&ExecuteArgs { user, update, id }).await {
Err(e) => Err(e.error),
Ok(JsonString::Err(e)) => Err(
anyhow::Error::from(e)
.context("failed to serialize response"),
),
Ok(JsonString::Ok(res)) => Ok(res),
};
let res = match request
.resolve(&ExecuteArgs {
user,
update,
task_id: id,
})
.await
{
Err(e) => Err(e.error),
Ok(JsonString::Err(e)) => Err(
anyhow::Error::from(e).context("failed to serialize response"),
),
Ok(JsonString::Ok(res)) => Ok(res),
};
if let Err(e) = &res {
warn!("EXECUTE REQUEST {id} | METHOD: {variant} | ERROR: {e:#}");
warn!(
api = "Execute",
task_id = id.to_string(),
method = method.to_string(),
user_id,
username,
"EXECUTE REQUEST | ERROR: {e:#}"
);
}
res
+6 -2
View File
@@ -59,7 +59,7 @@ impl Resolve<ExecuteArgs> for RunProcedure {
"RunProcedure",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
procedure = self.procedure,
@@ -67,7 +67,11 @@ impl Resolve<ExecuteArgs> for RunProcedure {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
Ok(
resolve_inner(self.procedure, user.clone(), update.clone())
+30 -14
View File
@@ -55,14 +55,14 @@ impl Resolve<ExecuteArgs> for BatchCloneRepo {
"BatchCloneRepo",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchCloneRepo>(&self.pattern, user)
@@ -76,7 +76,7 @@ impl Resolve<ExecuteArgs> for CloneRepo {
"CloneRepo",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
repo = self.repo,
@@ -84,7 +84,11 @@ impl Resolve<ExecuteArgs> for CloneRepo {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let mut repo = get_check_permissions::<Repo>(
&self.repo,
@@ -186,14 +190,14 @@ impl Resolve<ExecuteArgs> for BatchPullRepo {
"BatchPullRepo",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchPullRepo>(&self.pattern, user)
@@ -207,7 +211,7 @@ impl Resolve<ExecuteArgs> for PullRepo {
"PullRepo",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
repo = self.repo,
@@ -215,7 +219,11 @@ impl Resolve<ExecuteArgs> for PullRepo {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let mut repo = get_check_permissions::<Repo>(
&self.repo,
@@ -363,14 +371,14 @@ impl Resolve<ExecuteArgs> for BatchBuildRepo {
"BatchBuildRepo",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchBuildRepo>(&self.pattern, user)
@@ -384,7 +392,7 @@ impl Resolve<ExecuteArgs> for BuildRepo {
"BuildRepo",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
repo = self.repo,
@@ -392,7 +400,11 @@ impl Resolve<ExecuteArgs> for BuildRepo {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let mut repo = get_check_permissions::<Repo>(
&self.repo,
@@ -705,7 +717,7 @@ impl Resolve<ExecuteArgs> for CancelRepoBuild {
"CancelRepoBuild",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
repo = self.repo,
@@ -713,7 +725,11 @@ impl Resolve<ExecuteArgs> for CancelRepoBuild {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let repo = get_check_permissions::<Repo>(
&self.repo,
+154 -70
View File
@@ -14,7 +14,7 @@ use periphery_client::api;
use crate::{
helpers::{periphery_client, update::update_update},
monitor::update_cache_for_server,
monitor::refresh_server_cache,
permission::get_check_permissions,
state::action_states,
};
@@ -26,7 +26,7 @@ impl Resolve<ExecuteArgs> for StartContainer {
"StartContainer",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -35,7 +35,11 @@ impl Resolve<ExecuteArgs> for StartContainer {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -57,7 +61,7 @@ impl Resolve<ExecuteArgs> for StartContainer {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server).await?;
@@ -76,7 +80,7 @@ impl Resolve<ExecuteArgs> for StartContainer {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -90,7 +94,7 @@ impl Resolve<ExecuteArgs> for RestartContainer {
"RestartContainer",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -99,7 +103,11 @@ impl Resolve<ExecuteArgs> for RestartContainer {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -121,7 +129,7 @@ impl Resolve<ExecuteArgs> for RestartContainer {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server).await?;
@@ -142,7 +150,7 @@ impl Resolve<ExecuteArgs> for RestartContainer {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -156,7 +164,7 @@ impl Resolve<ExecuteArgs> for PauseContainer {
"PauseContainer",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -165,7 +173,11 @@ impl Resolve<ExecuteArgs> for PauseContainer {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -187,7 +199,7 @@ impl Resolve<ExecuteArgs> for PauseContainer {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server).await?;
@@ -206,7 +218,7 @@ impl Resolve<ExecuteArgs> for PauseContainer {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -220,7 +232,7 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
"UnpauseContainer",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -229,7 +241,11 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -251,7 +267,7 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server).await?;
@@ -272,7 +288,7 @@ impl Resolve<ExecuteArgs> for UnpauseContainer {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -286,7 +302,7 @@ impl Resolve<ExecuteArgs> for StopContainer {
"StopContainer",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -297,7 +313,11 @@ impl Resolve<ExecuteArgs> for StopContainer {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -319,7 +339,7 @@ impl Resolve<ExecuteArgs> for StopContainer {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server).await?;
@@ -340,7 +360,7 @@ impl Resolve<ExecuteArgs> for StopContainer {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -354,7 +374,7 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
"DestroyContainer",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -365,7 +385,11 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let DestroyContainer {
server,
@@ -393,7 +417,7 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
let mut update = update.clone();
// Send update after setting action state, this way frontend gets correct state.
// Send update after setting action state, this way UI gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server).await?;
@@ -416,7 +440,7 @@ impl Resolve<ExecuteArgs> for DestroyContainer {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -430,7 +454,7 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
"StartAllContainers",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -438,7 +462,11 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -477,7 +505,7 @@ impl Resolve<ExecuteArgs> for StartAllContainers {
);
}
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -490,7 +518,7 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
"RestartAllContainers",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -498,7 +526,11 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -539,7 +571,7 @@ impl Resolve<ExecuteArgs> for RestartAllContainers {
);
}
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -552,7 +584,7 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
"PauseAllContainers",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -560,7 +592,11 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -599,7 +635,7 @@ impl Resolve<ExecuteArgs> for PauseAllContainers {
);
}
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -612,7 +648,7 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
"UnpauseAllContainers",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -620,7 +656,11 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -661,7 +701,7 @@ impl Resolve<ExecuteArgs> for UnpauseAllContainers {
);
}
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -674,7 +714,7 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
"StopAllContainers",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -682,7 +722,11 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -721,7 +765,7 @@ impl Resolve<ExecuteArgs> for StopAllContainers {
);
}
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -734,7 +778,7 @@ impl Resolve<ExecuteArgs> for PruneContainers {
"PruneContainers",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -742,7 +786,11 @@ impl Resolve<ExecuteArgs> for PruneContainers {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -785,7 +833,7 @@ impl Resolve<ExecuteArgs> for PruneContainers {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -799,7 +847,7 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
"DeleteNetwork",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -808,7 +856,11 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -846,7 +898,7 @@ impl Resolve<ExecuteArgs> for DeleteNetwork {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -860,7 +912,7 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
"PruneNetworks",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -868,7 +920,11 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -909,7 +965,7 @@ impl Resolve<ExecuteArgs> for PruneNetworks {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -923,7 +979,7 @@ impl Resolve<ExecuteArgs> for DeleteImage {
"DeleteImage",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -932,7 +988,11 @@ impl Resolve<ExecuteArgs> for DeleteImage {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -967,7 +1027,7 @@ impl Resolve<ExecuteArgs> for DeleteImage {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -981,7 +1041,7 @@ impl Resolve<ExecuteArgs> for PruneImages {
"PruneImages",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -989,7 +1049,11 @@ impl Resolve<ExecuteArgs> for PruneImages {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -1028,7 +1092,7 @@ impl Resolve<ExecuteArgs> for PruneImages {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -1042,7 +1106,7 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
"DeleteVolume",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -1051,7 +1115,11 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -1089,7 +1157,7 @@ impl Resolve<ExecuteArgs> for DeleteVolume {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -1103,7 +1171,7 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
"PruneVolumes",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -1111,7 +1179,11 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -1150,7 +1222,7 @@ impl Resolve<ExecuteArgs> for PruneVolumes {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -1164,7 +1236,7 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
"PruneDockerBuilders",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -1172,7 +1244,11 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -1211,7 +1287,7 @@ impl Resolve<ExecuteArgs> for PruneDockerBuilders {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -1225,7 +1301,7 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
"PruneBuildx",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -1233,7 +1309,11 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -1272,7 +1352,7 @@ impl Resolve<ExecuteArgs> for PruneBuildx {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -1286,7 +1366,7 @@ impl Resolve<ExecuteArgs> for PruneSystem {
"PruneSystem",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
server = self.server,
@@ -1294,7 +1374,11 @@ impl Resolve<ExecuteArgs> for PruneSystem {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let server = get_check_permissions::<Server>(
&self.server,
@@ -1332,7 +1416,7 @@ impl Resolve<ExecuteArgs> for PruneSystem {
};
update.logs.push(log);
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
+77 -37
View File
@@ -39,7 +39,7 @@ use crate::{
add_update_without_send, init_execution_update, update_update,
},
},
monitor::{update_cache_for_server, update_cache_for_swarm},
monitor::{refresh_server_cache, refresh_swarm_cache},
permission::get_check_permissions,
resource,
stack::{
@@ -69,14 +69,14 @@ impl Resolve<ExecuteArgs> for BatchDeployStack {
"BatchDeployStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchDeployStack>(&self.pattern, user)
@@ -90,7 +90,7 @@ impl Resolve<ExecuteArgs> for DeployStack {
"DeployStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -100,7 +100,11 @@ impl Resolve<ExecuteArgs> for DeployStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (mut stack, swarm_or_server) = setup_stack_execution(
&self.stack,
@@ -313,10 +317,10 @@ impl Resolve<ExecuteArgs> for DeployStack {
match swarm_or_server {
SwarmOrServer::None => unreachable!(),
SwarmOrServer::Swarm(swarm) => {
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
SwarmOrServer::Server(server) => {
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
}
}
@@ -342,14 +346,14 @@ impl Resolve<ExecuteArgs> for BatchDeployStackIfChanged {
"BatchDeployStackIfChanged",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchDeployStackIfChanged>(
@@ -366,7 +370,7 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
"DeployStackIfChanged",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -375,7 +379,11 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let stack = get_check_permissions::<Stack>(
&self.stack,
@@ -433,7 +441,7 @@ impl Resolve<ExecuteArgs> for DeployStackIfChanged {
.resolve(&ExecuteArgs {
user: user.clone(),
update,
id: *id,
task_id: *task_id,
})
.await
}
@@ -572,7 +580,7 @@ async fn deploy_services(
.resolve(&ExecuteArgs {
user: user.clone(),
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
}
@@ -603,7 +611,7 @@ async fn restart_services(
.resolve(&ExecuteArgs {
user: user.clone(),
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
}
@@ -766,14 +774,14 @@ impl Resolve<ExecuteArgs> for BatchPullStack {
"BatchPullStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
Ok(
super::batch_execute::<BatchPullStack>(&self.pattern, user)
@@ -878,7 +886,7 @@ pub async fn pull_stack_inner(
.await?;
// Ensure cached stack state up to date by updating server cache
update_cache_for_server(server, true).await;
refresh_server_cache(server, true).await;
Ok(res)
}
@@ -888,7 +896,7 @@ impl Resolve<ExecuteArgs> for PullStack {
"PullStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -897,7 +905,11 @@ impl Resolve<ExecuteArgs> for PullStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (stack, swarm_or_server) = setup_stack_execution(
&self.stack,
@@ -959,7 +971,7 @@ impl Resolve<ExecuteArgs> for StartStack {
"StartStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -968,7 +980,11 @@ impl Resolve<ExecuteArgs> for StartStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
execute_compose::<StartStack>(
&self.stack,
@@ -988,7 +1004,7 @@ impl Resolve<ExecuteArgs> for RestartStack {
"RestartStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -997,7 +1013,11 @@ impl Resolve<ExecuteArgs> for RestartStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
execute_compose::<RestartStack>(
&self.stack,
@@ -1019,7 +1039,7 @@ impl Resolve<ExecuteArgs> for PauseStack {
"PauseStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -1028,7 +1048,11 @@ impl Resolve<ExecuteArgs> for PauseStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
execute_compose::<PauseStack>(
&self.stack,
@@ -1048,7 +1072,7 @@ impl Resolve<ExecuteArgs> for UnpauseStack {
"UnpauseStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -1057,7 +1081,11 @@ impl Resolve<ExecuteArgs> for UnpauseStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
execute_compose::<UnpauseStack>(
&self.stack,
@@ -1077,7 +1105,7 @@ impl Resolve<ExecuteArgs> for StopStack {
"StopStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -1086,7 +1114,11 @@ impl Resolve<ExecuteArgs> for StopStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
execute_compose::<StopStack>(
&self.stack,
@@ -1118,14 +1150,14 @@ impl Resolve<ExecuteArgs> for BatchDestroyStack {
"BatchDestroyStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
pattern = self.pattern,
)
)]
async fn resolve(
self,
ExecuteArgs { user, id, .. }: &ExecuteArgs,
ExecuteArgs { user, task_id, .. }: &ExecuteArgs,
) -> mogh_error::Result<BatchExecutionResponse> {
super::batch_execute::<BatchDestroyStack>(&self.pattern, user)
.await
@@ -1138,7 +1170,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
"DestroyStack",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -1149,7 +1181,11 @@ impl Resolve<ExecuteArgs> for DestroyStack {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (stack, swarm_or_server) = setup_stack_execution(
&self.stack,
@@ -1182,7 +1218,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
action_state.update(|state| state.destroying = true)?;
let mut update = update.clone();
// Send update here for frontend to recheck action state
// Send update here for UI to recheck action state
update_update(update.clone()).await?;
match swarm_request(
@@ -1205,7 +1241,7 @@ impl Resolve<ExecuteArgs> for DestroyStack {
}
// Ensure cached stack state up to date by updating swarm cache
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
update.finalize();
update_update(update.clone()).await?;
@@ -1233,7 +1269,7 @@ impl Resolve<ExecuteArgs> for RunStackService {
"RunStackService",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
stack = self.stack,
@@ -1243,7 +1279,11 @@ impl Resolve<ExecuteArgs> for RunStackService {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let (mut stack, swarm_or_server) = setup_stack_execution(
&self.stack,
+64 -28
View File
@@ -12,7 +12,7 @@ use mogh_resolver::Resolve;
use crate::{
api::execute::ExecuteArgs,
helpers::{swarm::swarm_request, update::update_update},
monitor::update_cache_for_swarm,
monitor::refresh_swarm_cache,
permission::get_check_permissions,
};
@@ -21,7 +21,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
"RemoveSwarmNodes",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -31,7 +31,11 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -55,7 +59,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
{
Ok(log) => {
update.logs.push(log);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Remove Swarm Nodes",
@@ -77,7 +81,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
"RemoveSwarmStacks",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -87,7 +91,11 @@ impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -111,7 +119,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
{
Ok(log) => {
update.logs.push(log);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Remove Swarm Stacks",
@@ -133,7 +141,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmServices {
"RemoveSwarmServices",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -142,7 +150,11 @@ impl Resolve<ExecuteArgs> for RemoveSwarmServices {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -165,7 +177,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmServices {
{
Ok(log) => {
update.logs.push(log);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Remove Swarm Services",
@@ -187,7 +199,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmConfig {
"CreateSwarmConfig",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -196,7 +208,11 @@ impl Resolve<ExecuteArgs> for CreateSwarmConfig {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -222,7 +238,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmConfig {
{
Ok(log) => {
update.logs.push(log);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Create Swarm Config",
@@ -244,7 +260,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmConfig {
"RotateSwarmConfig",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -253,7 +269,11 @@ impl Resolve<ExecuteArgs> for RotateSwarmConfig {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -277,7 +297,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmConfig {
{
Ok(logs) => {
update.logs.extend(logs);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Rotate Swarm Config",
@@ -299,7 +319,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmConfigs {
"RemoveSwarmConfigs",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -308,7 +328,11 @@ impl Resolve<ExecuteArgs> for RemoveSwarmConfigs {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -331,7 +355,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmConfigs {
{
Ok(log) => {
update.logs.push(log);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Remove Swarm Configs",
@@ -353,7 +377,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmSecret {
"CreateSwarmSecret",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -362,7 +386,11 @@ impl Resolve<ExecuteArgs> for CreateSwarmSecret {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -389,7 +417,7 @@ impl Resolve<ExecuteArgs> for CreateSwarmSecret {
{
Ok(log) => {
update.logs.push(log);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Create Swarm Secret",
@@ -411,7 +439,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmSecret {
"RotateSwarmSecret",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -420,7 +448,11 @@ impl Resolve<ExecuteArgs> for RotateSwarmSecret {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -444,7 +476,7 @@ impl Resolve<ExecuteArgs> for RotateSwarmSecret {
{
Ok(logs) => {
update.logs.extend(logs);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Rotate Swarm Secret",
@@ -466,7 +498,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmSecrets {
"RemoveSwarmSecrets",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
swarm = self.swarm,
@@ -475,7 +507,11 @@ impl Resolve<ExecuteArgs> for RemoveSwarmSecrets {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> Result<Self::Response, Self::Error> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
@@ -498,7 +534,7 @@ impl Resolve<ExecuteArgs> for RemoveSwarmSecrets {
{
Ok(log) => {
update.logs.push(log);
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
Err(e) => update.push_error_log(
"Remove Swarm Secrets",
+79 -182
View File
@@ -21,6 +21,7 @@ use komodo_client::{
repo::Repo,
server::Server,
stack::Stack,
swarm::Swarm,
sync::ResourceSync,
update::{Log, Update},
user::sync_user,
@@ -53,7 +54,7 @@ impl Resolve<ExecuteArgs> for RunSync {
"RunSync",
skip_all,
fields(
id = id.to_string(),
task_id = task_id.to_string(),
operator = user.id,
update_id = update.id,
sync = self.sync,
@@ -63,7 +64,11 @@ impl Resolve<ExecuteArgs> for RunSync {
)]
async fn resolve(
self,
ExecuteArgs { user, update, id }: &ExecuteArgs,
ExecuteArgs {
user,
update,
task_id,
}: &ExecuteArgs,
) -> mogh_error::Result<Update> {
let RunSync {
sync,
@@ -134,56 +139,36 @@ impl Resolve<ExecuteArgs> for RunSync {
let Some(resource_type) = match_resource_type else {
return Some(name_or_id);
};
match ObjectId::from_str(&name_or_id) {
Ok(_) => match resource_type {
ResourceTargetVariant::Swarm => all_resources
.swarms
.get(&name_or_id)
.map(|s| s.name.clone()),
ResourceTargetVariant::Server => all_resources
.servers
.get(&name_or_id)
.map(|s| s.name.clone()),
ResourceTargetVariant::Stack => all_resources
.stacks
.get(&name_or_id)
.map(|s| s.name.clone()),
ResourceTargetVariant::Deployment => all_resources
.deployments
.get(&name_or_id)
.map(|d| d.name.clone()),
ResourceTargetVariant::Build => all_resources
.builds
.get(&name_or_id)
.map(|b| b.name.clone()),
ResourceTargetVariant::Repo => all_resources
.repos
.get(&name_or_id)
.map(|r| r.name.clone()),
ResourceTargetVariant::Procedure => all_resources
.procedures
.get(&name_or_id)
.map(|p| p.name.clone()),
ResourceTargetVariant::Action => all_resources
.actions
.get(&name_or_id)
.map(|p| p.name.clone()),
ResourceTargetVariant::ResourceSync => all_resources
.syncs
.get(&name_or_id)
.map(|s| s.name.clone()),
ResourceTargetVariant::Builder => all_resources
.builders
.get(&name_or_id)
.map(|b| b.name.clone()),
ResourceTargetVariant::Alerter => all_resources
.alerters
.get(&name_or_id)
.map(|a| a.name.clone()),
ResourceTargetVariant::System => None,
},
Err(_) => Some(name_or_id),
macro_rules! resolve_id_to_name {
($(($Variant:ident, $field:ident)),* $(,)?) => {
match ObjectId::from_str(&name_or_id) {
Ok(_) => match resource_type {
$(
ResourceTargetVariant::$Variant => all_resources
.$field
.get(&name_or_id)
.map(|r| r.name.clone()),
)*
ResourceTargetVariant::System => None,
},
Err(_) => Some(name_or_id),
}
};
}
// New resource types need to be added here manually.
resolve_id_to_name!(
(Server, servers),
(Swarm, swarms),
(Stack, stacks),
(Deployment, deployments),
(Build, builds),
(Repo, repos),
(Procedure, procedures),
(Action, actions),
(ResourceSync, syncs),
(Builder, builders),
(Alerter, alerters),
)
})
.collect::<Vec<_>>()
});
@@ -231,136 +216,39 @@ impl Resolve<ExecuteArgs> for RunSync {
let delete = sync.config.managed || sync.config.delete;
let server_deltas = if sync.config.include_resources {
get_updates_for_execution::<Server>(
resources.servers,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let stack_deltas = if sync.config.include_resources {
get_updates_for_execution::<Stack>(
resources.stacks,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let deployment_deltas = if sync.config.include_resources {
get_updates_for_execution::<Deployment>(
resources.deployments,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let build_deltas = if sync.config.include_resources {
get_updates_for_execution::<Build>(
resources.builds,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let repo_deltas = if sync.config.include_resources {
get_updates_for_execution::<Repo>(
resources.repos,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let procedure_deltas = if sync.config.include_resources {
get_updates_for_execution::<Procedure>(
resources.procedures,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let action_deltas = if sync.config.include_resources {
get_updates_for_execution::<Action>(
resources.actions,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let builder_deltas = if sync.config.include_resources {
get_updates_for_execution::<Builder>(
resources.builders,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let alerter_deltas = if sync.config.include_resources {
get_updates_for_execution::<Alerter>(
resources.alerters,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
let resource_sync_deltas = if sync.config.include_resources {
get_updates_for_execution::<entities::sync::ResourceSync>(
resources.resource_syncs,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
macro_rules! get_deltas {
($(($var:ident, $Type:ident, $field:ident)),* $(,)?) => {
$(
let $var = if sync.config.include_resources {
get_updates_for_execution::<$Type>(
resources.$field,
delete,
match_resource_type,
match_resources.as_deref(),
&id_to_tags,
&sync.config.match_tags,
)
.await?
} else {
Default::default()
};
)*
};
}
// New resource types need to be added here manually.
get_deltas!(
(server_deltas, Server, servers),
(swarm_deltas, Swarm, swarms),
(stack_deltas, Stack, stacks),
(deployment_deltas, Deployment, deployments),
(build_deltas, Build, builds),
(repo_deltas, Repo, repos),
(procedure_deltas, Procedure, procedures),
(action_deltas, Action, actions),
(builder_deltas, Builder, builders),
(alerter_deltas, Alerter, alerters),
(resource_sync_deltas, ResourceSync, resource_syncs),
);
let (
variables_to_create,
@@ -398,6 +286,7 @@ impl Resolve<ExecuteArgs> for RunSync {
if deploy_cache.is_empty()
&& resource_sync_deltas.no_changes()
&& server_deltas.no_changes()
&& swarm_deltas.no_changes()
&& deployment_deltas.no_changes()
&& stack_deltas.no_changes()
&& build_deltas.no_changes()
@@ -425,7 +314,11 @@ impl Resolve<ExecuteArgs> for RunSync {
return Ok(update);
}
// =================
// =====================================================
// The ordering these are executed does matter, since
// latter resources may depend on prior synced resources
// already being updated with the declared state.
// =====================================================
// No deps
maybe_extend(
@@ -464,6 +357,10 @@ impl Resolve<ExecuteArgs> for RunSync {
);
// Dependent on server
maybe_extend(
&mut update.logs,
Swarm::execute_sync_updates(swarm_deltas).await,
);
maybe_extend(
&mut update.logs,
Builder::execute_sync_updates(builder_deltas).await,
+9 -9
View File
@@ -79,7 +79,7 @@ pub async fn handle_build_webhook<B: super::ExtractBranch>(
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -121,7 +121,7 @@ impl RepoExecution for CloneRepo {
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -145,7 +145,7 @@ impl RepoExecution for PullRepo {
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -169,7 +169,7 @@ impl RepoExecution for BuildRepo {
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -272,7 +272,7 @@ impl StackExecution for DeployStack {
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -290,7 +290,7 @@ impl StackExecution for DeployStack {
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -403,7 +403,7 @@ impl SyncExecution for RunSync {
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -517,7 +517,7 @@ pub async fn handle_procedure_webhook<B: super::ExtractBranch>(
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
@@ -583,7 +583,7 @@ pub async fn handle_action_webhook<B: super::ExtractBranch>(
.resolve(&ExecuteArgs {
user,
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
.map_err(|e| e.error)?;
+23
View File
@@ -222,8 +222,31 @@ where
.status_code(StatusCode::BAD_REQUEST)?;
P::verify_secret(headers, body, R::custom_secret(&resource))
.status_code(StatusCode::UNAUTHORIZED)?;
info!(
resource_type = R::resource_type().to_string(),
resource_id = id,
source_ip = ip.to_string(),
"Successfully authenticated incoming webhook"
);
debug!(
resource_type = R::resource_type().to_string(),
resource_id = id,
source_ip = ip.to_string(),
"Webhook body: {body}"
);
mogh_error::Result::Ok(resource)
}
.with_failure_rate_limit_using_ip(&GENERAL_RATE_LIMITER, &ip)
.await
.inspect_err(|e| {
warn!(
resource_id = id,
source_ip = ip.to_string(),
"Incoming webhook failed | ERROR: {:#}",
e.error
);
})
}
+1 -1
View File
@@ -39,7 +39,7 @@ pub fn app() -> Router {
.nest("/client", ts_client::router())
.layer(memory_session_layer(config))
.fallback_service(serve_static_ui(
&config.frontend_path,
&config.ui_path,
config.ui_index_force_no_cache,
))
.layer(cors_layer(config))
+17 -7
View File
@@ -70,7 +70,7 @@ pub struct ReadArgs {
#[derive(
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
)]
#[strum_discriminants(name(ReadRequestVariant), derive(Display))]
#[strum_discriminants(name(ReadRequestMethod), derive(Display))]
#[args(ReadArgs)]
#[response(Response)]
#[error(mogh_error::Error)]
@@ -291,18 +291,28 @@ async fn handler(
Json(request): Json<ReadRequest>,
) -> mogh_error::Result<axum::response::Response> {
let req_id = Uuid::new_v4();
let variant: ReadRequestVariant = (&request).into();
let method: ReadRequestMethod = (&request).into();
debug!(
"READ REQUEST {req_id} | METHOD: {variant} | USER: {} ({})",
user.username, user.id
let user_id = user.id.clone();
let username = user.username.clone();
trace!(
req_id = req_id.to_string(),
method = method.to_string(),
user_id,
username,
"READ REQUEST",
);
let res = request.resolve(&ReadArgs { user }).await;
if let Err(e) = &res {
debug!(
"READ REQUEST {req_id} | METHOD: {variant} | ERROR: {:#}",
trace!(
req_id = req_id.to_string(),
method = method.to_string(),
user_id,
username,
"READ REQUEST | ERROR: {:#}",
e.error
);
}
+1 -1
View File
@@ -120,7 +120,7 @@ impl Resolve<ReadArgs> for GetResourceSyncsSummary {
for resource_sync in resource_syncs {
res.total += 1;
if !(resource_sync.info.pending_deploy.to_deploy == 0
if !(resource_sync.info.pending_deploys.is_empty()
&& resource_sync.info.resource_updates.is_empty()
&& resource_sync.info.variable_updates.is_empty()
&& resource_sync.info.user_group_updates.is_empty())
+100 -326
View File
@@ -38,122 +38,45 @@ async fn get_all_targets(
user: &User,
) -> anyhow::Result<Vec<ResourceTarget>> {
let mut targets = Vec::<ResourceTarget>::new();
let all_tags = if tags.is_empty() {
vec![]
} else {
get_all_tags(None).await?
};
targets.extend(
resource::list_full_for_user::<Alerter>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Alerter(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Builder>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Builder(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Server>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Server(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Stack>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Stack(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Deployment>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Deployment(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Build>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Build(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Repo>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Repo(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Procedure>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Procedure(resource.id)),
);
targets.extend(
resource::list_full_for_user::<Action>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::Action(resource.id)),
);
targets.extend(
resource::list_full_for_user::<ResourceSync>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
// These will already be filtered by [ExportResourcesToToml]
.map(|resource| ResourceTarget::ResourceSync(resource.id)),
macro_rules! extend_targets {
($($Type:ident),* $(,)?) => {
$(
targets.extend(
resource::list_full_for_user::<$Type>(
ResourceQuery::builder().tags(tags).build(),
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?
.into_iter()
.map(|resource| ResourceTarget::$Type(resource.id)),
);
)*
};
}
extend_targets!(
Alerter,
Builder,
Server,
Swarm,
Stack,
Deployment,
Build,
Repo,
Procedure,
Action,
ResourceSync,
);
Ok(targets)
}
@@ -187,6 +110,7 @@ impl Resolve<ReadArgs> for ExportAllResourcesToToml {
targets,
user_groups,
include_variables: self.include_variables,
existing: self.existing,
}
.resolve(args)
.await
@@ -202,131 +126,64 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
targets,
user_groups,
include_variables,
existing,
} = self;
let mut res = ResourcesToml::default();
let id_to_tags = get_id_to_tags(None).await?;
let ReadArgs { user } = args;
macro_rules! convert_target {
($id:expr, $Type:ident, $field:ident) => {{
let mut resource = get_check_permissions::<$Type>(
&$id,
user,
PermissionLevel::Read.into(),
)
.await?;
$Type::replace_ids(&mut resource);
let (deploy, after) = existing
.as_ref()
.and_then(|e| {
e.$field.iter().find(|r| r.name == resource.name)
})
.map(|r| (r.deploy, r.after.clone()))
.unwrap_or_default();
res.$field.push(convert_resource::<$Type>(
resource,
deploy,
after,
&id_to_tags,
));
}};
}
for target in targets {
match target {
ResourceTarget::Swarm(id) => {
let mut swarm = get_check_permissions::<Swarm>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Swarm::replace_ids(&mut swarm);
res.swarms.push(convert_resource::<Swarm>(
swarm,
false,
vec![],
&id_to_tags,
))
}
ResourceTarget::Server(id) => {
let mut server = get_check_permissions::<Server>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Server::replace_ids(&mut server);
res.servers.push(convert_resource::<Server>(
server,
false,
vec![],
&id_to_tags,
))
convert_target!(id, Server, servers)
}
ResourceTarget::Swarm(id) => {
convert_target!(id, Swarm, swarms)
}
ResourceTarget::Stack(id) => {
let mut stack = get_check_permissions::<Stack>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Stack::replace_ids(&mut stack);
res.stacks.push(convert_resource::<Stack>(
stack,
false,
vec![],
&id_to_tags,
))
convert_target!(id, Stack, stacks)
}
ResourceTarget::Deployment(id) => {
let mut deployment = get_check_permissions::<Deployment>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Deployment::replace_ids(&mut deployment);
res.deployments.push(convert_resource::<Deployment>(
deployment,
false,
vec![],
&id_to_tags,
))
convert_target!(id, Deployment, deployments)
}
ResourceTarget::Build(id) => {
let mut build = get_check_permissions::<Build>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Build::replace_ids(&mut build);
res.builds.push(convert_resource::<Build>(
build,
false,
vec![],
&id_to_tags,
))
}
ResourceTarget::Repo(id) => {
let mut repo = get_check_permissions::<Repo>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Repo::replace_ids(&mut repo);
res.repos.push(convert_resource::<Repo>(
repo,
false,
vec![],
&id_to_tags,
))
convert_target!(id, Build, builds)
}
ResourceTarget::Repo(id) => convert_target!(id, Repo, repos),
ResourceTarget::Procedure(id) => {
let mut procedure = get_check_permissions::<Procedure>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Procedure::replace_ids(&mut procedure);
res.procedures.push(convert_resource::<Procedure>(
procedure,
false,
vec![],
&id_to_tags,
));
convert_target!(id, Procedure, procedures)
}
ResourceTarget::Action(id) => {
let mut action = get_check_permissions::<Action>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Action::replace_ids(&mut action);
res.actions.push(convert_resource::<Action>(
action,
false,
vec![],
&id_to_tags,
));
convert_target!(id, Action, actions)
}
ResourceTarget::Builder(id) => {
convert_target!(id, Builder, builders)
}
ResourceTarget::Alerter(id) => {
convert_target!(id, Alerter, alerters)
}
ResourceTarget::ResourceSync(id) => {
let mut sync = get_check_permissions::<ResourceSync>(
@@ -349,36 +206,6 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
))
}
}
ResourceTarget::Builder(id) => {
let mut builder = get_check_permissions::<Builder>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Builder::replace_ids(&mut builder);
res.builders.push(convert_resource::<Builder>(
builder,
false,
vec![],
&id_to_tags,
))
}
ResourceTarget::Alerter(id) => {
let mut alerter = get_check_permissions::<Alerter>(
&id,
user,
PermissionLevel::Read.into(),
)
.await?;
Alerter::replace_ids(&mut alerter);
res.alerters.push(convert_resource::<Alerter>(
alerter,
false,
vec![],
&id_to_tags,
))
}
ResourceTarget::System(_) => continue,
};
}
@@ -434,85 +261,32 @@ fn serialize_resources_toml(
) -> anyhow::Result<String> {
let mut toml = String::new();
for server in resources.servers {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[server]]\n");
Server::push_to_toml_string(server, &mut toml)?;
}
for stack in resources.stacks {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[stack]]\n");
Stack::push_to_toml_string(stack, &mut toml)?;
}
for deployment in resources.deployments {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[deployment]]\n");
Deployment::push_to_toml_string(deployment, &mut toml)?;
}
for build in resources.builds {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[build]]\n");
Build::push_to_toml_string(build, &mut toml)?;
}
for repo in resources.repos {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[repo]]\n");
Repo::push_to_toml_string(repo, &mut toml)?;
}
for procedure in resources.procedures {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[procedure]]\n");
Procedure::push_to_toml_string(procedure, &mut toml)?;
}
for action in resources.actions {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[action]]\n");
Action::push_to_toml_string(action, &mut toml)?;
}
for alerter in resources.alerters {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[alerter]]\n");
Alerter::push_to_toml_string(alerter, &mut toml)?;
}
for builder in resources.builders {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[builder]]\n");
Builder::push_to_toml_string(builder, &mut toml)?;
}
for resource_sync in resources.resource_syncs {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str("[[resource_sync]]\n");
ResourceSync::push_to_toml_string(resource_sync, &mut toml)?;
macro_rules! serialize_resources {
($(($Type:ident, $field:ident, $header:literal)),* $(,)?) => {
$(
for resource in resources.$field {
if !toml.is_empty() {
toml.push_str("\n\n##\n\n");
}
toml.push_str(concat!("[[",$header,"]]\n"));
$Type::push_to_toml_string(resource, &mut toml)?;
}
)*
};
}
serialize_resources!(
(Server, servers, "server"),
(Swarm, swarms, "swarm"),
(Stack, stacks, "stack"),
(Deployment, deployments, "deployment"),
(Build, builds, "build"),
(Repo, repos, "repo"),
(Procedure, procedures, "procedure"),
(Action, actions, "action"),
(Alerter, alerters, "alerter"),
(Builder, builders, "builder"),
(ResourceSync, resource_syncs, "resource_sync"),
);
for variable in &resources.variables {
if !toml.is_empty() {
+12 -1
View File
@@ -71,9 +71,20 @@ impl Resolve<ReadArgs> for ListUsers {
anyhow!("this route is only accessable by admins").into(),
);
}
let filter = match self.service_users {
komodo_client::api::read::ServiceUserQueryBehavior::Include => {
None
}
komodo_client::api::read::ServiceUserQueryBehavior::Exclude => {
Some(doc! { "config.type": { "$ne": "Service" } })
}
komodo_client::api::read::ServiceUserQueryBehavior::Only => {
Some(doc! { "config.type": "Service" })
}
};
let mut users = find_collect(
&db_client().users,
None,
filter,
FindOptions::builder().sort(doc! { "username": 1 }).build(),
)
.await
+38 -28
View File
@@ -27,12 +27,12 @@ use periphery_client::api::{self, container::InspectContainer};
use crate::{
alert::send_alerts,
api::execute::{self, ExecuteRequest},
api::execute::{self, ExecuteRequest, ExecutionResult},
helpers::{
periphery_client,
query::{get_deployment_state, get_swarm_or_server},
registry_token,
update::{add_update, make_update},
update::{add_update, make_update, poll_update_until_complete},
},
permission::get_check_permissions,
resource::{
@@ -436,7 +436,7 @@ pub async fn check_deployment_for_update_inner(
update_available: false,
});
}
let domain = extract_registry_domain(&image)?;
let domain = extract_registry_domain(image)?;
let account =
optional_string(&deployment.config.image_registry_account);
let token = if let Some(account) = &account {
@@ -458,7 +458,7 @@ pub async fn check_deployment_for_update_inner(
};
let latest_digest = image_digest_cache()
.get(&swarm_or_server, &image, account, token)
.get(swarm_or_server, image, account, token)
.await?;
resource::update_info::<Deployment>(
@@ -520,32 +520,42 @@ pub async fn check_deployment_for_update_inner(
)
.await
{
Ok(_) => {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Deployment(id.clone()),
data: AlertData::DeploymentAutoUpdated {
id,
name,
swarm_id,
swarm_name,
server_id,
server_name,
image,
},
Ok(res) => {
let ExecutionResult::Single(update) = res else {
unreachable!()
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!(
"Failed to record DeploymentAutoUpdated to db | {e:#}"
);
let Ok(update) =
poll_update_until_complete(&update.id).await
else {
return;
};
if update.success {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Deployment(id.clone()),
data: AlertData::DeploymentAutoUpdated {
id,
name,
swarm_id,
swarm_name,
server_id,
server_name,
image,
},
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!(
"Failed to record DeploymentAutoUpdated to db | {e:#}"
);
}
send_alerts(&[alert]).await;
}
send_alerts(&[alert]).await;
}
Err(e) => {
warn!("Failed to auto update Deployment {name} | {e:#}",)
+24 -8
View File
@@ -54,7 +54,7 @@ pub struct WriteArgs {
#[derive(
Serialize, Deserialize, Debug, Clone, Resolve, EnumDiscriminants,
)]
#[strum_discriminants(name(WriteRequestVariant), derive(Display))]
#[strum_discriminants(name(WriteRequestMethod), derive(Display))]
#[args(WriteArgs)]
#[response(Response)]
#[error(mogh_error::Error)]
@@ -255,19 +255,35 @@ async fn task(
request: WriteRequest,
user: User,
) -> mogh_error::Result<axum::response::Response> {
let req_id = Uuid::new_v4();
let variant: WriteRequestVariant = (&request).into();
let task_id = Uuid::new_v4();
let method: WriteRequestMethod = (&request).into();
info!(
"WRITE REQUEST {req_id} | METHOD: {variant} | USER: {} ({})",
user.username, user.id
);
let user_id = user.id.clone();
let username = user.username.clone();
if !matches!(
request,
WriteRequest::SetLastSeenUpdate(_)
| WriteRequest::PushRecentlyViewed(_)
) {
info!(
task_id = task_id.to_string(),
method = method.to_string(),
user_id,
username,
"WRITE REQUEST",
);
}
let res = request.resolve(&WriteArgs { user }).await;
if let Err(e) = &res {
warn!(
"WRITE REQUEST {req_id} | METHOD: {variant} | ERROR: {:#}",
task_id = task_id.to_string(),
method = method.to_string(),
user_id,
username,
"WRITE REQUEST | ERROR: {:#}",
e.error
);
}
+38 -5
View File
@@ -15,7 +15,10 @@ use mogh_pki::EncodedKeyPair;
use mogh_resolver::Resolve;
use reqwest::StatusCode;
use crate::{api::write::WriteArgs, state::db_client};
use crate::{
api::write::WriteArgs, helpers::query::get_all_tags,
state::db_client,
};
//
@@ -53,6 +56,21 @@ impl Resolve<WriteArgs> for CreateOnboardingKey {
)?
.public
.into_inner();
let tags = if self.tags.is_empty() {
self.tags
} else {
// fix_tags by ensuring existence, and force replace with tag name.
let all_tags = get_all_tags(None).await?;
self
.tags
.into_iter()
.filter_map(|tag| {
let tag =
all_tags.iter().find(|t| t.id == tag || t.name == tag)?;
Some(tag.name.clone())
})
.collect()
};
let onboarding_key = OnboardingKey {
public_key,
name: self.name,
@@ -60,8 +78,8 @@ impl Resolve<WriteArgs> for CreateOnboardingKey {
onboarded: Default::default(),
created_at: komodo_timestamp(),
expires: self.expires,
tags: self.tags,
fix_existing_servers: self.fix_existing_servers,
tags,
privileged: self.privileged,
copy_server: self.copy_server,
create_builder: self.create_builder,
};
@@ -139,11 +157,26 @@ impl Resolve<WriteArgs> for UpdateOnboardingKey {
}
if let Some(tags) = self.tags {
let tags = if tags.is_empty() {
tags
} else {
// fix_tags by ensuring existence, and force replace with tag name.
let all_tags = get_all_tags(None).await?;
tags
.into_iter()
.filter_map(|tag| {
let tag = all_tags
.iter()
.find(|t| t.id == tag || t.name == tag)?;
Some(tag.name.clone())
})
.collect()
};
update.insert("tags", tags);
}
if let Some(fix_existing_servers) = self.fix_existing_servers {
update.insert("fix_existing_servers", fix_existing_servers);
if let Some(privileged) = self.privileged {
update.insert("privileged", privileged);
}
if let Some(copy_server) = self.copy_server {
+64 -35
View File
@@ -33,12 +33,12 @@ use periphery_client::api::compose::{
use crate::{
alert::send_alerts,
api::execute::{self, ExecuteRequest},
api::execute::{self, ExecuteRequest, ExecutionResult},
config::core_config,
helpers::{
query::get_swarm_or_server,
stack_git_token, swarm_or_server_request,
update::{add_update, make_update},
update::{add_update, make_update, poll_update_until_complete},
},
permission::get_check_permissions,
resource::{self, list_full_for_user_using_pattern},
@@ -563,7 +563,16 @@ impl Resolve<WriteArgs> for RefreshStackCache {
(services, Some(contents), Some(errors), None, None)
} else {
(vec![], None, None, None, None)
// This path is reached if the swarm / server is not available.
// It carries over the last successful poll.
(
stack.info.latest_services,
stack.info.remote_contents,
stack.info.remote_errors,
// Files on host can set hash / message back to None.
None,
None,
)
}
} else if !repo_empty {
// ================
@@ -750,17 +759,28 @@ pub async fn check_stack_for_update_inner(
let cache = image_digest_cache();
for service in &mut stack.info.latest_services {
if service.image.is_empty() ||
// Prefer the image coming from deployed services
// so it will be after any interpolation.
let image = stack
.info
.deployed_services
.as_ref()
.and_then(|services| {
services.iter().find_map(|deployed| {
(deployed.service_name == service.service_name)
.then_some(&deployed.image)
})
})
.unwrap_or(&service.image);
if image.is_empty() ||
// Images with a hardcoded digest can't have update.
service.image.contains('@')
image.contains('@')
{
service.image_digest = None;
continue;
}
match cache
.get(&swarm_or_server, &service.image, None, None)
.await
{
match cache.get(swarm_or_server, image, None, None).await {
Ok(digest) => service.image_digest = Some(digest),
Err(e) => {
warn!(
@@ -850,7 +870,7 @@ pub async fn check_stack_for_update_inner(
};
service_with_update.update_available =
latest_digest.update_available(&current_digests);
latest_digest.update_available(current_digests);
if service_with_update.update_available
&& (skip_auto_update || !stack.config.auto_update)
@@ -946,33 +966,42 @@ pub async fn check_stack_for_update_inner(
)
.await
{
Ok(_) => {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Stack(stack.id.clone()),
data: AlertData::StackAutoUpdated {
id: stack.id.clone(),
name: stack.name.clone(),
swarm_id,
swarm_name,
server_id,
server_name,
images: services_with_update
.iter()
.map(|service| service.image.clone())
.collect(),
},
Ok(res) => {
let ExecutionResult::Single(update) = res else {
unreachable!()
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!("Failed to record StackAutoUpdated to db | {e:#}");
let Ok(update) = poll_update_until_complete(&update.id).await
else {
return;
};
if update.success {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Stack(stack.id.clone()),
data: AlertData::StackAutoUpdated {
id: stack.id.clone(),
name: stack.name.clone(),
swarm_id,
swarm_name,
server_id,
server_name,
images: services_with_update
.iter()
.map(|service| service.image.clone())
.collect(),
},
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!("Failed to record StackAutoUpdated to db | {e:#}");
}
send_alerts(&[alert]).await;
}
send_alerts(&[alert]).await;
}
Err(e) => {
warn!("Failed to auto update Stack {} | {e:#}", stack.name)
+46 -105
View File
@@ -26,7 +26,8 @@ use komodo_client::{
repo::Repo,
server::Server,
stack::Stack,
sync::{ResourceSync, ResourceSyncInfo, SyncDeployUpdate},
swarm::Swarm,
sync::{ResourceSync, ResourceSyncInfo},
to_path_compatible_name,
update::{Log, Update},
user::sync_user,
@@ -515,11 +516,20 @@ impl Resolve<WriteArgs> for CommitSync {
None
};
// Get the latest existing resources to preserve any meta values
let RemoteResources { resources, .. } =
crate::sync::remote::get_remote_resources(&sync, repo.as_ref())
.await
.context("failed to get remote resources")?;
let res = ExportAllResourcesToToml {
include_resources: sync.config.include_resources,
tags: sync.config.match_tags.clone(),
include_variables: sync.config.include_variables,
include_user_groups: sync.config.include_user_groups,
existing: resources
.inspect_err(|e| warn!("Existing resource TOML is unavailable, resource meta will not be preserved | ERROR: {e:#}"))
.ok(),
}
.resolve(&ReadArgs {
user: sync_user().to_owned(),
@@ -775,110 +785,40 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
let mut diffs = Vec::new();
push_updates_for_view::<Server>(
resources.servers,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Stack>(
resources.stacks,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Deployment>(
resources.deployments,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Build>(
resources.builds,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Repo>(
resources.repos,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Procedure>(
resources.procedures,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Action>(
resources.actions,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Builder>(
resources.builders,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<Alerter>(
resources.alerters,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
push_updates_for_view::<ResourceSync>(
resources.resource_syncs,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
macro_rules! push_updates {
($(($Type:ident, $field:ident)),* $(,)?) => {
$(
push_updates_for_view::<$Type>(
resources.$field,
delete,
None,
None,
&id_to_tags,
&sync.config.match_tags,
&mut diffs,
)
.await?;
)*
};
}
// New resource types need to be added here manually.
push_updates!(
(Server, servers),
(Swarm, swarms),
(Stack, stacks),
(Deployment, deployments),
(Build, builds),
(Repo, repos),
(Procedure, procedures),
(Action, actions),
(Builder, builders),
(Alerter, alerters),
(ResourceSync, resource_syncs),
);
(diffs, deploy_updates)
} else {
(Vec::new(), SyncDeployUpdate::default())
Default::default()
};
let variable_updates = if sync.config.include_variables {
@@ -912,7 +852,7 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
let (
resource_updates,
deploy_updates,
(pending_deploys, pending_deploy_error),
variable_updates,
user_group_updates,
pending_error,
@@ -928,7 +868,7 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
};
let has_updates = !resource_updates.is_empty()
|| !deploy_updates.to_deploy == 0
|| !pending_deploys.is_empty()
|| !variable_updates.is_empty()
|| !user_group_updates.is_empty();
@@ -940,7 +880,8 @@ impl Resolve<WriteArgs> for RefreshResourceSyncPending {
remote_errors: sync.info.remote_errors,
pending_hash: sync.info.pending_hash,
pending_message: sync.info.pending_message,
pending_deploy: deploy_updates,
pending_deploys,
pending_deploy_error,
resource_updates,
variable_updates,
user_group_updates,
+29 -15
View File
@@ -3,8 +3,12 @@ use futures_util::{StreamExt as _, stream::FuturesUnordered};
use komodo_client::{
api::write::*,
entities::{
NoData, deployment::Deployment, permission::PermissionLevel,
server::Server, stack::Stack, terminal::TerminalTarget,
NoData,
deployment::Deployment,
permission::PermissionLevel,
server::Server,
stack::Stack,
terminal::{Terminal, TerminalTarget},
user::User,
},
};
@@ -47,30 +51,35 @@ impl Resolve<WriteArgs> for CreateTerminal {
async fn resolve(
self,
WriteArgs { user }: &WriteArgs,
) -> mogh_error::Result<NoData> {
) -> mogh_error::Result<Terminal> {
match self.target.clone() {
TerminalTarget::Server { server } => {
let server = server
.context("Must provide 'target.params.server'")
.status_code(StatusCode::BAD_REQUEST)?;
create_server_terminal(self, server, user).await?;
create_server_terminal(self, server, user)
.await
.map_err(Into::into)
}
TerminalTarget::Container { server, container } => {
create_container_terminal(self, server, container, user)
.await?;
.await
.map_err(Into::into)
}
TerminalTarget::Stack { stack, service } => {
let service = service
.context("Must provide 'target.params.service'")
.status_code(StatusCode::BAD_REQUEST)?;
create_stack_service_terminal(self, stack, service, user)
.await?;
.await
.map_err(Into::into)
}
TerminalTarget::Deployment { deployment } => {
create_deployment_terminal(self, deployment, user).await?;
create_deployment_terminal(self, deployment, user)
.await
.map_err(Into::into)
}
};
Ok(NoData {})
}
}
}
@@ -84,7 +93,7 @@ async fn create_server_terminal(
}: CreateTerminal,
server: String,
user: &User,
) -> anyhow::Result<()> {
) -> anyhow::Result<Terminal> {
let server = get_check_permissions::<Server>(
&server,
user,
@@ -94,7 +103,7 @@ async fn create_server_terminal(
let periphery = periphery_client(&server).await?;
periphery
let mut terminal = periphery
.request(api::terminal::CreateServerTerminal {
name,
command,
@@ -103,7 +112,12 @@ async fn create_server_terminal(
.await
.context("Failed to create Server Terminal on Periphery")?;
Ok(())
// Fix server terminal target with server id
terminal.target = TerminalTarget::Server {
server: Some(server.id),
};
Ok(terminal)
}
async fn create_container_terminal(
@@ -111,7 +125,7 @@ async fn create_container_terminal(
server: String,
container: String,
user: &User,
) -> anyhow::Result<()> {
) -> anyhow::Result<Terminal> {
let server = get_check_permissions::<Server>(
&server,
user,
@@ -127,7 +141,7 @@ async fn create_stack_service_terminal(
stack: String,
service: String,
user: &User,
) -> anyhow::Result<()> {
) -> anyhow::Result<Terminal> {
let (_, periphery, container) =
get_stack_service_periphery_container(&stack, &service, user)
.await?;
@@ -138,7 +152,7 @@ async fn create_deployment_terminal(
req: CreateTerminal,
deployment: String,
user: &User,
) -> anyhow::Result<()> {
) -> anyhow::Result<Terminal> {
let (_, periphery, container) =
get_deployment_periphery_container(&deployment, user).await?;
create_container_terminal_inner(req, &periphery, container).await
+15
View File
@@ -36,6 +36,15 @@ use super::WriteArgs;
const RECENTLY_VIEWED_MAX: usize = 10;
impl Resolve<WriteArgs> for PushRecentlyViewed {
#[instrument(
"PushRecentlyViewed",
level = "debug",
skip_all,
fields(
user_id = user.id,
resource = format!("{:?}", self.resource)
)
)]
async fn resolve(
self,
WriteArgs { user, .. }: &WriteArgs,
@@ -79,6 +88,12 @@ impl Resolve<WriteArgs> for PushRecentlyViewed {
//
impl Resolve<WriteArgs> for SetLastSeenUpdate {
#[instrument(
"SetLastSeenUpdate",
level = "debug",
skip_all,
fields(user_id = user.id)
)]
async fn resolve(
self,
WriteArgs { user, .. }: &WriteArgs,
+18 -5
View File
@@ -5,6 +5,7 @@ use axum::{
response::IntoResponse,
};
use bytes::Bytes;
use colored::Colorize;
use futures_util::{SinkExt, StreamExt as _};
use komodo_client::{
api::terminal::ConnectTerminalQuery, entities::user::User,
@@ -128,14 +129,26 @@ async fn forward_ws_channel(
};
}
Ok(Err(e)) => {
let _ = client_send
.send(ws::Message::text(format!("{e:#}")))
.await;
let message = format!("{}: {e:#}", "ERROR".red().bold());
if message.contains("pty exited") {
let _ = client_send
.send(ws::Message::text(format!(
"\n{} {}",
"pty".bold(),
"exited".red().bold()
)))
.await;
} else {
let _ = client_send
.send(ws::Message::text(message.replace('\n', "\r\n")))
.await;
}
break;
}
Err(_) => {
let _ =
client_send.send(ws::Message::text("STREAM EOF")).await;
let _ = client_send
.send(ws::Message::text("\r\n\nSTREAM EOF"))
.await;
break;
}
}
+2 -4
View File
@@ -143,14 +143,12 @@ impl AuthImpl for KomodoAuthImpl {
}
fn host(&self) -> &str {
static AUTH_HOST: LazyLock<String> =
LazyLock::new(|| format!("{}/auth", core_config().host));
&AUTH_HOST
&core_config().host
}
fn post_link_redirect(&self) -> &str {
static POST_LINK_REDIRECT: LazyLock<String> =
LazyLock::new(|| format!("{}/settings", core_config().host));
LazyLock::new(|| format!("{}/profile", core_config().host));
&POST_LINK_REDIRECT
}
+4 -3
View File
@@ -309,6 +309,9 @@ pub fn core_config() -> &'static CoreConfig {
cors_allow_credentials: env
.komodo_cors_allow_credentials
.unwrap_or(config.cors_allow_credentials),
session_allow_cross_site: env
.komodo_session_allow_cross_site
.unwrap_or(config.session_allow_cross_site),
resource_poll_interval: env
.komodo_resource_poll_interval
.unwrap_or(config.resource_poll_interval),
@@ -400,9 +403,7 @@ pub fn core_config() -> &'static CoreConfig {
ssl_cert_file: env
.komodo_ssl_cert_file
.unwrap_or(config.ssl_cert_file),
frontend_path: env
.komodo_frontend_path
.unwrap_or(config.frontend_path),
ui_path: env.komodo_ui_path.unwrap_or(config.ui_path),
ui_index_force_no_cache: env
.komodo_ui_index_force_no_cache
.unwrap_or(config.ui_index_force_no_cache),
+23
View File
@@ -1,6 +1,7 @@
use std::time::Duration;
use anyhow::{Context, anyhow};
use komodo_client::entities::server::Server;
use periphery_client::{
CONNECTION_RETRY_SECONDS, transport::LoginMessage,
};
@@ -18,6 +19,7 @@ use transport::{
use crate::{
config::{core_config, core_connection_query},
monitor::refresh_server_cache,
periphery::PeripheryClient,
state::periphery_connections,
};
@@ -46,6 +48,7 @@ impl PeripheryConnectionArgs<'_> {
let responses = connection.responses.clone();
let _id = id.clone();
tokio::spawn(async move {
loop {
let ws = tokio::select! {
@@ -70,6 +73,14 @@ impl PeripheryConnectionArgs<'_> {
}
};
debug!(
host = identifiers.host(),
query = core_connection_query(),
sec_websocket_accept = accept.to_str().unwrap_or_default(),
resource_id = connection.args.id,
"[PERIPHERY AUTH] Zero trust identifiers"
);
let identifiers = identifiers.build(
accept.as_bytes(),
core_connection_query().as_bytes(),
@@ -86,6 +97,18 @@ impl PeripheryConnectionArgs<'_> {
continue;
};
// Waits until after connection is handled then
// force refreshes the server cache.
let id = _id.clone();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(100)).await;
let Ok(server) = crate::resource::get::<Server>(&id).await
else {
return;
};
refresh_server_cache(&server, true).await;
});
connection.handle_socket(socket, &mut receiver).await
}
});
+22 -8
View File
@@ -1,4 +1,4 @@
use std::str::FromStr;
use std::{str::FromStr, time::Duration};
use anyhow::{Context, anyhow};
use axum::{
@@ -42,6 +42,7 @@ use crate::{
api::write::WriteArgs,
config::core_keys,
helpers::query::id_or_name_filter,
monitor::refresh_server_cache,
resource::KomodoResource,
state::{db_client, periphery_connections},
};
@@ -129,6 +130,14 @@ async fn existing_server_handler(
return;
};
debug!(
host = identifiers.host.to_str().unwrap_or_default(),
query,
sec_websocket_accept = identifiers.accept,
resource_id = &server.id,
"[PERIPHERY AUTH] Zero trust identifiers"
);
let span = info_span!(
"PeripheryLogin",
server_id = server.id,
@@ -155,7 +164,7 @@ async fn existing_server_handler(
socket: &mut socket,
identifiers: identifiers.build(query.as_bytes()),
private_key: core_keys().load().private.as_str(),
public_key_validator: OnboardingKeyValidator { fix_existing_servers_required: true },
public_key_validator: OnboardingKeyValidator { privileged_required: true },
should_close: true
})
.await
@@ -193,6 +202,13 @@ async fn existing_server_handler(
return;
}
// Waits until after connection is handled then
// force refreshes the server cache.
tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(100)).await;
refresh_server_cache(&server, true).await;
});
connection.handle_socket(socket, &mut receiver).await
}))
}
@@ -219,7 +235,7 @@ async fn fix_existing_server_handler(
socket: &mut socket,
identifiers: identifiers.build(query.as_bytes()),
private_key: core_keys().load().private.as_str(),
public_key_validator: OnboardingKeyValidator { fix_existing_servers_required: true },
public_key_validator: OnboardingKeyValidator { privileged_required: true },
should_close: true,
})
.await
@@ -333,7 +349,7 @@ async fn onboard_new_server_handler(
socket: &mut socket,
identifiers: identifiers.build(query.as_bytes()),
private_key: core_keys().load().private.as_str(),
public_key_validator: OnboardingKeyValidator { fix_existing_servers_required: false },
public_key_validator: OnboardingKeyValidator { privileged_required: false },
should_close: true
})
.await
@@ -497,7 +513,7 @@ async fn create_server_maybe_builder(
}
struct OnboardingKeyValidator {
fix_existing_servers_required: bool,
privileged_required: bool,
}
impl PublicKeyValidator for OnboardingKeyValidator {
@@ -519,9 +535,7 @@ impl PublicKeyValidator for OnboardingKeyValidator {
{
return Err(anyhow!("Onboarding key is invalid"));
}
if self.fix_existing_servers_required
&& !onboarding_key.fix_existing_servers
{
if self.privileged_required && !onboarding_key.privileged {
return Err(anyhow!(
"Onboarding key not able to fix existing servers"
));
+24 -24
View File
@@ -39,34 +39,34 @@ impl ImageDigestCache {
account: Option<String>,
token: Option<String>,
) -> anyhow::Result<ImageDigest> {
if let Some((digest, valid_until)) = self.0.get(&image).await
if let Some((digest, valid_until)) = self.0.get(image).await
// Ensure the query time was within last 10 mins to use cache.
&& valid_until > komodo_timestamp()
{
return Ok(digest);
} else {
let digest = swarm_or_server_request(
&swarm_or_server,
GetLatestImageDigest {
name: image.clone(),
account,
token,
},
)
.await?
.digest;
let digest = ImageDigest::new(image, &digest);
self
.0
.insert(
image,
(digest.clone(), komodo_timestamp() + 10 * 60 * 1_000),
)
.await;
Ok(digest)
}
let digest = swarm_or_server_request(
swarm_or_server,
GetLatestImageDigest {
name: image.clone(),
account,
token,
},
)
.await?
.digest;
let digest = ImageDigest::new(image, &digest);
self
.0
.insert(
image,
(digest.clone(), komodo_timestamp() + 10 * 60 * 1_000),
)
.await;
Ok(digest)
}
}
+321 -65
View File
@@ -228,7 +228,7 @@ async fn execute_execution(
parent_name: &str,
) -> anyhow::Result<()> {
let user = procedure_user().to_owned();
let id = Uuid::new_v4();
let task_id = Uuid::new_v4();
let update = match execution {
Execution::None(_) => return Ok(()),
Execution::RunProcedure(req) => {
@@ -243,7 +243,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RunProcedure"),
@@ -266,7 +270,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RunAction"),
@@ -289,7 +297,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RunBuild"),
@@ -312,7 +324,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at CancelBuild"),
@@ -329,7 +345,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at Deploy"),
@@ -352,7 +372,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PullDeployment"),
@@ -369,7 +393,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StartDeployment"),
@@ -386,7 +414,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RestartDeployment"),
@@ -403,7 +435,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PauseDeployment"),
@@ -420,7 +456,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at UnpauseDeployment"),
@@ -437,7 +477,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StopDeployment"),
@@ -454,7 +498,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at DestroyDeployment"),
@@ -477,7 +525,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at CloneRepo"),
@@ -500,7 +552,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PullRepo"),
@@ -523,7 +579,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at BuildRepo"),
@@ -546,7 +606,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at CancelRepoBuild"),
@@ -563,7 +627,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StartContainer"),
@@ -580,7 +648,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RestartContainer"),
@@ -597,7 +669,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PauseContainer"),
@@ -614,7 +690,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at UnpauseContainer"),
@@ -631,7 +711,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StopContainer"),
@@ -648,7 +732,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RemoveContainer"),
@@ -665,7 +753,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StartAllContainers"),
@@ -682,7 +774,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RestartAllContainers"),
@@ -699,7 +795,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PauseAllContainers"),
@@ -716,7 +816,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at UnpauseAllContainers"),
@@ -733,7 +837,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StopAllContainers"),
@@ -750,7 +858,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PruneContainers"),
@@ -767,7 +879,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at DeleteNetwork"),
@@ -784,7 +900,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PruneNetworks"),
@@ -801,7 +921,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at DeleteImage"),
@@ -818,7 +942,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PruneImages"),
@@ -835,7 +963,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at DeleteVolume"),
@@ -852,7 +984,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PruneVolumes"),
@@ -869,7 +1005,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PruneDockerBuilders"),
@@ -886,7 +1026,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PruneBuildx"),
@@ -903,7 +1047,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PruneSystem"),
@@ -920,7 +1068,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RunSync"),
@@ -943,7 +1095,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at DeployStack"),
@@ -966,7 +1122,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at DeployStackIfChanged"),
@@ -989,7 +1149,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PullStack"),
@@ -1012,7 +1176,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StartStack"),
@@ -1029,7 +1197,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RestartStack"),
@@ -1046,7 +1218,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at PauseStack"),
@@ -1063,7 +1239,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at UnpauseStack"),
@@ -1080,7 +1260,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at StopStack"),
@@ -1097,7 +1281,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at DestroyStack"),
@@ -1114,7 +1302,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RunStackService"),
@@ -1137,7 +1329,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at TestAlerter"),
@@ -1154,7 +1350,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at SendAlert"),
@@ -1171,7 +1371,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RemoveSwarmNodes"),
@@ -1188,7 +1392,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RemoveSwarmStacks"),
@@ -1205,7 +1413,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RemoveSwarmServices"),
@@ -1222,7 +1434,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at CreateSwarmConfig"),
@@ -1239,7 +1455,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RotateSwarmConfig"),
@@ -1256,7 +1476,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RemoveSwarmConfigs"),
@@ -1273,7 +1497,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at CreateSwarmSecret"),
@@ -1290,7 +1518,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RotateSwarmSecret"),
@@ -1307,7 +1539,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RemoveSwarmSecrets"),
@@ -1324,7 +1560,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at ClearRepoCache"),
@@ -1341,7 +1581,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at BackupCoreDatabase"),
@@ -1358,7 +1602,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at GlobalAutoUpdate"),
@@ -1375,7 +1623,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RotateAllServerKeys"),
@@ -1392,7 +1644,11 @@ async fn execute_execution(
let update_id = update.id.clone();
handle_resolve_result(
req
.resolve(&ExecuteArgs { user, update, id })
.resolve(&ExecuteArgs {
user,
update,
task_id,
})
.await
.map_err(|e| e.error)
.context("Failed at RotateCoreKeys"),
+9 -10
View File
@@ -6,7 +6,7 @@ use komodo_client::{
permission::PermissionLevel,
server::Server,
stack::Stack,
terminal::{ContainerTerminalMode, TerminalTarget},
terminal::{ContainerTerminalMode, Terminal, TerminalTarget},
user::User,
},
};
@@ -84,7 +84,7 @@ async fn setup_server_target_for_user(
if let Some(init) = init {
periphery
.request(api::terminal::CreateServerTerminal {
name: terminal.clone(),
name: Some(terminal.clone()),
command: init.command,
recreate: init.recreate,
})
@@ -131,7 +131,7 @@ async fn setup_container_target_for_user(
if let Some(init) = init {
create_container_terminal_inner(
CreateTerminal {
name: terminal.clone(),
name: Some(terminal.clone()),
target: target.clone(),
command: init.command,
mode: init.mode,
@@ -166,7 +166,7 @@ async fn setup_stack_service_target_for_user(
if let Some(init) = init {
create_container_terminal_inner(
CreateTerminal {
name: terminal.clone(),
name: Some(terminal.clone()),
target: target.clone(),
command: init.command,
mode: init.mode,
@@ -199,7 +199,7 @@ async fn setup_deployment_target_for_user(
if let Some(init) = init {
create_container_terminal_inner(
CreateTerminal {
name: terminal.clone(),
name: Some(terminal.clone()),
target: target.clone(),
command: init.command,
mode: init.mode,
@@ -241,7 +241,7 @@ pub async fn create_container_terminal_inner(
}: CreateTerminal,
periphery: &PeripheryClient,
container: String,
) -> anyhow::Result<()> {
) -> anyhow::Result<Terminal> {
match mode.unwrap_or_default() {
ContainerTerminalMode::Exec => periphery
.request(periphery_client::api::terminal::CreateContainerExecTerminal {
@@ -254,7 +254,7 @@ pub async fn create_container_terminal_inner(
.await
.context(
"Failed to create Container Exec Terminal on Periphery",
)?,
),
ContainerTerminalMode::Attach => periphery
.request(periphery_client::api::terminal::CreateContainerAttachTerminal {
name,
@@ -265,9 +265,8 @@ pub async fn create_container_terminal_inner(
.await
.context(
"Failed to create Container Attach Terminal on Periphery",
)?,
};
Ok(())
),
}
}
pub async fn get_stack_service_periphery_container(
+17 -1
View File
@@ -1,3 +1,5 @@
use std::time::Duration;
use anyhow::Context;
use database::mungos::{
by_id::{find_one_by_id, update_one_by_id},
@@ -16,7 +18,7 @@ use komodo_client::entities::{
stack::Stack,
swarm::Swarm,
sync::ResourceSync,
update::{Update, UpdateListItem},
update::{Update, UpdateListItem, UpdateStatus},
user::User,
};
@@ -591,3 +593,17 @@ pub async fn init_execution_update(
Ok(update)
}
pub async fn poll_update_until_complete(
update_id: &str,
) -> anyhow::Result<Update> {
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
let update = find_one_by_id(&db_client().updates, update_id)
.await?
.context("No update found at given ID")?;
if matches!(update.status, UpdateStatus::Complete) {
return Ok(update);
}
}
}
+2 -1
View File
@@ -3,7 +3,8 @@ use std::collections::HashMap;
use komodo_client::entities::{
ResourceTarget,
alert::{Alert, AlertData, SeverityLevel},
deployment::{Deployment, DeploymentState}, optional_string,
deployment::{Deployment, DeploymentState},
optional_string,
};
use crate::{
+3 -1
View File
@@ -2,7 +2,9 @@ use std::{collections::HashMap, sync::Mutex};
use anyhow::Context;
use komodo_client::entities::{
alert::AlertDataVariant, permission::PermissionLevel, resource::ResourceQuery, server::Server, swarm::Swarm, user::system_user
alert::AlertDataVariant, permission::PermissionLevel,
resource::ResourceQuery, server::Server, swarm::Swarm,
user::system_user,
};
use crate::resource;
+164 -64
View File
@@ -51,7 +51,7 @@ pub async fn alert_servers(
let mut alerts_to_open = Vec::<(Alert, SendAlerts)>::new();
let mut alerts_to_update = Vec::<(Alert, SendAlerts)>::new();
let mut alert_ids_to_close = Vec::<(Alert, SendAlerts)>::new();
let mut alerts_to_close = Vec::<(Alert, SendAlerts)>::new();
let buffer = alert_buffer();
@@ -66,15 +66,21 @@ pub async fn alert_servers(
let in_maintenance =
is_in_maintenance(&server.config.maintenance_windows, ts);
// ===================
// SERVER HEALTH
// ===================
// ===============
// SERVER HEALTH
// ===============
let health_alert = server_alerts.as_ref().and_then(|alerts| {
alerts.get(&AlertDataVariant::ServerUnreachable)
});
match (server_status.state, health_alert) {
(ServerState::NotOk, None) => {
// Only open unreachable alert if not in maintenance and buffer is ready
match (
server_status.state,
health_alert,
server.config.send_unreachable_alerts,
) {
// Skip alerting if alerts disabled
(ServerState::NotOk, None, false) => {}
(ServerState::NotOk, None, true) => {
// Only open unreachable alert if unreachable alerts enabled and not in maintenance and buffer is ready
if !in_maintenance
&& buffer.ready_to_open(
server_status.id.clone(),
@@ -99,19 +105,14 @@ pub async fn alert_servers(
.push((alert, server.config.send_unreachable_alerts))
}
}
(ServerState::NotOk, Some(alert)) => {
(ServerState::NotOk, Some(alert), true) => {
// update alert err
let mut alert = alert.clone();
let (id, name, region) = match alert.data {
AlertData::ServerUnreachable {
id, name, region, ..
} => (id, name, region),
data => {
error!(
"got incorrect alert data in ServerStatus handler. got {data:?}"
);
continue;
}
_ => unreachable!(),
};
alert.data = AlertData::ServerUnreachable {
id,
@@ -124,23 +125,30 @@ pub async fn alert_servers(
alerts_to_update.push((alert, false));
}
// Close an open alert
(ServerState::Ok | ServerState::Disabled, Some(alert)) => {
alert_ids_to_close.push((
(ServerState::NotOk, Some(alert), false) => {
alerts_to_close.push((
alert.clone(),
server.config.send_unreachable_alerts,
));
}
(ServerState::Ok | ServerState::Disabled, None) => buffer
// Close an open alert
(ServerState::Ok | ServerState::Disabled, Some(alert), _) => {
alerts_to_close.push((
alert.clone(),
server.config.send_unreachable_alerts,
));
}
(ServerState::Ok | ServerState::Disabled, None, _) => buffer
.reset(
server_status.id.clone(),
AlertDataVariant::ServerUnreachable,
),
}
// ===================
// ========================
// SERVER VERSION MISMATCH
// ===================
// ========================
let core_version = env!("CARGO_PKG_VERSION");
let mismatched_server_version =
if server_status.state != ServerState::Ok {
@@ -158,9 +166,14 @@ pub async fn alert_servers(
alerts.get(&AlertDataVariant::ServerVersionMismatch)
});
match (mismatched_server_version, version_alert) {
(Some(version), None) => {
// Only open version mismatch alert if not in maintenance and buffer is ready
match (
mismatched_server_version,
version_alert,
server.config.send_version_mismatch_alerts,
) {
(Some(_), None, false) => {}
(Some(version), None, true) => {
// Only open version mismatch alert if version mismatch alerts enabled and not in maintenance and buffer is ready
if !in_maintenance
&& buffer.ready_to_open(
server_status.id.clone(),
@@ -187,27 +200,47 @@ pub async fn alert_servers(
.push((alert, server.config.send_version_mismatch_alerts))
}
}
(Some(version), Some(alert)) => {
// Update existing alert with current version info
let mut alert = alert.clone();
alert.data = AlertData::ServerVersionMismatch {
id: server_status.id.clone(),
name: server.name.clone(),
region: optional_string(&server.config.region),
server_version: version.clone(),
core_version: core_version.to_string(),
};
// Don't send notification for updates
alerts_to_update.push((alert, false));
(Some(version), Some(alert), true) => {
// Update existing alert with current version info if changed
let (alert_server_version, alert_core_version) =
match &alert.data {
AlertData::ServerVersionMismatch {
server_version,
core_version,
..
} => (server_version, core_version),
_ => unreachable!(),
};
if alert_server_version != version
|| alert_core_version != core_version
{
let mut alert = alert.clone();
alert.data = AlertData::ServerVersionMismatch {
id: server_status.id.clone(),
name: server.name.clone(),
region: optional_string(&server.config.region),
server_version: version.clone(),
core_version: core_version.to_string(),
};
// Don't send notification for updates
alerts_to_update.push((alert, false));
}
}
(None, Some(alert)) => {
// Version is now correct, close the alert
alert_ids_to_close.push((
(Some(_), Some(alert), false) => {
alerts_to_close.push((
alert.clone(),
server.config.send_version_mismatch_alerts,
));
}
(None, None) => {
(None, Some(alert), _) => {
// Version is now correct, close the alert
alerts_to_close.push((
alert.clone(),
server.config.send_version_mismatch_alerts,
));
}
(None, None, _) => {
// Reset buffer state when no mismatch and no alert
buffer.reset(
server_status.id.clone(),
@@ -227,10 +260,25 @@ pub async fn alert_servers(
.as_ref()
.and_then(|alerts| alerts.get(&AlertDataVariant::ServerCpu))
.cloned();
match (health.cpu.level, cpu_alert, health.cpu.should_close_alert)
{
(SeverityLevel::Warning | SeverityLevel::Critical, None, _) => {
// Only open CPU alert if not in maintenance and buffer is ready
match (
health.cpu.level,
cpu_alert,
health.cpu.should_close_alert,
server.config.send_cpu_alerts,
) {
(
SeverityLevel::Warning | SeverityLevel::Critical,
None,
_,
false,
) => {}
(
SeverityLevel::Warning | SeverityLevel::Critical,
None,
_,
true,
) => {
// Only open CPU alert if cpu alerts enabled and not in maintenance and buffer is ready
if !in_maintenance
&& buffer.ready_to_open(
server_status.id.clone(),
@@ -262,6 +310,7 @@ pub async fn alert_servers(
SeverityLevel::Warning | SeverityLevel::Critical,
Some(mut alert),
_,
true,
) => {
// modify alert level only if it has increased and not in maintenance
if !in_maintenance && alert.level < health.cpu.level {
@@ -280,8 +329,15 @@ pub async fn alert_servers(
.push((alert, server.config.send_cpu_alerts));
}
}
(SeverityLevel::Ok, Some(alert), true) => {
let mut alert = alert.clone();
(
SeverityLevel::Warning | SeverityLevel::Critical,
Some(alert),
_,
false,
) => {
alerts_to_close.push((alert, server.config.send_cpu_alerts));
}
(SeverityLevel::Ok, Some(mut alert), true, _) => {
alert.data = AlertData::ServerCpu {
id: server_status.id.clone(),
name: server.name.clone(),
@@ -292,10 +348,9 @@ pub async fn alert_servers(
.map(|s| s.cpu_perc as f64)
.unwrap_or(0.0),
};
alert_ids_to_close
.push((alert, server.config.send_cpu_alerts))
alerts_to_close.push((alert, server.config.send_cpu_alerts))
}
(SeverityLevel::Ok, _, _) => buffer
(SeverityLevel::Ok, _, _, _) => buffer
.reset(server_status.id.clone(), AlertDataVariant::ServerCpu),
}
@@ -306,10 +361,25 @@ pub async fn alert_servers(
.as_ref()
.and_then(|alerts| alerts.get(&AlertDataVariant::ServerMem))
.cloned();
match (health.mem.level, mem_alert, health.mem.should_close_alert)
{
(SeverityLevel::Warning | SeverityLevel::Critical, None, _) => {
// Only open memory alert if not in maintenance and buffer is ready
match (
health.mem.level,
mem_alert,
health.mem.should_close_alert,
server.config.send_mem_alerts,
) {
(
SeverityLevel::Warning | SeverityLevel::Critical,
None,
_,
false,
) => {}
(
SeverityLevel::Warning | SeverityLevel::Critical,
None,
_,
true,
) => {
// Only open memory alert if memory alerts enabled and not in maintenance and buffer is ready
if !in_maintenance
&& buffer.ready_to_open(
server_status.id.clone(),
@@ -346,6 +416,7 @@ pub async fn alert_servers(
SeverityLevel::Warning | SeverityLevel::Critical,
Some(mut alert),
_,
true,
) => {
// modify alert level only if it has increased and not in maintenance
if !in_maintenance && alert.level < health.mem.level {
@@ -369,7 +440,16 @@ pub async fn alert_servers(
.push((alert, server.config.send_mem_alerts));
}
}
(SeverityLevel::Ok, Some(alert), true) => {
(
SeverityLevel::Warning | SeverityLevel::Critical,
Some(alert),
_,
false,
) => {
alerts_to_close
.push((alert.clone(), server.config.send_mem_alerts));
}
(SeverityLevel::Ok, Some(alert), true, _) => {
let mut alert = alert.clone();
alert.data = AlertData::ServerMem {
id: server_status.id.clone(),
@@ -386,10 +466,9 @@ pub async fn alert_servers(
.map(|s| s.mem_used_gb)
.unwrap_or(0.0),
};
alert_ids_to_close
.push((alert, server.config.send_mem_alerts))
alerts_to_close.push((alert, server.config.send_mem_alerts))
}
(SeverityLevel::Ok, _, _) => buffer
(SeverityLevel::Ok, _, _, _) => buffer
.reset(server_status.id.clone(), AlertDataVariant::ServerMem),
}
@@ -405,13 +484,25 @@ pub async fn alert_servers(
.as_ref()
.and_then(|alerts| alerts.get(path))
.cloned();
match (health.level, disk_alert, health.should_close_alert) {
match (
health.level,
disk_alert,
health.should_close_alert,
server.config.send_disk_alerts,
) {
(
SeverityLevel::Warning | SeverityLevel::Critical,
None,
_,
false,
) => {}
(
SeverityLevel::Warning | SeverityLevel::Critical,
None,
_,
true,
) => {
// Only open disk alert if not in maintenance and buffer is ready
// Only open disk alert if disk alerts enabled and not in maintenance and buffer is ready
if !in_maintenance
&& buffer.ready_to_open(
server_status.id.clone(),
@@ -450,6 +541,7 @@ pub async fn alert_servers(
SeverityLevel::Warning | SeverityLevel::Critical,
Some(mut alert),
_,
true,
) => {
// modify alert level only if it has increased and not in maintenance
if !in_maintenance && health.level < alert.level {
@@ -470,8 +562,16 @@ pub async fn alert_servers(
.push((alert, server.config.send_disk_alerts));
}
}
(SeverityLevel::Ok, Some(alert), true) => {
let mut alert = alert.clone();
(
SeverityLevel::Warning | SeverityLevel::Critical,
Some(alert),
_,
false,
) => {
alerts_to_close
.push((alert, server.config.send_disk_alerts));
}
(SeverityLevel::Ok, Some(mut alert), true, _) => {
let disk =
server_status.system_stats.as_ref().and_then(|stats| {
stats.disks.iter().find(|disk| disk.mount == *path)
@@ -485,10 +585,10 @@ pub async fn alert_servers(
total_gb: disk.map(|d| d.total_gb).unwrap_or_default(),
used_gb: disk.map(|d| d.used_gb).unwrap_or_default(),
};
alert_ids_to_close
alerts_to_close
.push((alert, server.config.send_disk_alerts))
}
(SeverityLevel::Ok, _, _) => buffer.reset(
(SeverityLevel::Ok, _, _, _) => buffer.reset(
server_status.id.clone(),
AlertDataVariant::ServerDisk,
),
@@ -501,7 +601,7 @@ pub async fn alert_servers(
if !health.disks.contains_key(path) {
let mut alert = alert.clone();
alert.level = SeverityLevel::Ok;
alert_ids_to_close
alerts_to_close
.push((alert, server.config.send_disk_alerts));
}
}
@@ -511,7 +611,7 @@ pub async fn alert_servers(
tokio::join!(
open_new_alerts(&alerts_to_open),
update_alerts(&alerts_to_update),
resolve_alerts(&alert_ids_to_close),
resolve_alerts(&alerts_to_close),
);
}
+1 -3
View File
@@ -64,9 +64,7 @@ pub async fn alert_stacks(
let data = AlertData::StackStateChange {
id: status.curr.id.clone(),
name: stack.name,
swarm_name: swarm_names
.get(&stack.config.swarm_id)
.cloned(),
swarm_name: swarm_names.get(&stack.config.swarm_id).cloned(),
swarm_id: optional_string(stack.config.swarm_id),
server_name: server_names
.get(&stack.config.server_id)
+32 -8
View File
@@ -47,7 +47,7 @@ pub async fn alert_swarms(
let mut alerts_to_open = Vec::<(Alert, SendAlerts)>::new();
let mut alerts_to_update = Vec::<(Alert, SendAlerts)>::new();
let mut alert_ids_to_close = Vec::<(Alert, SendAlerts)>::new();
let mut alerts_to_close = Vec::<(Alert, SendAlerts)>::new();
let buffer = alert_buffer();
@@ -68,14 +68,26 @@ pub async fn alert_swarms(
let health_alert = swarm_alerts.as_ref().and_then(|alerts| {
alerts.get(&AlertDataVariant::SwarmUnhealthy)
});
match (swarm_status.state, health_alert) {
match (
swarm_status.state,
health_alert,
swarm.config.send_unhealthy_alerts,
) {
(
SwarmState::Unhealthy
| SwarmState::Down
| SwarmState::Unknown,
None,
false,
) => {}
(
SwarmState::Unhealthy
| SwarmState::Down
| SwarmState::Unknown,
None,
true,
) => {
// Only open unreachable alert if not in maintenance and buffer is ready
// Only open unhealthy alert if unhealthy alerts enabled and not in maintenance and buffer is ready
if !in_maintenance
&& buffer.ready_to_open(
swarm_status.id.clone(),
@@ -104,6 +116,7 @@ pub async fn alert_swarms(
| SwarmState::Down
| SwarmState::Unknown,
Some(alert),
true,
) => {
// update alert err
let mut alert = alert.clone();
@@ -126,12 +139,23 @@ pub async fn alert_swarms(
alerts_to_update.push((alert, false));
}
// Close an open alert
(SwarmState::Healthy, Some(alert)) => {
alert_ids_to_close
(
SwarmState::Unhealthy
| SwarmState::Down
| SwarmState::Unknown,
Some(alert),
false,
) => {
alerts_to_close
.push((alert.clone(), swarm.config.send_unhealthy_alerts));
}
(SwarmState::Healthy, None) => buffer.reset(
// Close an open alert
(SwarmState::Healthy, Some(alert), _) => {
alerts_to_close
.push((alert.clone(), swarm.config.send_unhealthy_alerts));
}
(SwarmState::Healthy, None, _) => buffer.reset(
swarm_status.id.clone(),
AlertDataVariant::SwarmUnhealthy,
),
@@ -141,7 +165,7 @@ pub async fn alert_swarms(
tokio::join!(
open_new_alerts(&alerts_to_open),
update_alerts(&alerts_to_update),
resolve_alerts(&alert_ids_to_close),
resolve_alerts(&alerts_to_close),
);
}
+180 -168
View File
@@ -41,7 +41,7 @@ mod record;
mod resources;
mod swarm;
pub use swarm::update_cache_for_swarm;
pub use swarm::refresh_swarm_cache;
const ADDITIONAL_MS: u128 = 500;
@@ -52,17 +52,17 @@ pub fn spawn_monitoring_loops() {
fn spawn_server_monitoring_loop() {
tokio::spawn(async move {
refresh_server_cache(komodo_timestamp()).await;
refresh_all_server_cache(komodo_timestamp()).await;
let interval = monitoring_interval();
loop {
let ts = (wait_until_timelength(interval, ADDITIONAL_MS).await
- ADDITIONAL_MS) as i64;
refresh_server_cache(ts).await;
refresh_all_server_cache(ts).await;
}
});
}
async fn refresh_server_cache(ts: i64) {
async fn refresh_all_server_cache(ts: i64) {
let servers =
match find_collect(&db_client().servers, None, None).await {
Ok(servers) => servers,
@@ -74,7 +74,7 @@ async fn refresh_server_cache(ts: i64) {
}
};
let futures = servers.into_iter().map(|server| async move {
update_cache_for_server(&server, false).await;
refresh_server_cache(&server, false).await;
});
join_all(futures).await;
tokio::join!(check_alerts(ts), record_server_stats(ts));
@@ -82,7 +82,7 @@ async fn refresh_server_cache(ts: i64) {
/// Makes sure cache for server doesn't update too frequently / simultaneously.
/// If forced, will still block against simultaneous update.
fn update_cache_for_server_controller()
fn refresh_server_cache_controller()
-> &'static CloneCache<String, Arc<Mutex<i64>>> {
static CACHE: OnceLock<CloneCache<String, Arc<Mutex<i64>>>> =
OnceLock::new();
@@ -93,188 +93,176 @@ fn update_cache_for_server_controller()
/// which exits early if the lock is busy or it was completed too recently.
/// If force is true, it will wait on simultaneous calls, and will
/// ignore the restriction on being completed too recently.
pub async fn update_cache_for_server(server: &Server, force: bool) {
// Concurrency controller to ensure it isn't done too often
// when it happens in other contexts.
let controller = update_cache_for_server_controller()
.get_or_insert_default(&server.id)
.await;
let mut lock = match controller.try_lock() {
Ok(lock) => lock,
Err(_) if force => controller.lock().await,
Err(_) => return,
};
///
/// Returns impl Future for explicit Send bound, as this function
/// calls `periphery_client` which in some paths calls back this function `refresh_server_cache`.
/// While infinite recursion is prevented, the compiler needs some help to know the Send bound.
///
/// Clippy is not aware of this requirement, so the `manual_async_fn` lint is allowed here.
#[allow(clippy::manual_async_fn)]
pub fn refresh_server_cache(
server: &Server,
force: bool,
) -> impl Future<Output = ()> + Send + '_ {
async move {
// Concurrency controller to ensure it isn't done too often
// when it happens in other contexts.
let controller = refresh_server_cache_controller()
.get_or_insert_default(&server.id)
.await;
let mut lock = match controller.try_lock() {
Ok(lock) => lock,
Err(_) if force => controller.lock().await,
Err(_) => return,
};
let now = komodo_timestamp();
let now = komodo_timestamp();
// early return if called again sooner than 1s.
if !force && *lock > now - 1_000 {
return;
}
// early return if called again sooner than 1s.
if !force && *lock > now - 1_000 {
return;
}
*lock = now;
*lock = now;
let resources = UpdateCacheResources::load_server(server).await;
let resources = RefreshCacheResources::load_server(server).await;
// Handle server disabled
if !server.config.enabled {
resources.insert_status_unknown().await;
insert_server_status(
server,
ServerState::Disabled,
None,
None,
None,
None,
None,
)
.await;
periphery_connections().remove(&server.id).await;
return;
}
let periphery = match periphery_client(server).await {
Ok(periphery) => periphery,
Err(e) => {
resources.insert_status_unknown().await;
insert_server_status(
server,
ServerState::NotOk,
None,
None,
None,
None,
Serror::from(&e),
)
.await;
return;
}
};
let PollStatusResponse {
periphery_info,
system_info,
system_stats,
mut docker,
} = match periphery
.request(api::poll::PollStatus {
include_stats: server.config.stats_monitoring,
include_docker: true,
})
.await
{
Ok(info) => info,
Err(e) => {
resources.insert_status_unknown().await;
insert_server_status(
server,
ServerState::NotOk,
None,
None,
None,
None,
Serror::from(&e),
)
.await;
return;
}
};
if let Some(docker) = &mut docker {
docker.containers.iter_mut().for_each(|container| {
container.server_id = Some(server.id.clone())
});
}
let containers = docker
.as_ref()
.map(|docker| docker.containers.as_slice())
.unwrap_or(&[]);
let images = docker
.as_ref()
.map(|docker| docker.images.as_slice())
.unwrap_or(&[]);
tokio::join!(
resources::update_server_stack_cache(
resources.stacks,
containers,
images
),
resources::update_server_deployment_cache(
resources.deployments,
containers,
images,
),
);
// Handle server disabled
if !server.config.enabled {
resources.insert_status_unknown().await;
insert_server_status(
server,
ServerState::Disabled,
None,
None,
None,
None,
ServerState::Ok,
Some(periphery_info),
Some(system_info),
system_stats.map(|stats| filter_volumes(server, stats)),
docker,
None,
)
.await;
periphery_connections().remove(&server.id).await;
return;
}
let periphery = match periphery_client(server).await {
Ok(periphery) => periphery,
Err(e) => {
resources.insert_status_unknown().await;
insert_server_status(
server,
ServerState::NotOk,
None,
None,
None,
None,
Serror::from(&e),
)
.await;
return;
let status_cache = repo_status_cache();
for repo in resources.repos {
let (latest_hash, latest_message) = periphery
.request(GetLatestCommit {
name: repo.name.clone(),
path: optional_string(&repo.config.path),
})
.await
.ok()
.flatten()
.map(|c| (c.hash, c.message))
.unzip();
status_cache
.insert(
repo.id,
CachedRepoStatus {
latest_hash,
latest_message,
}
.into(),
)
.await;
}
};
let PollStatusResponse {
periphery_info,
system_info,
system_stats,
mut docker,
} = match periphery
.request(api::poll::PollStatus {
include_stats: server.config.stats_monitoring,
include_docker: true,
})
.await
{
Ok(info) => info,
Err(e) => {
resources.insert_status_unknown().await;
insert_server_status(
server,
ServerState::NotOk,
None,
None,
None,
None,
Serror::from(&e),
)
.await;
return;
}
};
if let Some(docker) = &mut docker {
docker.containers.iter_mut().for_each(|container| {
container.server_id = Some(server.id.clone())
});
}
let containers = docker
.as_ref()
.map(|docker| docker.containers.as_slice())
.unwrap_or(&[]);
let images = docker
.as_ref()
.map(|docker| docker.images.as_slice())
.unwrap_or(&[]);
tokio::join!(
resources::update_server_stack_cache(
resources.stacks,
containers,
images
),
resources::update_server_deployment_cache(
resources.deployments,
containers,
images,
),
);
insert_server_status(
server,
ServerState::Ok,
Some(periphery_info),
Some(system_info),
system_stats.map(|stats| filter_volumes(server, stats)),
docker,
None,
)
.await;
let status_cache = repo_status_cache();
for repo in resources.repos {
let (latest_hash, latest_message) = periphery
.request(GetLatestCommit {
name: repo.name.clone(),
path: optional_string(&repo.config.path),
})
.await
.ok()
.flatten()
.map(|c| (c.hash, c.message))
.unzip();
status_cache
.insert(
repo.id,
CachedRepoStatus {
latest_hash,
latest_message,
}
.into(),
)
.await;
}
}
struct UpdateCacheResources {
struct RefreshCacheResources {
stacks: Vec<Stack>,
deployments: Vec<Deployment>,
repos: Vec<Repo>,
}
impl UpdateCacheResources {
pub async fn load_swarm(swarm: &Swarm) -> Self {
let (stacks, deployments) = tokio::join!(
find_collect(
&db_client().stacks,
doc! { "config.swarm_id": &swarm.id },
None,
),
find_collect(
&db_client().deployments,
doc! { "config.swarm_id": &swarm.id },
None,
),
);
let stacks = stacks.inspect_err(|e| error!("Failed to get stacks list from db (update swarm status cache) | swarm: {} | {e:#}", swarm.name)).unwrap_or_default();
let deployments = deployments.inspect_err(|e| error!("Failed to get deployments list from db (update swarm status cache) | swarm : {} | {e:#}", swarm.name)).unwrap_or_default();
Self {
stacks,
deployments,
repos: Default::default(),
}
}
impl RefreshCacheResources {
pub async fn load_server(server: &Server) -> Self {
let (stacks, deployments, repos) = tokio::join!(
find_collect(
@@ -305,6 +293,30 @@ impl UpdateCacheResources {
}
}
pub async fn load_swarm(swarm: &Swarm) -> Self {
let (stacks, deployments) = tokio::join!(
find_collect(
&db_client().stacks,
doc! { "config.swarm_id": &swarm.id },
None,
),
find_collect(
&db_client().deployments,
doc! { "config.swarm_id": &swarm.id },
None,
),
);
let stacks = stacks.inspect_err(|e| error!("Failed to get stacks list from db (update swarm status cache) | swarm: {} | {e:#}", swarm.name)).unwrap_or_default();
let deployments = deployments.inspect_err(|e| error!("Failed to get deployments list from db (update swarm status cache) | swarm : {} | {e:#}", swarm.name)).unwrap_or_default();
Self {
stacks,
deployments,
repos: Default::default(),
}
}
pub async fn insert_status_unknown(self) {
insert_stacks_status_unknown(self.stacks).await;
insert_deployments_status_unknown(self.deployments).await;
+9 -9
View File
@@ -23,7 +23,7 @@ use crate::{
config::monitoring_interval,
helpers::swarm::swarm_request_custom_timeout,
monitor::{
UpdateCacheResources,
RefreshCacheResources,
resources::{
update_swarm_deployment_cache, update_swarm_stack_cache,
},
@@ -35,16 +35,16 @@ const ADDITIONAL_MS: u128 = 1000;
pub fn spawn_swarm_monitoring_loop() {
tokio::spawn(async move {
refresh_swarm_cache().await;
refresh_all_swarm_cache().await;
let interval = monitoring_interval();
loop {
wait_until_timelength(interval, ADDITIONAL_MS).await;
refresh_swarm_cache().await;
refresh_all_swarm_cache().await;
}
});
}
async fn refresh_swarm_cache() {
async fn refresh_all_swarm_cache() {
let swarms =
match find_collect(&db_client().swarms, None, None).await {
Ok(swarms) => swarms,
@@ -56,7 +56,7 @@ async fn refresh_swarm_cache() {
}
};
let futures = swarms.into_iter().map(|swarm| async move {
update_cache_for_swarm(&swarm, false).await;
refresh_swarm_cache(&swarm, false).await;
});
join_all(futures).await;
// tokio::join!(check_alerts(ts), record_swarm_stats(ts));
@@ -64,7 +64,7 @@ async fn refresh_swarm_cache() {
/// Makes sure cache for swarm doesn't update too frequently / simultaneously.
/// If forced, will still block against simultaneous update.
fn update_cache_for_swarm_controller()
fn refresh_swarm_cache_controller()
-> &'static CloneCache<String, Arc<Mutex<i64>>> {
static CACHE: OnceLock<CloneCache<String, Arc<Mutex<i64>>>> =
OnceLock::new();
@@ -75,10 +75,10 @@ fn update_cache_for_swarm_controller()
/// which exits early if the lock is busy or it was completed too recently.
/// If force is true, it will wait on simultaneous calls, and will
/// ignore the restriction on being completed too recently.
pub async fn update_cache_for_swarm(swarm: &Swarm, force: bool) {
pub async fn refresh_swarm_cache(swarm: &Swarm, force: bool) {
// Concurrency controller to ensure it isn't done too often
// when it happens in other contexts.
let controller = update_cache_for_swarm_controller()
let controller = refresh_swarm_cache_controller()
.get_or_insert_default(&swarm.id)
.await;
let mut lock = match controller.try_lock() {
@@ -96,7 +96,7 @@ pub async fn update_cache_for_swarm(swarm: &Swarm, force: bool) {
*lock = now;
let resources = UpdateCacheResources::load_swarm(swarm).await;
let resources = RefreshCacheResources::load_swarm(swarm).await;
if swarm.config.server_ids.is_empty() {
resources.insert_status_unknown().await;
+10 -4
View File
@@ -33,7 +33,7 @@ use crate::{
query::{get_deployment_state, get_swarm_or_server},
swarm::swarm_request,
},
monitor::{update_cache_for_server, update_cache_for_swarm},
monitor::{refresh_server_cache, refresh_swarm_cache},
state::{action_states, db_client, deployment_status_cache},
};
@@ -72,7 +72,13 @@ impl super::KomodoResource for Deployment {
fn inherit_specific_permissions_from(
_self: &Resource<Self::Config, Self::Info>,
) -> Option<ResourceTarget> {
ResourceTarget::Server(_self.config.server_id.clone()).into()
if !_self.config.swarm_id.is_empty() {
Some(ResourceTarget::Swarm(_self.config.swarm_id.clone()))
} else if !_self.config.server_id.is_empty() {
Some(ResourceTarget::Server(_self.config.server_id.clone()))
} else {
None
}
}
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
@@ -221,10 +227,10 @@ impl super::KomodoResource for Deployment {
};
match swarm_or_server {
SwarmOrServer::Swarm(swarm) => {
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
SwarmOrServer::Server(server) => {
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
}
SwarmOrServer::None => {}
}
+3 -3
View File
@@ -24,7 +24,7 @@ use periphery_client::api;
use crate::{
config::core_config,
helpers::periphery_client,
monitor::update_cache_for_server,
monitor::refresh_server_cache,
state::{
action_states, db_client, periphery_connections,
server_status_cache,
@@ -151,7 +151,7 @@ impl super::KomodoResource for Server {
created: &Resource<Self::Config, Self::Info>,
_update: &mut Update,
) -> anyhow::Result<()> {
update_cache_for_server(created, true).await;
refresh_server_cache(created, true).await;
Ok(())
}
@@ -173,7 +173,7 @@ impl super::KomodoResource for Server {
updated: &Self,
_update: &mut Update,
) -> anyhow::Result<()> {
update_cache_for_server(updated, true).await;
refresh_server_cache(updated, true).await;
Ok(())
}
+10 -4
View File
@@ -35,7 +35,7 @@ use crate::{
repo_link,
swarm::swarm_request,
},
monitor::{update_cache_for_server, update_cache_for_swarm},
monitor::{refresh_server_cache, refresh_swarm_cache},
state::{
action_states, all_resources_cache, db_client,
server_status_cache, stack_status_cache,
@@ -77,7 +77,13 @@ impl super::KomodoResource for Stack {
fn inherit_specific_permissions_from(
_self: &Resource<Self::Config, Self::Info>,
) -> Option<ResourceTarget> {
ResourceTarget::Server(_self.config.server_id.clone()).into()
if !_self.config.swarm_id.is_empty() {
Some(ResourceTarget::Swarm(_self.config.swarm_id.clone()))
} else if !_self.config.server_id.is_empty() {
Some(ResourceTarget::Server(_self.config.server_id.clone()))
} else {
None
}
}
fn coll() -> &'static Collection<Resource<Self::Config, Self::Info>>
@@ -294,10 +300,10 @@ impl super::KomodoResource for Stack {
};
match swarm_or_server {
SwarmOrServer::Swarm(swarm) => {
update_cache_for_swarm(&swarm, true).await;
refresh_swarm_cache(&swarm, true).await;
}
SwarmOrServer::Server(server) => {
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
}
SwarmOrServer::None => {}
}
+3 -3
View File
@@ -15,7 +15,7 @@ use komodo_client::entities::{
use crate::{
config::core_config,
monitor::update_cache_for_swarm,
monitor::refresh_swarm_cache,
state::{db_client, swarm_status_cache},
};
@@ -89,7 +89,7 @@ impl super::KomodoResource for Swarm {
created: &Self,
_update: &mut Update,
) -> anyhow::Result<()> {
update_cache_for_swarm(created, true).await;
refresh_swarm_cache(created, true).await;
Ok(())
}
@@ -111,7 +111,7 @@ impl super::KomodoResource for Swarm {
updated: &Self,
_update: &mut Update,
) -> anyhow::Result<()> {
update_cache_for_swarm(updated, true).await;
refresh_swarm_cache(updated, true).await;
Ok(())
}
+6 -8
View File
@@ -10,12 +10,7 @@ use komodo_client::{
permission::PermissionLevel,
repo::Repo,
resource::Resource,
sync::{
PartialResourceSyncConfig, ResourceSync, ResourceSyncConfig,
ResourceSyncConfigDiff, ResourceSyncInfo, ResourceSyncListItem,
ResourceSyncListItemInfo, ResourceSyncQuerySpecifics,
ResourceSyncState,
},
sync::*,
update::Update,
user::{User, sync_user},
},
@@ -260,12 +255,15 @@ async fn get_resource_sync_state(
{
return state;
}
if data.pending_error.is_some() || !data.remote_errors.is_empty() {
if data.pending_error.is_some()
|| data.pending_deploy_error.is_some()
|| !data.remote_errors.is_empty()
{
ResourceSyncState::Failed
} else if !data.resource_updates.is_empty()
|| !data.variable_updates.is_empty()
|| !data.user_group_updates.is_empty()
|| data.pending_deploy.to_deploy > 0
|| !data.pending_deploys.is_empty()
{
ResourceSyncState::Pending
} else {
+2 -2
View File
@@ -109,7 +109,7 @@ pub fn spawn_schedule_executor() {
.resolve(&ExecuteArgs {
user: action_user().to_owned(),
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
{
@@ -180,7 +180,7 @@ pub fn spawn_schedule_executor() {
.resolve(&ExecuteArgs {
user: procedure_user().to_owned(),
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
{
+3 -3
View File
@@ -14,7 +14,7 @@ use periphery_client::api::compose::*;
use crate::{
helpers::{periphery_client, update::update_update},
monitor::update_cache_for_server,
monitor::refresh_server_cache,
periphery::PeripheryClient,
state::action_states,
};
@@ -82,7 +82,7 @@ pub async fn execute_compose_with_stack_and_server<
// The returned guard will set the action state back to default when dropped.
let _action_guard = action_state.update(set_in_progress)?;
// Send update here for frontend to recheck action state
// Send update here for UI to recheck action state
update_update(update.clone()).await?;
let periphery = periphery_client(&server).await?;
@@ -102,7 +102,7 @@ pub async fn execute_compose_with_stack_and_server<
.push(T::execute(periphery, stack, services, extras).await?);
// Ensure cached stack state up to date by updating server cache
update_cache_for_server(&server, true).await;
refresh_server_cache(&server, true).await;
update.finalize();
update_update(update.clone()).await?;
+20 -20
View File
@@ -1,7 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use colored::Colorize;
use database::mungos::{
find::find_collect,
mongodb::bson::{
@@ -33,6 +32,7 @@ use komodo_client::{
user::{action_user, system_user},
},
};
use mogh_auth_server::api::login::local::sign_up_local_user;
use mogh_resolver::Resolve;
use uuid::Uuid;
@@ -41,6 +41,7 @@ use crate::{
execute::{ExecuteArgs, ExecuteRequest},
write::WriteArgs,
},
auth::KomodoAuthImpl,
config::core_config,
helpers::update::init_execution_update,
network, resource,
@@ -91,7 +92,7 @@ pub async fn run_startup_actions() {
.resolve(&ExecuteArgs {
user: action_user().to_owned(),
update,
id: Uuid::new_v4(),
task_id: Uuid::new_v4(),
})
.await
{
@@ -301,7 +302,7 @@ async fn ensure_init_user_and_resources() {
// Assumes if there are any existing users, procedures, or tags,
// the default procedures do not need to be set up.
let Ok((None, None, None)) = tokio::try_join!(
let Ok((None, procedures, tags)) = tokio::try_join!(
db.users.find_one(Document::new()),
db.procedures.find_one(Document::new()),
db.tags.find_one(Document::new()),
@@ -314,20 +315,16 @@ async fn ensure_init_user_and_resources() {
// Init admin user if set in config.
if let Some(username) = &config.init_admin_username {
info!("Creating init admin user...");
// if let Err(e) = (SignUpLocalUser {
// username: username.clone(),
// password: config.init_admin_password.clone(),
// })
// .resolve(&AuthArgs {
// headers: Default::default(),
// ip: IpAddr::V4(Ipv4Addr::UNSPECIFIED),
// session: None,
// })
// .await
// {
// error!("Failed to create init admin user | {:#}", e.error);
// return;
// }
if let Err(e) = sign_up_local_user(
&KomodoAuthImpl,
username.to_string(),
&config.init_admin_password,
)
.await
{
error!("Failed to create init admin user | {:#}", e.error);
return;
}
match db
.users
.find_one(doc! { "username": username })
@@ -347,12 +344,15 @@ async fn ensure_init_user_and_resources() {
}
}
if config.disable_init_resources {
info!("System resources init {}", "DISABLED".red());
if config.disable_init_resources
|| procedures.is_some()
|| tags.is_some()
{
info!("Skipping initial system resource creation");
return;
}
info!("Creating init system resources...");
info!("Creating initial system resources...");
let write_args = WriteArgs {
user: system_user().to_owned(),
+1 -1
View File
@@ -220,5 +220,5 @@ pub fn all_resources_cache() -> &'static ArcSwap<AllResourcesById> {
pub fn image_digest_cache() -> &'static ImageDigestCache {
static IMAGE_DIGEST_CACHE: OnceLock<Arc<ImageDigestCache>> =
OnceLock::new();
IMAGE_DIGEST_CACHE.get_or_init(|| ImageDigestCache::new())
IMAGE_DIGEST_CACHE.get_or_init(ImageDigestCache::new)
}
+27 -54
View File
@@ -18,7 +18,7 @@ use komodo_client::{
PartialStackConfig, Stack, StackConfig,
StackRemoteFileContents, StackState,
},
sync::SyncDeployUpdate,
sync::SyncDeployTarget,
toml::ResourceToml,
update::Log,
user::sync_user,
@@ -41,8 +41,7 @@ use super::ResourceSyncTrait;
/// All entries in here are due to be deployed,
/// after the given dependencies,
/// with the given reason.
pub type ToDeployCache =
Vec<(ResourceTarget, String, Vec<ResourceTarget>)>;
pub type ToDeployCache = Vec<SyncDeployTarget>;
#[derive(Clone, Copy)]
pub struct SyncDeployParams<'a> {
@@ -72,14 +71,16 @@ pub async fn deploy_from_cache(
// Collect all waiting deployments without waiting dependencies.
let good_to_deploy = to_deploy
.iter()
.filter(|(_, _, after)| {
to_deploy
.iter()
.all(|(target, _, _)| !after.contains(target))
.filter(|SyncDeployTarget { after, .. }| {
to_deploy.iter().all(|SyncDeployTarget { target, .. }| {
!after.contains(target)
})
})
// The target / reason need the be cloned out to to_deploy is not borrowed from.
// to_deploy will be mutably accessed later.
.map(|(target, reason, _)| (target.clone(), reason.clone()))
.map(|SyncDeployTarget { target, reason, .. }| {
(target.clone(), reason.clone())
})
.collect::<HashMap<_, _>>();
// Deploy the ones ready for deployment
@@ -103,7 +104,7 @@ pub async fn deploy_from_cache(
.resolve(&ExecuteArgs {
user: user.to_owned(),
update,
id,
task_id: id,
})
.await
}
@@ -122,7 +123,7 @@ pub async fn deploy_from_cache(
.resolve(&ExecuteArgs {
user: user.to_owned(),
update,
id,
task_id: id,
})
.await
}
@@ -172,8 +173,9 @@ pub async fn deploy_from_cache(
}
// Remove the deployed ones from 'to_deploy'
to_deploy
.retain(|(target, _, _)| !good_to_deploy.contains_key(target));
to_deploy.retain(|SyncDeployTarget { target, .. }| {
!good_to_deploy.contains_key(target)
});
// If there must be another round, these are dependent on the first round.
// Sleep for 1s to allow for first round to startup
@@ -196,46 +198,10 @@ pub async fn deploy_from_cache(
pub async fn get_updates_for_view(
params: SyncDeployParams<'_>,
) -> SyncDeployUpdate {
let inner = async {
let mut update = SyncDeployUpdate {
to_deploy: 0,
log: String::from("Deploy Updates\n-------------------\n"),
};
let mut lines = Vec::<String>::new();
for (target, reason, after) in build_deploy_cache(params).await? {
update.to_deploy += 1;
let mut line = format!(
"{}: {}. reason: {reason}",
colored("Deploy", Color::Green),
bold(format!("{target:?}")),
);
if !after.is_empty() {
line.push_str(&format!(
"\n{}: {}",
colored("After", Color::Blue),
after
.iter()
.map(|target| format!("{target:?}"))
.collect::<Vec<_>>()
.join(", ")
))
}
lines.push(line);
}
update.log.push_str(&lines.join("\n-------------------\n"));
anyhow::Ok(update)
};
match inner.await {
Ok(res) => res,
Err(e) => SyncDeployUpdate {
to_deploy: 0,
log: format_serror(
&e.context("failed to get deploy updates for view").into(),
),
},
) -> (Vec<SyncDeployTarget>, Option<String>) {
match build_deploy_cache(params).await {
Ok(targets) => (targets, None),
Err(e) => (Vec::new(), Some(format_serror(&e.into()))),
}
}
@@ -294,7 +260,11 @@ pub async fn build_deploy_cache(
.map(|(target, (reason, mut after))| {
// Only keep targets which are deploying.
after.retain(|target| clone.contains_key(target));
(target, reason, after)
SyncDeployTarget {
target,
reason,
after,
}
})
.collect(),
)
@@ -620,9 +590,12 @@ fn build_cache_for_stack<'a>(
|| diff.extra_args.is_some()
|| diff.environment.is_some()
|| diff.env_file_path.is_some()
|| diff.additional_env_files.is_some()
|| diff.linked_repo.is_some()
|| diff.repo.is_some()
|| diff.branch.is_some()
|| diff.commit.is_some();
|| diff.commit.is_some()
|| diff.compose_cmd_wrapper.is_some();
if changed {
cache.insert(
target,
+150 -475
View File
@@ -35,17 +35,6 @@ use super::{
include_resource_by_tags,
};
impl ResourceSyncTrait for Swarm {
fn get_diff(
original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
Ok(original.partial_diff(update))
}
}
impl ExecuteResourceSync for Swarm {}
impl ResourceSyncTrait for Server {
fn get_diff(
original: Self::Config,
@@ -57,20 +46,41 @@ impl ResourceSyncTrait for Server {
impl ExecuteResourceSync for Server {}
impl ResourceSyncTrait for Swarm {
fn get_diff(
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let all = all_resources_cache().load();
original.server_ids.iter_mut().for_each(|server_id| {
*server_id = all
.servers
.get(server_id)
.map(|s| s.name.clone())
.unwrap_or_default();
});
Ok(original.partial_diff(update))
}
}
impl ExecuteResourceSync for Swarm {}
impl ResourceSyncTrait for Deployment {
fn get_diff(
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let resources = all_resources_cache().load();
let all = all_resources_cache().load();
original.swarm_id = resources
original.swarm_id = all
.swarms
.get(&original.swarm_id)
.map(|s| s.name.clone())
.unwrap_or_default();
original.server_id = resources
original.server_id = all
.servers
.get(&original.server_id)
.map(|s| s.name.clone())
@@ -80,7 +90,7 @@ impl ResourceSyncTrait for Deployment {
&original.image
{
original.image = DeploymentImage::Build {
build_id: resources
build_id: all
.builds
.get(build_id)
.map(|b| b.name.clone())
@@ -100,21 +110,21 @@ impl ResourceSyncTrait for Stack {
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let resources = all_resources_cache().load();
let all = all_resources_cache().load();
original.swarm_id = resources
original.swarm_id = all
.swarms
.get(&original.swarm_id)
.map(|s| s.name.clone())
.unwrap_or_default();
original.server_id = resources
original.server_id = all
.servers
.get(&original.server_id)
.map(|s| s.name.clone())
.unwrap_or_default();
original.linked_repo = resources
original.linked_repo = all
.repos
.get(&original.linked_repo)
.map(|r| r.name.clone())
@@ -131,13 +141,13 @@ impl ResourceSyncTrait for Build {
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let resources = all_resources_cache().load();
original.builder_id = resources
let all = all_resources_cache().load();
original.builder_id = all
.builders
.get(&original.builder_id)
.map(|b| b.name.clone())
.unwrap_or_default();
original.linked_repo = resources
original.linked_repo = all
.repos
.get(&original.linked_repo)
.map(|r| r.name.clone())
@@ -165,16 +175,16 @@ impl ResourceSyncTrait for Repo {
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let resources = all_resources_cache().load();
let all = all_resources_cache().load();
// Need to replace server id with name
original.server_id = resources
original.server_id = all
.servers
.get(&original.server_id)
.map(|s| s.name.clone())
.unwrap_or_default();
// Need to replace builder id with name
original.builder_id = resources
original.builder_id = all
.builders
.get(&original.builder_id)
.map(|s| s.name.clone())
@@ -204,8 +214,8 @@ impl ResourceSyncTrait for Builder {
) -> anyhow::Result<Self::ConfigDiff> {
// need to replace server builder id with name
if let BuilderConfig::Server(config) = &mut original {
let resources = all_resources_cache().load();
config.server_id = resources
let all = all_resources_cache().load();
config.server_id = all
.servers
.get(&config.server_id)
.map(|s| s.name.clone())
@@ -313,8 +323,8 @@ impl ResourceSyncTrait for ResourceSync {
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let resources = all_resources_cache().load();
original.linked_repo = resources
let all = all_resources_cache().load();
original.linked_repo = all
.repos
.get(&original.linked_repo)
.map(|r| r.name.clone())
@@ -331,456 +341,121 @@ impl ResourceSyncTrait for Procedure {
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let resources = all_resources_cache().load();
let all = all_resources_cache().load();
for stage in &mut original.stages {
for execution in &mut stage.executions {
match &mut execution.execution {
Execution::None(_) => {}
Execution::RunProcedure(config) => {
config.procedure = resources
.procedures
.get(&config.procedure)
.map(|p| p.name.clone())
.unwrap_or_default();
}
Execution::BatchRunProcedure(_config) => {}
Execution::RunAction(config) => {
config.action = resources
.actions
.get(&config.action)
.map(|p| p.name.clone())
.unwrap_or_default();
}
Execution::BatchRunAction(_config) => {}
Execution::RunBuild(config) => {
config.build = resources
.builds
.get(&config.build)
.map(|b| b.name.clone())
.unwrap_or_default();
}
Execution::BatchRunBuild(_config) => {}
Execution::CancelBuild(config) => {
config.build = resources
.builds
.get(&config.build)
.map(|b| b.name.clone())
.unwrap_or_default();
}
Execution::Deploy(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::BatchDeploy(_config) => {}
Execution::PullDeployment(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::StartDeployment(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::RestartDeployment(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PauseDeployment(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::UnpauseDeployment(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::StopDeployment(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::DestroyDeployment(config) => {
config.deployment = resources
.deployments
.get(&config.deployment)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::BatchDestroyDeployment(_config) => {}
Execution::CloneRepo(config) => {
config.repo = resources
.repos
.get(&config.repo)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::BatchCloneRepo(_config) => {}
Execution::PullRepo(config) => {
config.repo = resources
.repos
.get(&config.repo)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::BatchPullRepo(_config) => {}
Execution::BuildRepo(config) => {
config.repo = resources
.repos
.get(&config.repo)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::BatchBuildRepo(_config) => {}
Execution::CancelRepoBuild(config) => {
config.repo = resources
.repos
.get(&config.repo)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::StartContainer(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::RestartContainer(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PauseContainer(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::UnpauseContainer(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::StopContainer(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::DestroyContainer(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::StartAllContainers(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::RestartAllContainers(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PauseAllContainers(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::UnpauseAllContainers(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::StopAllContainers(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PruneContainers(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::DeleteNetwork(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PruneNetworks(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::DeleteImage(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PruneImages(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::DeleteVolume(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PruneVolumes(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PruneDockerBuilders(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PruneBuildx(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::PruneSystem(config) => {
config.server = resources
.servers
.get(&config.server)
.map(|d| d.name.clone())
.unwrap_or_default();
}
Execution::RunSync(config) => {
config.sync = resources
.syncs
.get(&config.sync)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::CommitSync(config) => {
config.sync = resources
.syncs
.get(&config.sync)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::DeployStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::BatchDeployStack(_config) => {}
Execution::DeployStackIfChanged(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::BatchDeployStackIfChanged(_config) => {}
Execution::PullStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::BatchPullStack(_config) => {}
Execution::StartStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RestartStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::PauseStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::UnpauseStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::StopStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::DestroyStack(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RunStackService(config) => {
config.stack = resources
.stacks
.get(&config.stack)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::BatchDestroyStack(_config) => {}
Execution::TestAlerter(config) => {
config.alerter = resources
.alerters
.get(&config.alerter)
.map(|a| a.name.clone())
.unwrap_or_default();
}
Execution::SendAlert(config) => {
config.alerters = config
.alerters
.iter()
.map(|alerter| {
resources
// Replaces an id field on config with the resource name from `all`.
macro_rules! replace_id_with_name {
($($Variant:ident => $field:ident, $collection:ident);* $(;)?) => {
match &mut execution.execution {
$(
Execution::$Variant(config) => {
config.$field = all
.$collection
.get(&config.$field)
.map(|r| r.name.clone())
.unwrap_or_default();
}
)*
// SendAlert maps a Vec of alerter ids
Execution::SendAlert(config) => {
config.alerters = config
.alerters
.get(alerter)
.map(|a| a.name.clone())
.unwrap_or_default()
})
.collect();
}
Execution::RemoveSwarmNodes(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RemoveSwarmStacks(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RemoveSwarmServices(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::CreateSwarmConfig(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RotateSwarmConfig(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RemoveSwarmConfigs(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::CreateSwarmSecret(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RotateSwarmSecret(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::RemoveSwarmSecrets(config) => {
config.swarm = resources
.swarms
.get(&config.swarm)
.map(|s| s.name.clone())
.unwrap_or_default();
}
Execution::ClearRepoCache(_) => {}
Execution::BackupCoreDatabase(_) => {}
Execution::GlobalAutoUpdate(_) => {}
Execution::RotateAllServerKeys(_) => {}
Execution::RotateCoreKeys(_) => {}
Execution::Sleep(_) => {}
.iter()
.map(|alerter| {
all
.alerters
.get(alerter)
.map(|a| a.name.clone())
.unwrap_or_default()
})
.collect();
}
// No-op variants
Execution::None(_)
| Execution::BatchRunProcedure(_)
| Execution::BatchRunAction(_)
| Execution::BatchRunBuild(_)
| Execution::BatchDeploy(_)
| Execution::BatchDestroyDeployment(_)
| Execution::BatchCloneRepo(_)
| Execution::BatchPullRepo(_)
| Execution::BatchBuildRepo(_)
| Execution::BatchDeployStack(_)
| Execution::BatchDeployStackIfChanged(_)
| Execution::BatchPullStack(_)
| Execution::BatchDestroyStack(_)
| Execution::ClearRepoCache(_)
| Execution::BackupCoreDatabase(_)
| Execution::GlobalAutoUpdate(_)
| Execution::RotateAllServerKeys(_)
| Execution::RotateCoreKeys(_)
| Execution::Sleep(_) => {}
}
};
}
replace_id_with_name!(
RunProcedure => procedure, procedures;
RunAction => action, actions;
RunBuild => build, builds;
CancelBuild => build, builds;
Deploy => deployment, deployments;
PullDeployment => deployment, deployments;
StartDeployment => deployment, deployments;
RestartDeployment => deployment, deployments;
PauseDeployment => deployment, deployments;
UnpauseDeployment => deployment, deployments;
StopDeployment => deployment, deployments;
DestroyDeployment => deployment, deployments;
CloneRepo => repo, repos;
PullRepo => repo, repos;
BuildRepo => repo, repos;
CancelRepoBuild => repo, repos;
StartContainer => server, servers;
RestartContainer => server, servers;
PauseContainer => server, servers;
UnpauseContainer => server, servers;
StopContainer => server, servers;
DestroyContainer => server, servers;
StartAllContainers => server, servers;
RestartAllContainers => server, servers;
PauseAllContainers => server, servers;
UnpauseAllContainers => server, servers;
StopAllContainers => server, servers;
PruneContainers => server, servers;
DeleteNetwork => server, servers;
PruneNetworks => server, servers;
DeleteImage => server, servers;
PruneImages => server, servers;
DeleteVolume => server, servers;
PruneVolumes => server, servers;
PruneDockerBuilders => server, servers;
PruneBuildx => server, servers;
PruneSystem => server, servers;
RunSync => sync, syncs;
CommitSync => sync, syncs;
DeployStack => stack, stacks;
DeployStackIfChanged => stack, stacks;
PullStack => stack, stacks;
StartStack => stack, stacks;
RestartStack => stack, stacks;
PauseStack => stack, stacks;
UnpauseStack => stack, stacks;
StopStack => stack, stacks;
DestroyStack => stack, stacks;
RunStackService => stack, stacks;
TestAlerter => alerter, alerters;
RemoveSwarmNodes => swarm, swarms;
RemoveSwarmStacks => swarm, swarms;
RemoveSwarmServices => swarm, swarms;
CreateSwarmConfig => swarm, swarms;
RotateSwarmConfig => swarm, swarms;
RemoveSwarmConfigs => swarm, swarms;
CreateSwarmSecret => swarm, swarms;
RotateSwarmSecret => swarm, swarms;
RemoveSwarmSecrets => swarm, swarms;
);
}
}
Ok(original.partial_diff(update))
+8
View File
@@ -178,6 +178,14 @@ impl ToToml for ResourceSync {
impl ToToml for Swarm {
fn replace_ids(resource: &mut Resource<Self::Config, Self::Info>) {
let all = all_resources_cache().load();
resource.config.server_ids.iter_mut().for_each(|server_id| {
*server_id = all
.servers
.get(server_id)
.map(|s| s.name.clone())
.unwrap_or_default();
});
let mut res =
Vec::with_capacity(resource.config.server_ids.capacity());
for server_id in &resource.config.server_ids {
+1 -1
View File
@@ -45,7 +45,7 @@ async fn serve_client_file(
let contents = fs::read_to_string(format!(
"{}/client/{path}",
core_config().frontend_path
core_config().ui_path
))
.await
.with_context(|| format!("Failed to read file: {path}"))?;
+1 -1
View File
@@ -1,6 +1,6 @@
## All in one, multi stage compile + runtime Docker build for your architecture.
FROM rust:1.93.0-trixie AS builder
FROM rust:1.94.0-trixie AS builder
RUN cargo install cargo-strip
WORKDIR /builder
+1 -1
View File
@@ -2,7 +2,7 @@
## Sets up the necessary runtime container dependencies for Komodo Periphery.
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
ARG X86_64_BINARIES=${BINARIES_IMAGE}-x86_64
ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
+1 -1
View File
@@ -1,7 +1,7 @@
## Assumes the latest binaries for the required arch are already built (by binaries.Dockerfile).
## Sets up the necessary runtime container dependencies for Komodo Periphery.
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:latest
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries:2
# This is required to work with COPY --from
FROM ${BINARIES_IMAGE} AS binaries
+219 -44
View File
@@ -307,8 +307,8 @@ impl Resolve<crate::api::Args> for ComposePull {
};
// Canonicalize the path to ensure it exists, and is the cleanest path to the run directory.
let run_directory = run_directory.canonicalize().context(
"Failed to validate run directory on host after stack write (canonicalize error)",
let run_directory = run_directory.canonicalize().with_context(||
format!("Failed to validate run directory on host after stack write (canonicalize error), path={}", run_directory.to_string_lossy()),
)?;
let file_paths = stack
@@ -352,16 +352,56 @@ impl Resolve<crate::api::Args> for ComposePull {
let project_name = stack.project_name(false);
// Parse wrapper configuration
let compose_cmd_wrapper =
parse_multiline_command(&stack.config.compose_cmd_wrapper);
// If wrapper_include is empty but wrapper is set, use default ["up"] for backward compatibility
let default_include = vec![String::from("up")];
let wrapper_include =
if stack.config.compose_cmd_wrapper_include.is_empty()
&& !compose_cmd_wrapper.is_empty()
{
&default_include
} else {
&stack.config.compose_cmd_wrapper_include
};
let pull_command = format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull{service_args}",
);
let (pull_command, wrapped) = match maybe_wrap_command(
pull_command,
&compose_cmd_wrapper,
wrapper_include,
"pull",
) {
Ok(result) => result,
Err(log) => {
res.logs.push(log);
return Ok(res);
}
};
let span = info_span!("RunComposePull");
let log = run_komodo_standard_command(
let mode = if wrapped {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Standard
};
let Some(log) = run_komodo_command_with_sanitization(
"Compose Pull",
run_directory.as_ref(),
format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull{service_args}",
),
run_directory.as_path(),
pull_command,
mode,
&replacers,
)
.instrument(span)
.await;
.await
else {
// Only reachable if command is empty,
// not the case since it is provided above.
unreachable!()
};
res.logs.push(log);
@@ -427,8 +467,8 @@ impl Resolve<crate::api::Args> for ComposeUp {
};
// Canonicalize the path to ensure it exists, and is the cleanest path to the run directory.
let run_directory = run_directory.canonicalize().context(
"Failed to validate run directory on host after stack write (canonicalize error)",
let run_directory = run_directory.canonicalize().with_context(||
format!("Failed to validate run directory on host after stack write (canonicalize error), path={}", run_directory.to_string_lossy()),
)?;
validate_files(&stack, &run_directory, &mut res).await;
@@ -483,25 +523,54 @@ impl Resolve<crate::api::Args> for ComposeUp {
&stack.config.additional_env_files,
)?;
// Parse wrapper configuration once for reuse
let compose_cmd_wrapper =
parse_multiline_command(&stack.config.compose_cmd_wrapper);
// If wrapper_include is empty but wrapper is set, use default ["up"] for backward compatibility
let default_include = vec![String::from("up")];
let wrapper_include =
if stack.config.compose_cmd_wrapper_include.is_empty()
&& !compose_cmd_wrapper.is_empty()
{
&default_include
} else {
&stack.config.compose_cmd_wrapper_include
};
// Uses 'docker compose config' command to extract services (including image)
// after performing interpolation
{
let command = format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} config",
);
let (command, wrapped) = match maybe_wrap_command(
command,
&compose_cmd_wrapper,
wrapper_include,
"config",
) {
Ok(result) => result,
Err(log) => {
res.logs.push(log);
return Ok(res);
}
};
let mode = if wrapped {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Standard
};
let span = info_span!("GetComposeConfig", command);
let Some(config_log) = run_komodo_command_with_sanitization(
"Compose Config",
run_directory.as_path(),
command,
KomodoCommandMode::Standard,
mode,
&replacers,
)
.instrument(span)
.await
else {
// Only reachable if command is empty,
// not the case since it is provided above.
unreachable!()
};
if !config_log.success {
@@ -558,12 +627,29 @@ impl Resolve<crate::api::Args> for ComposeUp {
let command = format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} build{build_extra_args}{service_args}",
);
let (command, wrapped) = match maybe_wrap_command(
command,
&compose_cmd_wrapper,
wrapper_include,
"build",
) {
Ok(result) => result,
Err(log) => {
res.logs.push(log);
return Ok(res);
}
};
let mode = if wrapped {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Standard
};
let span = info_span!("ExecuteComposeBuild");
let Some(log) = run_komodo_command_with_sanitization(
"Compose Build",
run_directory.as_path(),
command,
KomodoCommandMode::Shell,
mode,
&replacers,
)
.instrument(span)
@@ -584,14 +670,36 @@ impl Resolve<crate::api::Args> for ComposeUp {
let command = format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull{service_args}",
);
let span = info_span!("RunComposePull");
let log = run_komodo_standard_command(
"Compose Pull",
run_directory.as_ref(),
let (command, wrapped) = match maybe_wrap_command(
command,
&compose_cmd_wrapper,
wrapper_include,
"pull",
) {
Ok(result) => result,
Err(log) => {
res.logs.push(log);
return Ok(res);
}
};
let mode = if wrapped {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Standard
};
let span = info_span!("RunComposePull");
let Some(log) = run_komodo_command_with_sanitization(
"Compose Pull",
run_directory.as_path(),
command,
mode,
&replacers,
)
.instrument(span)
.await;
.await
else {
unreachable!()
};
res.logs.push(log);
if !all_logs_success(&res.logs) {
return Ok(res);
@@ -611,24 +719,21 @@ impl Resolve<crate::api::Args> for ComposeUp {
// Run compose up
let extra_args = format_extra_args(&stack.config.extra_args);
let mut command = format!(
let command = format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} up -d{extra_args}{service_args}",
);
// Apply compose cmd wrapper if configured
let compose_cmd_wrapper =
parse_multiline_command(&stack.config.compose_cmd_wrapper);
if !compose_cmd_wrapper.is_empty() {
if !compose_cmd_wrapper.contains("[[COMPOSE_COMMAND]]") {
res.logs.push(Log::error(
"Compose Command Wrapper",
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
));
let (command, _) = match maybe_wrap_command(
command,
&compose_cmd_wrapper,
wrapper_include,
"up",
) {
Ok(result) => result,
Err(log) => {
res.logs.push(log);
return Ok(res);
}
command =
compose_cmd_wrapper.replace("[[COMPOSE_COMMAND]]", &command);
}
};
let span = info_span!("ExecuteComposeUp");
let Some(log) = run_komodo_command_with_sanitization(
@@ -762,8 +867,8 @@ impl Resolve<crate::api::Args> for ComposeRun {
}
};
let run_directory = run_directory.canonicalize().context(
"Failed to validate run directory on host after stack write (canonicalize error)",
let run_directory = run_directory.canonicalize().with_context(||
format!("Failed to validate run directory on host after stack write (canonicalize error), path={}", run_directory.to_string_lossy())
)?;
maybe_login_registry(&stack, registry_token, &mut Vec::new())
@@ -784,15 +889,49 @@ impl Resolve<crate::api::Args> for ComposeRun {
let project_name = stack.project_name(true);
// Parse wrapper configuration
let compose_cmd_wrapper =
parse_multiline_command(&stack.config.compose_cmd_wrapper);
// If wrapper_include is empty but wrapper is set, use default ["up"] for backward compatibility
let default_include = vec![String::from("up")];
let wrapper_include =
if stack.config.compose_cmd_wrapper_include.is_empty()
&& !compose_cmd_wrapper.is_empty()
{
&default_include
} else {
&stack.config.compose_cmd_wrapper_include
};
if pull.unwrap_or_default() {
let pull_log = run_komodo_standard_command(
let pull_command = format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull {service}",
);
let (pull_command, wrapped) = match maybe_wrap_command(
pull_command,
&compose_cmd_wrapper,
wrapper_include,
"pull",
) {
Ok(result) => result,
Err(log) => return Ok(log),
};
let mode = if wrapped {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Standard
};
let Some(pull_log) = run_komodo_command_with_sanitization(
"Compose Pull",
run_directory.as_ref(),
format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} pull {service}",
),
run_directory.as_path(),
pull_command,
mode,
&replacers,
)
.await;
.await
else {
unreachable!()
};
if !pull_log.success {
return Ok(pull_log);
}
@@ -839,15 +978,24 @@ impl Resolve<crate::api::Args> for ComposeRun {
})
.unwrap_or_default();
let command = format!(
let run_command = format!(
"{docker_compose} -p {project_name} -f {file_args}{env_file_args} run{run_flags} {service}{command_args}",
);
let (run_command, _) = match maybe_wrap_command(
run_command,
&compose_cmd_wrapper,
wrapper_include,
"run",
) {
Ok(result) => result,
Err(log) => return Ok(log),
};
let span = info_span!("RunComposeRun", command);
let span = info_span!("RunComposeRun", run_command);
let Some(log) = run_komodo_command_with_sanitization(
"Compose Run",
run_directory.as_path(),
command,
run_command,
KomodoCommandMode::Shell,
&replacers,
)
@@ -888,6 +1036,33 @@ fn env_file_args(
Ok(res)
}
/// Apply compose_cmd_wrapper to command if the subcommand is in wrapper_include list.
/// Returns Ok((command, wrapped)) where `wrapped` indicates if wrapper was applied.
/// Returns Err(Log) if wrapper is invalid (missing placeholder).
fn maybe_wrap_command(
command: String,
wrapper: &str,
wrapper_include: &[String],
subcommand: &str,
) -> Result<(String, bool), Log> {
// Skip wrapping if wrapper is empty or subcommand is not in include list
if wrapper.is_empty()
|| !wrapper_include.iter().any(|c| c == subcommand)
{
return Ok((command, false));
}
// Validate wrapper contains placeholder
if !wrapper.contains("[[COMPOSE_COMMAND]]") {
return Err(Log::error(
"Compose Command Wrapper",
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
));
}
Ok((wrapper.replace("[[COMPOSE_COMMAND]]", &command), true))
}
#[instrument("ComposeDown", skip(res))]
async fn compose_down(
project: &str,
+68 -12
View File
@@ -30,6 +30,32 @@ use crate::{
state::docker_client,
};
/// Apply compose_cmd_wrapper to command if the subcommand is in wrapper_include list.
/// Returns Ok((command, wrapped)) where `wrapped` indicates if wrapper was applied.
fn maybe_wrap_command(
command: String,
wrapper: &str,
wrapper_include: &[String],
subcommand: &str,
) -> Result<(String, bool), Log> {
// Skip wrapping if wrapper is empty or subcommand is not in include list
if wrapper.is_empty()
|| !wrapper_include.iter().any(|c| c == subcommand)
{
return Ok((command, false));
}
// Validate wrapper contains placeholder
if !wrapper.contains("[[COMPOSE_COMMAND]]") {
return Err(Log::error(
"Compose Command Wrapper",
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
));
}
Ok((wrapper.replace("[[COMPOSE_COMMAND]]", &command), true))
}
impl Resolve<crate::api::Args> for InspectSwarmStack {
async fn resolve(
self,
@@ -178,16 +204,47 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
let last_project_name = stack.project_name(false);
let project_name = stack.project_name(true);
// Parse wrapper configuration once for reuse
let compose_cmd_wrapper =
parse_multiline_command(&stack.config.compose_cmd_wrapper);
// If wrapper_include is empty but wrapper is set, use default ["deploy"] for backward compatibility
let default_include = vec![String::from("deploy")];
let wrapper_include =
if stack.config.compose_cmd_wrapper_include.is_empty()
&& !compose_cmd_wrapper.is_empty()
{
&default_include
} else {
&stack.config.compose_cmd_wrapper_include
};
// Uses 'docker stack config' command to extract services (including image)
// after performing interpolation
{
let command = format!("docker stack config -c {file_args}",);
let (command, wrapped) = match maybe_wrap_command(
command,
&compose_cmd_wrapper,
wrapper_include,
"config",
) {
Ok(result) => result,
Err(log) => {
res.logs.push(log);
return Ok(res);
}
};
let mode = if wrapped {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Standard
};
let span = info_span!("GetStackConfig", command);
let Some(config_log) = run_komodo_command_with_sanitization(
"Stack Config",
run_directory.as_path(),
command,
KomodoCommandMode::Standard,
mode,
&replacers,
)
.instrument(span)
@@ -240,19 +297,18 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
write!(&mut command, " {project_name}")?;
// Apply compose cmd wrapper if configured
let compose_cmd_wrapper =
parse_multiline_command(&stack.config.compose_cmd_wrapper);
if !compose_cmd_wrapper.is_empty() {
if !compose_cmd_wrapper.contains("[[COMPOSE_COMMAND]]") {
res.logs.push(Log::error(
"Compose Command Wrapper",
"compose_cmd_wrapper is configured but does not contain [[COMPOSE_COMMAND]] placeholder. The placeholder is required to inject the compose command.".to_string(),
));
let (command, _) = match maybe_wrap_command(
command,
&compose_cmd_wrapper,
wrapper_include,
"deploy",
) {
Ok(result) => result,
Err(log) => {
res.logs.push(log);
return Ok(res);
}
command =
compose_cmd_wrapper.replace("[[COMPOSE_COMMAND]]", &command);
}
};
let span = info_span!("ExecuteStackDeploy");
let Some(log) = run_komodo_command_with_sanitization(
+40 -10
View File
@@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::{Context, anyhow};
use futures_util::{Stream, StreamExt, TryStreamExt};
use komodo_client::entities::{
KOMODO_EXIT_CODE, NoData,
KOMODO_EXIT_CODE, NoData, optional_string,
terminal::{Terminal, TerminalStdinMessage, TerminalTarget},
};
use mogh_resolver::Resolve;
@@ -52,20 +52,32 @@ impl Resolve<crate::api::Args> for CreateServerTerminal {
async fn resolve(
self,
args: &crate::api::Args,
) -> anyhow::Result<NoData> {
) -> anyhow::Result<Terminal> {
if periphery_config().disable_terminals {
return Err(anyhow!(
"Terminals are disabled in the Periphery config"
));
}
let existing =
list_terminals(Some(&TerminalTarget::Server { server: None }))
.await;
create_terminal(
self.name,
self
.name
.and_then(optional_string)
.unwrap_or_else(|| format!("term-{}", existing.len())),
TerminalTarget::Server { server: None },
self.command,
self.recreate,
)
.await
.map(|_| NoData {})
.map(|terminal| Terminal {
name: terminal.name.clone(),
target: TerminalTarget::Server { server: None },
command: terminal.command.clone(),
stored_size_kb: terminal.history.size_kb(),
created_at: terminal.created_at,
})
}
}
@@ -87,7 +99,7 @@ impl Resolve<crate::api::Args> for CreateContainerExecTerminal {
async fn resolve(
self,
args: &crate::api::Args,
) -> anyhow::Result<NoData> {
) -> anyhow::Result<Terminal> {
if periphery_config().disable_container_terminals {
return Err(anyhow!(
"Container Terminals are disabled in the Periphery config"
@@ -106,14 +118,23 @@ impl Resolve<crate::api::Args> for CreateContainerExecTerminal {
"The use of '&&' is forbidden in the container name or command"
));
}
let existing = list_terminals(Some(&target)).await;
create_terminal(
name,
name.and_then(optional_string).unwrap_or_else(|| {
format!("exec-{container}-{}", existing.len())
}),
target,
Some(format!("docker exec -it {container} {command}")),
recreate,
)
.await
.map(|_| NoData {})
.map(|terminal| Terminal {
name: terminal.name.clone(),
target: terminal.target.clone(),
command: terminal.command.clone(),
stored_size_kb: terminal.history.size_kb(),
created_at: terminal.created_at,
})
}
}
@@ -134,7 +155,7 @@ impl Resolve<crate::api::Args> for CreateContainerAttachTerminal {
async fn resolve(
self,
args: &crate::api::Args,
) -> anyhow::Result<NoData> {
) -> anyhow::Result<Terminal> {
if periphery_config().disable_container_terminals {
return Err(anyhow!(
"Container Terminals are disabled in the Periphery config"
@@ -151,14 +172,23 @@ impl Resolve<crate::api::Args> for CreateContainerAttachTerminal {
"The use of '&&' is forbidden in the container name"
));
}
let existing = list_terminals(Some(&target)).await;
create_terminal(
name,
name.and_then(optional_string).unwrap_or_else(|| {
format!("attach-{container}-{}", existing.len())
}),
target,
Some(format!("docker attach {container} --sig-proxy=false")),
recreate,
)
.await
.map(|_| NoData {})
.map(|terminal| Terminal {
name: terminal.name.clone(),
target: terminal.target.clone(),
command: terminal.command.clone(),
stored_size_kb: terminal.history.size_kb(),
created_at: terminal.created_at,
})
}
}
+7
View File
@@ -94,6 +94,13 @@ pub async fn handler(
already_logged_connection_error = false;
debug!(
host = identifiers.host(),
query,
sec_websocket_accept = accept.to_str().unwrap_or_default(),
"[CORE AUTH] Zero trust identifiers"
);
let identifiers =
identifiers.build(accept.as_bytes(), query.as_bytes());
+7
View File
@@ -109,6 +109,13 @@ async fn handler(
let query = format!("core={}", urlencoding::encode(&core));
debug!(
host = identifiers.host.to_str().unwrap_or_default(),
query,
sec_websocket_accept = identifiers.accept,
"[CORE AUTH] Zero trust identifiers"
);
if let Err(e) =
handle_login(&mut socket, identifiers.build(query.as_bytes()))
.await
+46 -46
View File
@@ -461,80 +461,80 @@ impl DockerClient {
}
fn convert_summary_container_state(
state: bollard::secret::ContainerSummaryStateEnum,
state: bollard::config::ContainerSummaryStateEnum,
) -> ContainerStateStatusEnum {
match state {
bollard::secret::ContainerSummaryStateEnum::EMPTY => {
bollard::config::ContainerSummaryStateEnum::EMPTY => {
ContainerStateStatusEnum::Empty
}
bollard::secret::ContainerSummaryStateEnum::CREATED => {
bollard::config::ContainerSummaryStateEnum::CREATED => {
ContainerStateStatusEnum::Created
}
bollard::secret::ContainerSummaryStateEnum::RUNNING => {
bollard::config::ContainerSummaryStateEnum::RUNNING => {
ContainerStateStatusEnum::Running
}
bollard::secret::ContainerSummaryStateEnum::PAUSED => {
bollard::config::ContainerSummaryStateEnum::PAUSED => {
ContainerStateStatusEnum::Paused
}
bollard::secret::ContainerSummaryStateEnum::RESTARTING => {
bollard::config::ContainerSummaryStateEnum::RESTARTING => {
ContainerStateStatusEnum::Restarting
}
bollard::secret::ContainerSummaryStateEnum::EXITED => {
bollard::config::ContainerSummaryStateEnum::EXITED => {
ContainerStateStatusEnum::Exited
}
bollard::secret::ContainerSummaryStateEnum::REMOVING => {
bollard::config::ContainerSummaryStateEnum::REMOVING => {
ContainerStateStatusEnum::Removing
}
bollard::secret::ContainerSummaryStateEnum::DEAD => {
bollard::config::ContainerSummaryStateEnum::DEAD => {
ContainerStateStatusEnum::Dead
}
}
}
fn convert_container_state_status(
state: bollard::secret::ContainerStateStatusEnum,
state: bollard::config::ContainerStateStatusEnum,
) -> ContainerStateStatusEnum {
match state {
bollard::secret::ContainerStateStatusEnum::EMPTY => {
bollard::config::ContainerStateStatusEnum::EMPTY => {
ContainerStateStatusEnum::Empty
}
bollard::secret::ContainerStateStatusEnum::CREATED => {
bollard::config::ContainerStateStatusEnum::CREATED => {
ContainerStateStatusEnum::Created
}
bollard::secret::ContainerStateStatusEnum::RUNNING => {
bollard::config::ContainerStateStatusEnum::RUNNING => {
ContainerStateStatusEnum::Running
}
bollard::secret::ContainerStateStatusEnum::PAUSED => {
bollard::config::ContainerStateStatusEnum::PAUSED => {
ContainerStateStatusEnum::Paused
}
bollard::secret::ContainerStateStatusEnum::RESTARTING => {
bollard::config::ContainerStateStatusEnum::RESTARTING => {
ContainerStateStatusEnum::Restarting
}
bollard::secret::ContainerStateStatusEnum::EXITED => {
bollard::config::ContainerStateStatusEnum::EXITED => {
ContainerStateStatusEnum::Exited
}
bollard::secret::ContainerStateStatusEnum::REMOVING => {
bollard::config::ContainerStateStatusEnum::REMOVING => {
ContainerStateStatusEnum::Removing
}
bollard::secret::ContainerStateStatusEnum::DEAD => {
bollard::config::ContainerStateStatusEnum::DEAD => {
ContainerStateStatusEnum::Dead
}
}
}
fn convert_port_type(
typ: bollard::secret::PortSummaryTypeEnum,
typ: bollard::config::PortSummaryTypeEnum,
) -> PortTypeEnum {
match typ {
bollard::secret::PortSummaryTypeEnum::EMPTY => {
bollard::config::PortSummaryTypeEnum::EMPTY => {
PortTypeEnum::EMPTY
}
bollard::secret::PortSummaryTypeEnum::TCP => PortTypeEnum::TCP,
bollard::secret::PortSummaryTypeEnum::UDP => PortTypeEnum::UDP,
bollard::secret::PortSummaryTypeEnum::SCTP => PortTypeEnum::SCTP,
bollard::config::PortSummaryTypeEnum::TCP => PortTypeEnum::TCP,
bollard::config::PortSummaryTypeEnum::UDP => PortTypeEnum::UDP,
bollard::config::PortSummaryTypeEnum::SCTP => PortTypeEnum::SCTP,
}
}
fn convert_port(port: bollard::secret::PortSummary) -> Port {
fn convert_port(port: bollard::config::PortSummary) -> Port {
Port {
ip: port.ip,
private_port: port.private_port,
@@ -544,27 +544,27 @@ fn convert_port(port: bollard::secret::PortSummary) -> Port {
}
fn convert_health_status(
status: bollard::secret::HealthStatusEnum,
status: bollard::config::HealthStatusEnum,
) -> HealthStatusEnum {
match status {
bollard::secret::HealthStatusEnum::EMPTY => {
bollard::config::HealthStatusEnum::EMPTY => {
HealthStatusEnum::Empty
}
bollard::secret::HealthStatusEnum::NONE => HealthStatusEnum::None,
bollard::secret::HealthStatusEnum::STARTING => {
bollard::config::HealthStatusEnum::NONE => HealthStatusEnum::None,
bollard::config::HealthStatusEnum::STARTING => {
HealthStatusEnum::Starting
}
bollard::secret::HealthStatusEnum::HEALTHY => {
bollard::config::HealthStatusEnum::HEALTHY => {
HealthStatusEnum::Healthy
}
bollard::secret::HealthStatusEnum::UNHEALTHY => {
bollard::config::HealthStatusEnum::UNHEALTHY => {
HealthStatusEnum::Unhealthy
}
}
}
fn convert_health_check_result(
check: bollard::secret::HealthcheckResult,
check: bollard::config::HealthcheckResult,
) -> HealthcheckResult {
HealthcheckResult {
start: check.start,
@@ -575,57 +575,57 @@ fn convert_health_check_result(
}
fn convert_restart_policy(
policy: bollard::secret::RestartPolicyNameEnum,
policy: bollard::config::RestartPolicyNameEnum,
) -> RestartPolicyNameEnum {
match policy {
bollard::secret::RestartPolicyNameEnum::EMPTY => {
bollard::config::RestartPolicyNameEnum::EMPTY => {
RestartPolicyNameEnum::Empty
}
bollard::secret::RestartPolicyNameEnum::NO => {
bollard::config::RestartPolicyNameEnum::NO => {
RestartPolicyNameEnum::No
}
bollard::secret::RestartPolicyNameEnum::ALWAYS => {
bollard::config::RestartPolicyNameEnum::ALWAYS => {
RestartPolicyNameEnum::Always
}
bollard::secret::RestartPolicyNameEnum::UNLESS_STOPPED => {
bollard::config::RestartPolicyNameEnum::UNLESS_STOPPED => {
RestartPolicyNameEnum::UnlessStopped
}
bollard::secret::RestartPolicyNameEnum::ON_FAILURE => {
bollard::config::RestartPolicyNameEnum::ON_FAILURE => {
RestartPolicyNameEnum::OnFailure
}
}
}
fn convert_cgroupns_mode(
mode: bollard::secret::HostConfigCgroupnsModeEnum,
mode: bollard::config::HostConfigCgroupnsModeEnum,
) -> HostConfigCgroupnsModeEnum {
match mode {
bollard::secret::HostConfigCgroupnsModeEnum::EMPTY => {
bollard::config::HostConfigCgroupnsModeEnum::EMPTY => {
HostConfigCgroupnsModeEnum::Empty
}
bollard::secret::HostConfigCgroupnsModeEnum::PRIVATE => {
bollard::config::HostConfigCgroupnsModeEnum::PRIVATE => {
HostConfigCgroupnsModeEnum::Private
}
bollard::secret::HostConfigCgroupnsModeEnum::HOST => {
bollard::config::HostConfigCgroupnsModeEnum::HOST => {
HostConfigCgroupnsModeEnum::Host
}
}
}
fn convert_isolation_mode(
isolation: bollard::secret::HostConfigIsolationEnum,
isolation: bollard::config::HostConfigIsolationEnum,
) -> HostConfigIsolationEnum {
match isolation {
bollard::secret::HostConfigIsolationEnum::EMPTY => {
bollard::config::HostConfigIsolationEnum::EMPTY => {
HostConfigIsolationEnum::Empty
}
bollard::secret::HostConfigIsolationEnum::DEFAULT => {
bollard::config::HostConfigIsolationEnum::DEFAULT => {
HostConfigIsolationEnum::Default
}
bollard::secret::HostConfigIsolationEnum::PROCESS => {
bollard::config::HostConfigIsolationEnum::PROCESS => {
HostConfigIsolationEnum::Process
}
bollard::secret::HostConfigIsolationEnum::HYPERV => {
bollard::config::HostConfigIsolationEnum::HYPERV => {
HostConfigIsolationEnum::Hyperv
}
}
+7 -8
View File
@@ -29,8 +29,7 @@ impl DockerClient {
});
let name = image
.repo_tags
.iter()
.next()
.first()
.cloned()
.unwrap_or_else(|| image.id.clone());
ImageListItem {
@@ -73,10 +72,10 @@ impl DockerClient {
content: manifest.size.content,
},
kind: manifest.kind.map(|kind| match kind {
bollard::secret::ImageManifestSummaryKindEnum::EMPTY => ImageManifestSummaryKindEnum::Empty,
bollard::secret::ImageManifestSummaryKindEnum::IMAGE => ImageManifestSummaryKindEnum::Image,
bollard::secret::ImageManifestSummaryKindEnum::ATTESTATION => ImageManifestSummaryKindEnum::Attestation,
bollard::secret::ImageManifestSummaryKindEnum::UNKNOWN => ImageManifestSummaryKindEnum::Unknown,
bollard::config::ImageManifestSummaryKindEnum::EMPTY => ImageManifestSummaryKindEnum::Empty,
bollard::config::ImageManifestSummaryKindEnum::IMAGE => ImageManifestSummaryKindEnum::Image,
bollard::config::ImageManifestSummaryKindEnum::ATTESTATION => ImageManifestSummaryKindEnum::Attestation,
bollard::config::ImageManifestSummaryKindEnum::UNKNOWN => ImageManifestSummaryKindEnum::Unknown,
}),
image_data: manifest.image_data.map(|data| {
ImageManifestSummaryImageData {
@@ -160,7 +159,7 @@ impl DockerClient {
}
fn convert_oci_descriptor(
descriptor: bollard::secret::OciDescriptor,
descriptor: bollard::config::OciDescriptor,
) -> OciDescriptor {
OciDescriptor {
media_type: descriptor.media_type,
@@ -175,7 +174,7 @@ fn convert_oci_descriptor(
}
fn convert_oci_platform(
platform: bollard::secret::OciPlatform,
platform: bollard::config::OciPlatform,
) -> OciPlatform {
OciPlatform {
architecture: platform.architecture,
+47 -47
View File
@@ -151,68 +151,68 @@ fn convert_mount(mount: bollard::models::Mount) -> Mount {
}
fn convert_mount_type(
typ: bollard::secret::MountTypeEnum,
typ: bollard::config::MountTypeEnum,
) -> MountTypeEnum {
match typ {
bollard::secret::MountTypeEnum::EMPTY => MountTypeEnum::Empty,
bollard::secret::MountTypeEnum::BIND => MountTypeEnum::Bind,
bollard::secret::MountTypeEnum::VOLUME => MountTypeEnum::Volume,
bollard::secret::MountTypeEnum::IMAGE => MountTypeEnum::Image,
bollard::secret::MountTypeEnum::TMPFS => MountTypeEnum::Tmpfs,
bollard::secret::MountTypeEnum::NPIPE => MountTypeEnum::Npipe,
bollard::secret::MountTypeEnum::CLUSTER => MountTypeEnum::Cluster,
bollard::config::MountTypeEnum::EMPTY => MountTypeEnum::Empty,
bollard::config::MountTypeEnum::BIND => MountTypeEnum::Bind,
bollard::config::MountTypeEnum::VOLUME => MountTypeEnum::Volume,
bollard::config::MountTypeEnum::IMAGE => MountTypeEnum::Image,
bollard::config::MountTypeEnum::TMPFS => MountTypeEnum::Tmpfs,
bollard::config::MountTypeEnum::NPIPE => MountTypeEnum::Npipe,
bollard::config::MountTypeEnum::CLUSTER => MountTypeEnum::Cluster,
}
}
fn convert_mount_propogation(
propogation: bollard::secret::MountBindOptionsPropagationEnum,
propogation: bollard::config::MountBindOptionsPropagationEnum,
) -> MountBindOptionsPropagationEnum {
match propogation {
bollard::secret::MountBindOptionsPropagationEnum::EMPTY => {
bollard::config::MountBindOptionsPropagationEnum::EMPTY => {
MountBindOptionsPropagationEnum::Empty
}
bollard::secret::MountBindOptionsPropagationEnum::PRIVATE => {
bollard::config::MountBindOptionsPropagationEnum::PRIVATE => {
MountBindOptionsPropagationEnum::Private
}
bollard::secret::MountBindOptionsPropagationEnum::RPRIVATE => {
bollard::config::MountBindOptionsPropagationEnum::RPRIVATE => {
MountBindOptionsPropagationEnum::Rprivate
}
bollard::secret::MountBindOptionsPropagationEnum::SHARED => {
bollard::config::MountBindOptionsPropagationEnum::SHARED => {
MountBindOptionsPropagationEnum::Shared
}
bollard::secret::MountBindOptionsPropagationEnum::RSHARED => {
bollard::config::MountBindOptionsPropagationEnum::RSHARED => {
MountBindOptionsPropagationEnum::Rshared
}
bollard::secret::MountBindOptionsPropagationEnum::SLAVE => {
bollard::config::MountBindOptionsPropagationEnum::SLAVE => {
MountBindOptionsPropagationEnum::Slave
}
bollard::secret::MountBindOptionsPropagationEnum::RSLAVE => {
bollard::config::MountBindOptionsPropagationEnum::RSLAVE => {
MountBindOptionsPropagationEnum::Rslave
}
}
}
fn convert_mount_point_type(
typ: bollard::secret::MountPointTypeEnum,
typ: bollard::config::MountPointTypeEnum,
) -> MountTypeEnum {
match typ {
bollard::secret::MountPointTypeEnum::EMPTY => {
bollard::config::MountPointTypeEnum::EMPTY => {
MountTypeEnum::Empty
}
bollard::secret::MountPointTypeEnum::BIND => MountTypeEnum::Bind,
bollard::secret::MountPointTypeEnum::VOLUME => {
bollard::config::MountPointTypeEnum::BIND => MountTypeEnum::Bind,
bollard::config::MountPointTypeEnum::VOLUME => {
MountTypeEnum::Volume
}
bollard::secret::MountPointTypeEnum::IMAGE => {
bollard::config::MountPointTypeEnum::IMAGE => {
MountTypeEnum::Image
}
bollard::secret::MountPointTypeEnum::TMPFS => {
bollard::config::MountPointTypeEnum::TMPFS => {
MountTypeEnum::Tmpfs
}
bollard::secret::MountPointTypeEnum::NPIPE => {
bollard::config::MountPointTypeEnum::NPIPE => {
MountTypeEnum::Npipe
}
bollard::secret::MountPointTypeEnum::CLUSTER => {
bollard::config::MountPointTypeEnum::CLUSTER => {
MountTypeEnum::Cluster
}
}
@@ -290,17 +290,17 @@ fn convert_endpoint_spec_ports(
.map(|port| EndpointPortConfig {
name: port.name,
protocol: port.protocol.map(|protocol| match protocol {
bollard::secret::EndpointPortConfigProtocolEnum::EMPTY => EndpointPortConfigProtocolEnum::EMPTY,
bollard::secret::EndpointPortConfigProtocolEnum::TCP => EndpointPortConfigProtocolEnum::TCP,
bollard::secret::EndpointPortConfigProtocolEnum::UDP => EndpointPortConfigProtocolEnum::UDP,
bollard::secret::EndpointPortConfigProtocolEnum::SCTP => EndpointPortConfigProtocolEnum::SCTP,
bollard::config::EndpointPortConfigProtocolEnum::EMPTY => EndpointPortConfigProtocolEnum::EMPTY,
bollard::config::EndpointPortConfigProtocolEnum::TCP => EndpointPortConfigProtocolEnum::TCP,
bollard::config::EndpointPortConfigProtocolEnum::UDP => EndpointPortConfigProtocolEnum::UDP,
bollard::config::EndpointPortConfigProtocolEnum::SCTP => EndpointPortConfigProtocolEnum::SCTP,
}),
target_port: port.target_port,
published_port: port.published_port,
publish_mode: port.publish_mode.map(|protocol| match protocol {
bollard::secret::EndpointPortConfigPublishModeEnum::EMPTY => EndpointPortConfigPublishModeEnum::EMPTY,
bollard::secret::EndpointPortConfigPublishModeEnum::INGRESS => EndpointPortConfigPublishModeEnum::INGRESS,
bollard::secret::EndpointPortConfigPublishModeEnum::HOST => EndpointPortConfigPublishModeEnum::HOST,
bollard::config::EndpointPortConfigPublishModeEnum::EMPTY => EndpointPortConfigPublishModeEnum::EMPTY,
bollard::config::EndpointPortConfigPublishModeEnum::INGRESS => EndpointPortConfigPublishModeEnum::INGRESS,
bollard::config::EndpointPortConfigPublishModeEnum::HOST => EndpointPortConfigPublishModeEnum::HOST,
}),
})
.collect()
@@ -423,10 +423,10 @@ fn convert_task_spec_container_spec(
seccomp: privilege.seccomp.map(|seccomp| {
TaskSpecContainerSpecPrivilegesSeccomp {
mode: seccomp.mode.map(|mode| match mode {
bollard::secret::TaskSpecContainerSpecPrivilegesSeccompModeEnum::EMPTY => TaskSpecContainerSpecPrivilegesSeccompModeEnum::EMPTY,
bollard::secret::TaskSpecContainerSpecPrivilegesSeccompModeEnum::DEFAULT => TaskSpecContainerSpecPrivilegesSeccompModeEnum::DEFAULT,
bollard::secret::TaskSpecContainerSpecPrivilegesSeccompModeEnum::UNCONFINED => TaskSpecContainerSpecPrivilegesSeccompModeEnum::UNCONFINED,
bollard::secret::TaskSpecContainerSpecPrivilegesSeccompModeEnum::CUSTOM => TaskSpecContainerSpecPrivilegesSeccompModeEnum::CUSTOM,
bollard::config::TaskSpecContainerSpecPrivilegesSeccompModeEnum::EMPTY => TaskSpecContainerSpecPrivilegesSeccompModeEnum::EMPTY,
bollard::config::TaskSpecContainerSpecPrivilegesSeccompModeEnum::DEFAULT => TaskSpecContainerSpecPrivilegesSeccompModeEnum::DEFAULT,
bollard::config::TaskSpecContainerSpecPrivilegesSeccompModeEnum::UNCONFINED => TaskSpecContainerSpecPrivilegesSeccompModeEnum::UNCONFINED,
bollard::config::TaskSpecContainerSpecPrivilegesSeccompModeEnum::CUSTOM => TaskSpecContainerSpecPrivilegesSeccompModeEnum::CUSTOM,
}),
profile: seccomp.profile,
}
@@ -434,9 +434,9 @@ fn convert_task_spec_container_spec(
app_armor: privilege.app_armor.map(|app_armor| {
TaskSpecContainerSpecPrivilegesAppArmor {
mode: app_armor.mode.map(|mode| match mode {
bollard::secret::TaskSpecContainerSpecPrivilegesAppArmorModeEnum::EMPTY => TaskSpecContainerSpecPrivilegesAppArmorModeEnum::EMPTY,
bollard::secret::TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DEFAULT => TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DEFAULT,
bollard::secret::TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DISABLED => TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DISABLED,
bollard::config::TaskSpecContainerSpecPrivilegesAppArmorModeEnum::EMPTY => TaskSpecContainerSpecPrivilegesAppArmorModeEnum::EMPTY,
bollard::config::TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DEFAULT => TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DEFAULT,
bollard::config::TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DISABLED => TaskSpecContainerSpecPrivilegesAppArmorModeEnum::DISABLED,
}),
}
}),
@@ -478,10 +478,10 @@ fn convert_task_spec_container_spec(
config_name: config.config_name,
}).collect()),
isolation: spec.isolation.map(|isolation| match isolation {
bollard::secret::TaskSpecContainerSpecIsolationEnum::DEFAULT => TaskSpecContainerSpecIsolationEnum::DEFAULT,
bollard::secret::TaskSpecContainerSpecIsolationEnum::PROCESS => TaskSpecContainerSpecIsolationEnum::PROCESS,
bollard::secret::TaskSpecContainerSpecIsolationEnum::HYPERV => TaskSpecContainerSpecIsolationEnum::HYPERV,
bollard::secret::TaskSpecContainerSpecIsolationEnum::EMPTY => TaskSpecContainerSpecIsolationEnum::EMPTY,
bollard::config::TaskSpecContainerSpecIsolationEnum::DEFAULT => TaskSpecContainerSpecIsolationEnum::DEFAULT,
bollard::config::TaskSpecContainerSpecIsolationEnum::PROCESS => TaskSpecContainerSpecIsolationEnum::PROCESS,
bollard::config::TaskSpecContainerSpecIsolationEnum::HYPERV => TaskSpecContainerSpecIsolationEnum::HYPERV,
bollard::config::TaskSpecContainerSpecIsolationEnum::EMPTY => TaskSpecContainerSpecIsolationEnum::EMPTY,
}),
init: spec.init,
sysctls: spec.sysctls,
@@ -492,13 +492,13 @@ fn convert_task_spec_container_spec(
}
fn convert_task_spec_restart_policy_condition(
condition: bollard::secret::TaskSpecRestartPolicyConditionEnum,
condition: bollard::config::TaskSpecRestartPolicyConditionEnum,
) -> TaskSpecRestartPolicyConditionEnum {
match condition {
bollard::secret::TaskSpecRestartPolicyConditionEnum::EMPTY => TaskSpecRestartPolicyConditionEnum::EMPTY,
bollard::secret::TaskSpecRestartPolicyConditionEnum::NONE => TaskSpecRestartPolicyConditionEnum::NONE,
bollard::secret::TaskSpecRestartPolicyConditionEnum::ON_FAILURE => TaskSpecRestartPolicyConditionEnum::ON_FAILURE,
bollard::secret::TaskSpecRestartPolicyConditionEnum::ANY => TaskSpecRestartPolicyConditionEnum::ANY,
bollard::config::TaskSpecRestartPolicyConditionEnum::EMPTY => TaskSpecRestartPolicyConditionEnum::EMPTY,
bollard::config::TaskSpecRestartPolicyConditionEnum::NONE => TaskSpecRestartPolicyConditionEnum::NONE,
bollard::config::TaskSpecRestartPolicyConditionEnum::ON_FAILURE => TaskSpecRestartPolicyConditionEnum::ON_FAILURE,
bollard::config::TaskSpecRestartPolicyConditionEnum::ANY => TaskSpecRestartPolicyConditionEnum::ANY,
}
}
+17 -17
View File
@@ -116,13 +116,13 @@ fn convert_node(node: bollard::models::Node) -> SwarmNode {
leader: manager_status.leader,
reachability: manager_status.reachability.map(
|reachability| match reachability {
bollard::secret::Reachability::UNKNOWN => {
bollard::config::Reachability::UNKNOWN => {
NodeReachability::UNKNOWN
}
bollard::secret::Reachability::UNREACHABLE => {
bollard::config::Reachability::UNREACHABLE => {
NodeReachability::UNREACHABLE
}
bollard::secret::Reachability::REACHABLE => {
bollard::config::Reachability::REACHABLE => {
NodeReachability::REACHABLE
}
},
@@ -143,46 +143,46 @@ fn convert_node_spec(spec: bollard::models::NodeSpec) -> NodeSpec {
}
fn convert_role(
role: bollard::secret::NodeSpecRoleEnum,
role: bollard::config::NodeSpecRoleEnum,
) -> NodeSpecRoleEnum {
match role {
bollard::secret::NodeSpecRoleEnum::EMPTY => {
bollard::config::NodeSpecRoleEnum::EMPTY => {
NodeSpecRoleEnum::EMPTY
}
bollard::secret::NodeSpecRoleEnum::WORKER => {
bollard::config::NodeSpecRoleEnum::WORKER => {
NodeSpecRoleEnum::WORKER
}
bollard::secret::NodeSpecRoleEnum::MANAGER => {
bollard::config::NodeSpecRoleEnum::MANAGER => {
NodeSpecRoleEnum::MANAGER
}
}
}
fn convert_availability(
availability: bollard::secret::NodeSpecAvailabilityEnum,
availability: bollard::config::NodeSpecAvailabilityEnum,
) -> NodeSpecAvailabilityEnum {
match availability {
bollard::secret::NodeSpecAvailabilityEnum::EMPTY => {
bollard::config::NodeSpecAvailabilityEnum::EMPTY => {
NodeSpecAvailabilityEnum::EMPTY
}
bollard::secret::NodeSpecAvailabilityEnum::ACTIVE => {
bollard::config::NodeSpecAvailabilityEnum::ACTIVE => {
NodeSpecAvailabilityEnum::ACTIVE
}
bollard::secret::NodeSpecAvailabilityEnum::PAUSE => {
bollard::config::NodeSpecAvailabilityEnum::PAUSE => {
NodeSpecAvailabilityEnum::PAUSE
}
bollard::secret::NodeSpecAvailabilityEnum::DRAIN => {
bollard::config::NodeSpecAvailabilityEnum::DRAIN => {
NodeSpecAvailabilityEnum::DRAIN
}
}
}
fn convert_state(state: bollard::secret::NodeState) -> NodeState {
fn convert_state(state: bollard::config::NodeState) -> NodeState {
match state {
bollard::secret::NodeState::UNKNOWN => NodeState::UNKNOWN,
bollard::secret::NodeState::DOWN => NodeState::DOWN,
bollard::secret::NodeState::READY => NodeState::READY,
bollard::secret::NodeState::DISCONNECTED => {
bollard::config::NodeState::UNKNOWN => NodeState::UNKNOWN,
bollard::config::NodeState::DOWN => NodeState::DOWN,
bollard::config::NodeState::READY => NodeState::READY,
bollard::config::NodeState::DISCONNECTED => {
NodeState::DISCONNECTED
}
}
+25 -25
View File
@@ -27,7 +27,7 @@ impl DockerClient {
.await
.context("Failed to query for swarm service list")?
.into_iter()
.map(|service| convert_service_list_item(service))
.map(convert_service_list_item)
.collect::<Vec<_>>();
services.sort_by(|a, b| {
@@ -324,17 +324,17 @@ fn convert_service(
parallelism: config.parallelism,
delay: config.delay,
failure_action: config.failure_action.map(|action| match action {
bollard::secret::ServiceSpecUpdateConfigFailureActionEnum::EMPTY => ServiceSpecUpdateConfigFailureActionEnum::EMPTY,
bollard::secret::ServiceSpecUpdateConfigFailureActionEnum::CONTINUE => ServiceSpecUpdateConfigFailureActionEnum::CONTINUE,
bollard::secret::ServiceSpecUpdateConfigFailureActionEnum::PAUSE => ServiceSpecUpdateConfigFailureActionEnum::PAUSE,
bollard::secret::ServiceSpecUpdateConfigFailureActionEnum::ROLLBACK => ServiceSpecUpdateConfigFailureActionEnum::ROLLBACK,
bollard::config::ServiceSpecUpdateConfigFailureActionEnum::EMPTY => ServiceSpecUpdateConfigFailureActionEnum::EMPTY,
bollard::config::ServiceSpecUpdateConfigFailureActionEnum::CONTINUE => ServiceSpecUpdateConfigFailureActionEnum::CONTINUE,
bollard::config::ServiceSpecUpdateConfigFailureActionEnum::PAUSE => ServiceSpecUpdateConfigFailureActionEnum::PAUSE,
bollard::config::ServiceSpecUpdateConfigFailureActionEnum::ROLLBACK => ServiceSpecUpdateConfigFailureActionEnum::ROLLBACK,
}),
monitor: config.monitor,
max_failure_ratio: config.max_failure_ratio,
order: config.order.map(|order| match order {
bollard::secret::ServiceSpecUpdateConfigOrderEnum::EMPTY => ServiceSpecUpdateConfigOrderEnum::EMPTY,
bollard::secret::ServiceSpecUpdateConfigOrderEnum::STOP_FIRST => ServiceSpecUpdateConfigOrderEnum::STOP_FIRST,
bollard::secret::ServiceSpecUpdateConfigOrderEnum::START_FIRST => ServiceSpecUpdateConfigOrderEnum::START_FIRST,
bollard::config::ServiceSpecUpdateConfigOrderEnum::EMPTY => ServiceSpecUpdateConfigOrderEnum::EMPTY,
bollard::config::ServiceSpecUpdateConfigOrderEnum::STOP_FIRST => ServiceSpecUpdateConfigOrderEnum::STOP_FIRST,
bollard::config::ServiceSpecUpdateConfigOrderEnum::START_FIRST => ServiceSpecUpdateConfigOrderEnum::START_FIRST,
}),
}
}),
@@ -343,16 +343,16 @@ fn convert_service(
parallelism: config.parallelism,
delay: config.delay,
failure_action: config.failure_action.map(|action| match action {
bollard::secret::ServiceSpecRollbackConfigFailureActionEnum::EMPTY => ServiceSpecRollbackConfigFailureActionEnum::EMPTY,
bollard::secret::ServiceSpecRollbackConfigFailureActionEnum::CONTINUE => ServiceSpecRollbackConfigFailureActionEnum::CONTINUE,
bollard::secret::ServiceSpecRollbackConfigFailureActionEnum::PAUSE => ServiceSpecRollbackConfigFailureActionEnum::PAUSE,
bollard::config::ServiceSpecRollbackConfigFailureActionEnum::EMPTY => ServiceSpecRollbackConfigFailureActionEnum::EMPTY,
bollard::config::ServiceSpecRollbackConfigFailureActionEnum::CONTINUE => ServiceSpecRollbackConfigFailureActionEnum::CONTINUE,
bollard::config::ServiceSpecRollbackConfigFailureActionEnum::PAUSE => ServiceSpecRollbackConfigFailureActionEnum::PAUSE,
}),
monitor: config.monitor,
max_failure_ratio: config.max_failure_ratio,
order: config.order.map(|order| match order {
bollard::secret::ServiceSpecRollbackConfigOrderEnum::EMPTY => ServiceSpecRollbackConfigOrderEnum::EMPTY,
bollard::secret::ServiceSpecRollbackConfigOrderEnum::STOP_FIRST => ServiceSpecRollbackConfigOrderEnum::STOP_FIRST,
bollard::secret::ServiceSpecRollbackConfigOrderEnum::START_FIRST => ServiceSpecRollbackConfigOrderEnum::START_FIRST,
bollard::config::ServiceSpecRollbackConfigOrderEnum::EMPTY => ServiceSpecRollbackConfigOrderEnum::EMPTY,
bollard::config::ServiceSpecRollbackConfigOrderEnum::STOP_FIRST => ServiceSpecRollbackConfigOrderEnum::STOP_FIRST,
bollard::config::ServiceSpecRollbackConfigOrderEnum::START_FIRST => ServiceSpecRollbackConfigOrderEnum::START_FIRST,
}),
}
}),
@@ -408,13 +408,13 @@ fn convert_endpoint_spec(
) -> EndpointSpec {
EndpointSpec {
mode: spec.mode.map(|mode| match mode {
bollard::secret::EndpointSpecModeEnum::EMPTY => {
bollard::config::EndpointSpecModeEnum::EMPTY => {
EndpointSpecModeEnum::EMPTY
}
bollard::secret::EndpointSpecModeEnum::VIP => {
bollard::config::EndpointSpecModeEnum::VIP => {
EndpointSpecModeEnum::VIP
}
bollard::secret::EndpointSpecModeEnum::DNSRR => {
bollard::config::EndpointSpecModeEnum::DNSRR => {
EndpointSpecModeEnum::DNSRR
}
}),
@@ -423,15 +423,15 @@ fn convert_endpoint_spec(
}
fn convert_state(
state: bollard::secret::ServiceUpdateStatusStateEnum,
state: bollard::config::ServiceUpdateStatusStateEnum,
) -> ServiceUpdateStatusStateEnum {
match state {
bollard::secret::ServiceUpdateStatusStateEnum::EMPTY => ServiceUpdateStatusStateEnum::EMPTY,
bollard::secret::ServiceUpdateStatusStateEnum::UPDATING => ServiceUpdateStatusStateEnum::UPDATING,
bollard::secret::ServiceUpdateStatusStateEnum::PAUSED => ServiceUpdateStatusStateEnum::PAUSED,
bollard::secret::ServiceUpdateStatusStateEnum::COMPLETED => ServiceUpdateStatusStateEnum::COMPLETED,
bollard::secret::ServiceUpdateStatusStateEnum::ROLLBACK_STARTED => ServiceUpdateStatusStateEnum::ROLLBACK_STARTED,
bollard::secret::ServiceUpdateStatusStateEnum::ROLLBACK_PAUSED => ServiceUpdateStatusStateEnum::ROLLBACK_PAUSED,
bollard::secret::ServiceUpdateStatusStateEnum::ROLLBACK_COMPLETED => ServiceUpdateStatusStateEnum::ROLLBACK_COMPLETED,
bollard::config::ServiceUpdateStatusStateEnum::EMPTY => ServiceUpdateStatusStateEnum::EMPTY,
bollard::config::ServiceUpdateStatusStateEnum::UPDATING => ServiceUpdateStatusStateEnum::UPDATING,
bollard::config::ServiceUpdateStatusStateEnum::PAUSED => ServiceUpdateStatusStateEnum::PAUSED,
bollard::config::ServiceUpdateStatusStateEnum::COMPLETED => ServiceUpdateStatusStateEnum::COMPLETED,
bollard::config::ServiceUpdateStatusStateEnum::ROLLBACK_STARTED => ServiceUpdateStatusStateEnum::ROLLBACK_STARTED,
bollard::config::ServiceUpdateStatusStateEnum::ROLLBACK_PAUSED => ServiceUpdateStatusStateEnum::ROLLBACK_PAUSED,
bollard::config::ServiceUpdateStatusStateEnum::ROLLBACK_COMPLETED => ServiceUpdateStatusStateEnum::ROLLBACK_COMPLETED,
}
}
+2 -2
View File
@@ -60,8 +60,8 @@ fn convert_swarm_info(
.into_iter()
.map(|cas| SwarmSpecCaConfigExternalCas {
protocol: cas.protocol.map(|protocol| match protocol {
bollard::secret::SwarmSpecCaConfigExternalCasProtocolEnum::EMPTY => SwarmSpecCaConfigExternalCasProtocolEnum::EMPTY,
bollard::secret::SwarmSpecCaConfigExternalCasProtocolEnum::CFSSL => SwarmSpecCaConfigExternalCasProtocolEnum::CFSSL,
bollard::config::SwarmSpecCaConfigExternalCasProtocolEnum::EMPTY => SwarmSpecCaConfigExternalCasProtocolEnum::EMPTY,
bollard::config::SwarmSpecCaConfigExternalCasProtocolEnum::CFSSL => SwarmSpecCaConfigExternalCasProtocolEnum::CFSSL,
}),
url: cas.url,
options: cas.options,
+15 -15
View File
@@ -135,20 +135,20 @@ fn convert_task_state(
state: bollard::models::TaskState,
) -> TaskState {
match state {
bollard::secret::TaskState::NEW => TaskState::NEW,
bollard::secret::TaskState::ALLOCATED => TaskState::ALLOCATED,
bollard::secret::TaskState::PENDING => TaskState::PENDING,
bollard::secret::TaskState::ASSIGNED => TaskState::ASSIGNED,
bollard::secret::TaskState::ACCEPTED => TaskState::ACCEPTED,
bollard::secret::TaskState::PREPARING => TaskState::PREPARING,
bollard::secret::TaskState::READY => TaskState::READY,
bollard::secret::TaskState::STARTING => TaskState::STARTING,
bollard::secret::TaskState::RUNNING => TaskState::RUNNING,
bollard::secret::TaskState::COMPLETE => TaskState::COMPLETE,
bollard::secret::TaskState::SHUTDOWN => TaskState::SHUTDOWN,
bollard::secret::TaskState::FAILED => TaskState::FAILED,
bollard::secret::TaskState::REJECTED => TaskState::REJECTED,
bollard::secret::TaskState::REMOVE => TaskState::REMOVE,
bollard::secret::TaskState::ORPHANED => TaskState::ORPHANED,
bollard::config::TaskState::NEW => TaskState::NEW,
bollard::config::TaskState::ALLOCATED => TaskState::ALLOCATED,
bollard::config::TaskState::PENDING => TaskState::PENDING,
bollard::config::TaskState::ASSIGNED => TaskState::ASSIGNED,
bollard::config::TaskState::ACCEPTED => TaskState::ACCEPTED,
bollard::config::TaskState::PREPARING => TaskState::PREPARING,
bollard::config::TaskState::READY => TaskState::READY,
bollard::config::TaskState::STARTING => TaskState::STARTING,
bollard::config::TaskState::RUNNING => TaskState::RUNNING,
bollard::config::TaskState::COMPLETE => TaskState::COMPLETE,
bollard::config::TaskState::SHUTDOWN => TaskState::SHUTDOWN,
bollard::config::TaskState::FAILED => TaskState::FAILED,
bollard::config::TaskState::REJECTED => TaskState::REJECTED,
bollard::config::TaskState::REMOVE => TaskState::REMOVE,
bollard::config::TaskState::ORPHANED => TaskState::ORPHANED,
}
}
+23 -23
View File
@@ -21,13 +21,13 @@ impl DockerClient {
let scope = volume
.scope
.map(|scope| match scope {
bollard::secret::VolumeScopeEnum::EMPTY => {
bollard::config::VolumeScopeEnum::EMPTY => {
VolumeScopeEnum::Empty
}
bollard::secret::VolumeScopeEnum::LOCAL => {
bollard::config::VolumeScopeEnum::LOCAL => {
VolumeScopeEnum::Local
}
bollard::secret::VolumeScopeEnum::GLOBAL => {
bollard::config::VolumeScopeEnum::GLOBAL => {
VolumeScopeEnum::Global
}
})
@@ -69,13 +69,13 @@ impl DockerClient {
scope: volume
.scope
.map(|scope| match scope {
bollard::secret::VolumeScopeEnum::EMPTY => {
bollard::config::VolumeScopeEnum::EMPTY => {
VolumeScopeEnum::Empty
}
bollard::secret::VolumeScopeEnum::LOCAL => {
bollard::config::VolumeScopeEnum::LOCAL => {
VolumeScopeEnum::Local
}
bollard::secret::VolumeScopeEnum::GLOBAL => {
bollard::config::VolumeScopeEnum::GLOBAL => {
VolumeScopeEnum::Global
}
})
@@ -91,16 +91,16 @@ impl DockerClient {
access_mode: spec.access_mode.map(|mode| {
ClusterVolumeSpecAccessMode {
scope: mode.scope.map(|scope| match scope {
bollard::secret::ClusterVolumeSpecAccessModeScopeEnum::EMPTY => ClusterVolumeSpecAccessModeScopeEnum::Empty,
bollard::secret::ClusterVolumeSpecAccessModeScopeEnum::SINGLE => ClusterVolumeSpecAccessModeScopeEnum::Single,
bollard::secret::ClusterVolumeSpecAccessModeScopeEnum::MULTI => ClusterVolumeSpecAccessModeScopeEnum::Multi,
bollard::config::ClusterVolumeSpecAccessModeScopeEnum::EMPTY => ClusterVolumeSpecAccessModeScopeEnum::Empty,
bollard::config::ClusterVolumeSpecAccessModeScopeEnum::SINGLE => ClusterVolumeSpecAccessModeScopeEnum::Single,
bollard::config::ClusterVolumeSpecAccessModeScopeEnum::MULTI => ClusterVolumeSpecAccessModeScopeEnum::Multi,
}).unwrap_or_default(),
sharing: mode.sharing.map(|sharing| match sharing {
bollard::secret::ClusterVolumeSpecAccessModeSharingEnum::EMPTY => ClusterVolumeSpecAccessModeSharingEnum::Empty,
bollard::secret::ClusterVolumeSpecAccessModeSharingEnum::NONE => ClusterVolumeSpecAccessModeSharingEnum::None,
bollard::secret::ClusterVolumeSpecAccessModeSharingEnum::READONLY => ClusterVolumeSpecAccessModeSharingEnum::Readonly,
bollard::secret::ClusterVolumeSpecAccessModeSharingEnum::ONEWRITER => ClusterVolumeSpecAccessModeSharingEnum::Onewriter,
bollard::secret::ClusterVolumeSpecAccessModeSharingEnum::ALL => ClusterVolumeSpecAccessModeSharingEnum::All,
bollard::config::ClusterVolumeSpecAccessModeSharingEnum::EMPTY => ClusterVolumeSpecAccessModeSharingEnum::Empty,
bollard::config::ClusterVolumeSpecAccessModeSharingEnum::NONE => ClusterVolumeSpecAccessModeSharingEnum::None,
bollard::config::ClusterVolumeSpecAccessModeSharingEnum::READONLY => ClusterVolumeSpecAccessModeSharingEnum::Readonly,
bollard::config::ClusterVolumeSpecAccessModeSharingEnum::ONEWRITER => ClusterVolumeSpecAccessModeSharingEnum::Onewriter,
bollard::config::ClusterVolumeSpecAccessModeSharingEnum::ALL => ClusterVolumeSpecAccessModeSharingEnum::All,
}).unwrap_or_default(),
secrets: mode.secrets.unwrap_or_default().into_iter().map(|secret| ClusterVolumeSpecAccessModeSecrets {
key: secret.key,
@@ -116,10 +116,10 @@ impl DockerClient {
limit_bytes: range.limit_bytes,
}),
availability: mode.availability.map(|availability| match availability {
bollard::secret::ClusterVolumeSpecAccessModeAvailabilityEnum::EMPTY => ClusterVolumeSpecAccessModeAvailabilityEnum::Empty,
bollard::secret::ClusterVolumeSpecAccessModeAvailabilityEnum::ACTIVE => ClusterVolumeSpecAccessModeAvailabilityEnum::Active,
bollard::secret::ClusterVolumeSpecAccessModeAvailabilityEnum::PAUSE => ClusterVolumeSpecAccessModeAvailabilityEnum::Pause,
bollard::secret::ClusterVolumeSpecAccessModeAvailabilityEnum::DRAIN => ClusterVolumeSpecAccessModeAvailabilityEnum::Drain,
bollard::config::ClusterVolumeSpecAccessModeAvailabilityEnum::EMPTY => ClusterVolumeSpecAccessModeAvailabilityEnum::Empty,
bollard::config::ClusterVolumeSpecAccessModeAvailabilityEnum::ACTIVE => ClusterVolumeSpecAccessModeAvailabilityEnum::Active,
bollard::config::ClusterVolumeSpecAccessModeAvailabilityEnum::PAUSE => ClusterVolumeSpecAccessModeAvailabilityEnum::Pause,
bollard::config::ClusterVolumeSpecAccessModeAvailabilityEnum::DRAIN => ClusterVolumeSpecAccessModeAvailabilityEnum::Drain,
}).unwrap_or_default(),
}
}),
@@ -137,11 +137,11 @@ impl DockerClient {
.map(|status| ClusterVolumePublishStatus {
node_id: status.node_id,
state: status.state.map(|state| match state {
bollard::secret::ClusterVolumePublishStatusStateEnum::EMPTY => ClusterVolumePublishStatusStateEnum::Empty,
bollard::secret::ClusterVolumePublishStatusStateEnum::PENDING_PUBLISH => ClusterVolumePublishStatusStateEnum::PendingPublish,
bollard::secret::ClusterVolumePublishStatusStateEnum::PUBLISHED => ClusterVolumePublishStatusStateEnum::Published,
bollard::secret::ClusterVolumePublishStatusStateEnum::PENDING_NODE_UNPUBLISH => ClusterVolumePublishStatusStateEnum::PendingNodeUnpublish,
bollard::secret::ClusterVolumePublishStatusStateEnum::PENDING_CONTROLLER_UNPUBLISH => ClusterVolumePublishStatusStateEnum::PendingControllerUnpublish,
bollard::config::ClusterVolumePublishStatusStateEnum::EMPTY => ClusterVolumePublishStatusStateEnum::Empty,
bollard::config::ClusterVolumePublishStatusStateEnum::PENDING_PUBLISH => ClusterVolumePublishStatusStateEnum::PendingPublish,
bollard::config::ClusterVolumePublishStatusStateEnum::PUBLISHED => ClusterVolumePublishStatusStateEnum::Published,
bollard::config::ClusterVolumePublishStatusStateEnum::PENDING_NODE_UNPUBLISH => ClusterVolumePublishStatusStateEnum::PendingNodeUnpublish,
bollard::config::ClusterVolumePublishStatusStateEnum::PENDING_CONTROLLER_UNPUBLISH => ClusterVolumePublishStatusStateEnum::PendingControllerUnpublish,
}).unwrap_or_default(),
publish_context: status.publish_context.unwrap_or_default(),
})
+6 -9
View File
@@ -17,20 +17,17 @@
//! from the Komodo UI Settings page.
//!
//! To call the api, construct JSON bodies following
//! the schemas given in [read], [mod@write], [execute], and so on.
//! the schemas in [read], [mod@write], [execute], and so on.
//!
//! For example, this is an example body for [read::GetDeployment]:
//! ```json
//! {
//! "type": "GetDeployment",
//! "params": {
//! "deployment": "66113df3abe32960b87018dd"
//! }
//! "deployment": "my-deployment"
//! }
//! ```
//!
//! The request's parent module (eg. [read], [mod@write]) determines the http path which
//! must be used for the requests. For example, requests under [read] are made using http path `/read`.
//! The request's parent module (eg. [read], [mod@write]) and name determines the http path which
//! must be used for the requests. For example, requests under [read] are made using http path `/read/{REQUEST_NAME}`.
//!
//! ## Curl Example
//!
@@ -40,8 +37,8 @@
//! curl --header "Content-Type: application/json" \
//! --header "X-Api-Key: your_api_key" \
//! --header "X-Api-Secret: your_api_secret" \
//! --data '{ "type": "UpdateBuild", "params": { "id": "67076689ed600cfdd52ac637", "config": { "version": "1.15.9" } } }' \
//! https://komodo.example.com/write
//! --data '{ "id": "my-build", "config": { "version": "2.0.0" } }' \
//! https://komodo.example.com/write/UpdateBuild
//! ```
//!
//! ## Modules
+7 -1
View File
@@ -2,7 +2,7 @@ use mogh_resolver::Resolve;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::ResourceTarget;
use crate::entities::{ResourceTarget, toml::ResourcesToml};
use super::KomodoReadRequest;
@@ -54,6 +54,9 @@ pub struct ExportAllResourcesToToml {
/// Default: false
#[serde(default)]
pub include_user_groups: bool,
/// Pass an existing [ResourcesToml] to preserve
/// the meta configuration.
pub existing: Option<ResourcesToml>,
}
fn default_include_resources() -> bool {
@@ -95,6 +98,9 @@ pub struct ExportResourcesToToml {
/// Whether to include variables
#[serde(default)]
pub include_variables: bool,
/// Pass an existing [ResourcesToml] to preserve
/// the meta configuration.
pub existing: Option<ResourcesToml>,
}
#[typeshare]

Some files were not shown because too many files have changed in this diff Show More