mirror of
https://github.com/moghtech/komodo.git
synced 2026-08-10 01:29:51 -05:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20b9d16d4b | ||
|
|
7c95823eb0 | ||
|
|
254622078e | ||
|
|
38421d6719 | ||
|
|
0f96794559 | ||
|
|
d79fefff17 | ||
|
|
4626b4a477 | ||
|
|
150a8a160d | ||
|
|
5b3bb9b8bb | ||
|
|
79831f29af | ||
|
|
b68f5abd25 | ||
|
|
2325899ebf | ||
|
|
23fd208f0f | ||
|
|
19904e00b9 | ||
|
|
8880b1c948 | ||
|
|
e47c8d2b62 | ||
|
|
1addb8fccd | ||
|
|
08e55b5180 |
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
@@ -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
|
||||
Vendored
+14
-14
@@ -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
File diff suppressed because it is too large
Load Diff
+38
-38
@@ -8,7 +8,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "2.0.0-dev-114"
|
||||
version = "2.1.2"
|
||||
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.5"
|
||||
mogh_logger = "1.3.3"
|
||||
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.2"
|
||||
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.23.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.1"
|
||||
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,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.1-bookworm AS builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
## Uses chef for dependency caching to help speed up back-to-back builds.
|
||||
|
||||
FROM lukemathwalker/cargo-chef:latest-rust-1.90.0-bookworm AS chef
|
||||
FROM lukemathwalker/cargo-chef:latest-rust-1.94.1-bookworm AS chef
|
||||
WORKDIR /builder
|
||||
|
||||
# Plan just the RECIPE to see if things have changed
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
FROM rust:1.93.0-bullseye AS builder
|
||||
FROM rust:1.94.1-bullseye AS builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## Assumes the latest binaries for x86_64 and aarch64 are already built (by binaries.Dockerfile).
|
||||
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries: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,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
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
+131
-550
@@ -28,558 +28,139 @@ pub async fn handle(
|
||||
}
|
||||
|
||||
println!("\n{}: Execution", "Mode".dimmed());
|
||||
match execution {
|
||||
Execution::None(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunAction(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchRunAction(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunProcedure(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchRunProcedure(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchRunBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CancelBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::Deploy(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDeploy(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PullDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DestroyDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDestroyDeployment(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CloneRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchCloneRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PullRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchPullRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BuildRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchBuildRepo(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CancelRepoBuild(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DestroyContainer(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopAllContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneContainers(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeleteNetwork(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneNetworks(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeleteImage(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneImages(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeleteVolume(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneVolumes(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneDockerBuilders(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneBuildx(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PruneSystem(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunSync(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CommitSync(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeployStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDeployStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DeployStackIfChanged(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PullStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchPullStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StartStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RestartStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::PauseStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::UnpauseStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::StopStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::DestroyStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BatchDestroyStack(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RunStackService(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::TestAlerter(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::SendAlert(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmNodes(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmStacks(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmServices(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CreateSwarmConfig(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateSwarmConfig(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::CreateSwarmSecret(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateSwarmSecret(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::ClearRepoCache(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::BackupCoreDatabase(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::GlobalAutoUpdate(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateAllServerKeys(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::RotateCoreKeys(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
Execution::Sleep(data) => {
|
||||
println!("{}: {data:?}", "Data".dimmed())
|
||||
}
|
||||
|
||||
macro_rules! handle_execution {
|
||||
(
|
||||
execute: [$($ExecVariant:ident),* $(,)?],
|
||||
batch: [$($BatchVariant:ident),* $(,)?],
|
||||
) => {{
|
||||
// Print data
|
||||
match execution {
|
||||
$(Execution::$ExecVariant(data) => println!("{}: {data:?}", "Data".dimmed()),)*
|
||||
$(Execution::$BatchVariant(data) => println!("{}: {data:?}", "Data".dimmed()),)*
|
||||
Execution::CommitSync(data) => println!("{}: {data:?}", "Data".dimmed()),
|
||||
Execution::Sleep(data) => println!("{}: {data:?}", "Data".dimmed()),
|
||||
Execution::None(data) => println!("{}: {data:?}", "Data".dimmed()),
|
||||
}
|
||||
|
||||
$crate::command::wait_for_enter("run execution", yes)?;
|
||||
|
||||
info!("Running Execution...");
|
||||
|
||||
let client = $crate::command::komodo_client().await?;
|
||||
|
||||
// Execute and get result
|
||||
match execution.clone() {
|
||||
$(
|
||||
Execution::$ExecVariant(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
)*
|
||||
$(
|
||||
Execution::$BatchVariant(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
)*
|
||||
Execution::CommitSync(request) => client
|
||||
.write(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Sleep(request) => {
|
||||
let duration =
|
||||
Duration::from_millis(request.duration_ms as u64);
|
||||
tokio::time::sleep(duration).await;
|
||||
println!("Finished sleeping!");
|
||||
std::process::exit(0)
|
||||
}
|
||||
Execution::None(_) => unreachable!(),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
super::wait_for_enter("run execution", yes)?;
|
||||
|
||||
info!("Running Execution...");
|
||||
|
||||
let client = super::komodo_client().await?;
|
||||
|
||||
let res = match execution.clone() {
|
||||
Execution::RunAction(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunAction(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::RunProcedure(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunProcedure(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::RunBuild(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchRunBuild(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::CancelBuild(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Deploy(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeploy(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::PullDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyDeployment(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDestroyDeployment(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::CloneRepo(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchCloneRepo(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::PullRepo(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchPullRepo(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::BuildRepo(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchBuildRepo(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::CancelRepoBuild(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyContainer(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StartAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopAllContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneContainers(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteNetwork(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneNetworks(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteImage(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneImages(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeleteVolume(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneVolumes(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneDockerBuilders(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneBuildx(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PruneSystem(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RunSync(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::CommitSync(request) => client
|
||||
.write(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DeployStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeployStack(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::DeployStackIfChanged(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDeployStackIfChanged(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::PullStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchPullStack(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::StartStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RestartStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::PauseStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::UnpauseStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::StopStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::DestroyStack(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BatchDestroyStack(request) => {
|
||||
client.execute(request).await.map(ExecutionResult::Batch)
|
||||
}
|
||||
Execution::RunStackService(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::TestAlerter(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::SendAlert(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmNodes(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmStacks(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmServices(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::CreateSwarmConfig(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RotateSwarmConfig(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmConfigs(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::CreateSwarmSecret(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RotateSwarmSecret(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RemoveSwarmSecrets(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::ClearRepoCache(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::BackupCoreDatabase(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::GlobalAutoUpdate(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RotateAllServerKeys(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::RotateCoreKeys(request) => client
|
||||
.execute(request)
|
||||
.await
|
||||
.map(|u| ExecutionResult::Single(u.into())),
|
||||
Execution::Sleep(request) => {
|
||||
let duration =
|
||||
Duration::from_millis(request.duration_ms as u64);
|
||||
tokio::time::sleep(duration).await;
|
||||
println!("Finished sleeping!");
|
||||
std::process::exit(0)
|
||||
}
|
||||
Execution::None(_) => unreachable!(),
|
||||
};
|
||||
let res = handle_execution!(
|
||||
execute: [
|
||||
RunAction,
|
||||
RunProcedure,
|
||||
RunBuild,
|
||||
CancelBuild,
|
||||
Deploy,
|
||||
PullDeployment,
|
||||
StartDeployment,
|
||||
RestartDeployment,
|
||||
PauseDeployment,
|
||||
UnpauseDeployment,
|
||||
StopDeployment,
|
||||
DestroyDeployment,
|
||||
CloneRepo,
|
||||
PullRepo,
|
||||
BuildRepo,
|
||||
CancelRepoBuild,
|
||||
StartContainer,
|
||||
RestartContainer,
|
||||
PauseContainer,
|
||||
UnpauseContainer,
|
||||
StopContainer,
|
||||
DestroyContainer,
|
||||
StartAllContainers,
|
||||
RestartAllContainers,
|
||||
PauseAllContainers,
|
||||
UnpauseAllContainers,
|
||||
StopAllContainers,
|
||||
PruneContainers,
|
||||
DeleteNetwork,
|
||||
PruneNetworks,
|
||||
DeleteImage,
|
||||
PruneImages,
|
||||
DeleteVolume,
|
||||
PruneVolumes,
|
||||
PruneDockerBuilders,
|
||||
PruneBuildx,
|
||||
PruneSystem,
|
||||
RunSync,
|
||||
DeployStack,
|
||||
DeployStackIfChanged,
|
||||
PullStack,
|
||||
StartStack,
|
||||
RestartStack,
|
||||
PauseStack,
|
||||
UnpauseStack,
|
||||
StopStack,
|
||||
DestroyStack,
|
||||
RunStackService,
|
||||
TestAlerter,
|
||||
SendAlert,
|
||||
RemoveSwarmNodes,
|
||||
UpdateSwarmNode,
|
||||
RemoveSwarmStacks,
|
||||
RemoveSwarmServices,
|
||||
CreateSwarmConfig,
|
||||
RotateSwarmConfig,
|
||||
RemoveSwarmConfigs,
|
||||
CreateSwarmSecret,
|
||||
RotateSwarmSecret,
|
||||
RemoveSwarmSecrets,
|
||||
ClearRepoCache,
|
||||
BackupCoreDatabase,
|
||||
GlobalAutoUpdate,
|
||||
RotateAllServerKeys,
|
||||
RotateCoreKeys,
|
||||
],
|
||||
batch: [
|
||||
BatchRunAction,
|
||||
BatchRunProcedure,
|
||||
BatchRunBuild,
|
||||
BatchDeploy,
|
||||
BatchDestroyDeployment,
|
||||
BatchCloneRepo,
|
||||
BatchPullRepo,
|
||||
BatchBuildRepo,
|
||||
BatchDeployStack,
|
||||
BatchDeployStackIfChanged,
|
||||
BatchPullStack,
|
||||
BatchDestroyStack,
|
||||
],
|
||||
);
|
||||
|
||||
match res {
|
||||
Ok(ExecutionResult::Single(update)) => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2,7 +2,7 @@ use anyhow::{Context, anyhow};
|
||||
use colored::Colorize;
|
||||
use komodo_client::{
|
||||
api::{
|
||||
read::{ListAllDockerContainers, ListServers},
|
||||
read::{GetServer, ListAllDockerContainers, ListServers},
|
||||
terminal::InitTerminal,
|
||||
},
|
||||
entities::{
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -132,11 +138,14 @@ async fn get_server(
|
||||
}
|
||||
|
||||
if containers.len() == 1 {
|
||||
return containers
|
||||
let server_id = containers
|
||||
.pop()
|
||||
.context("Shouldn't happen")?
|
||||
.server_id
|
||||
.context("Container doesn't have server_id");
|
||||
.context("Container doesn't have server_id")?;
|
||||
let server_name =
|
||||
client.read(GetServer { server: server_id }).await?.name;
|
||||
return Ok(server_name);
|
||||
}
|
||||
|
||||
let servers = containers
|
||||
@@ -163,6 +172,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 +257,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 +317,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")?;
|
||||
|
||||
@@ -264,6 +264,9 @@ pub fn cli_config() -> &'static CliConfig {
|
||||
ansi: env
|
||||
.komodo_cli_logging_ansi
|
||||
.unwrap_or(config.cli_logging.ansi),
|
||||
timestamps: env
|
||||
.komodo_cli_logging_timestamps
|
||||
.unwrap_or(config.cli_logging.timestamps),
|
||||
otlp_endpoint: env
|
||||
.komodo_cli_logging_otlp_endpoint
|
||||
.unwrap_or(config.cli_logging.otlp_endpoint),
|
||||
|
||||
@@ -12,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
|
||||
}
|
||||
|
||||
@@ -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.1-trixie AS core-builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
@@ -17,13 +17,13 @@ RUN cargo build -p komodo_core --release && \
|
||||
cargo build -p komodo_cli --release && \
|
||||
cargo strip
|
||||
|
||||
# Build 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,12 +20,14 @@ pub async fn send_alert(
|
||||
let link = resource_link(ResourceTargetVariant::Swarm, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!("{level} | Swarm **{name}** is now **healthy**\n{link}")
|
||||
format!(
|
||||
"{level} | Swarm **{name}** is now **healthy**\n{link}"
|
||||
)
|
||||
}
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\n**error**: {e}"))
|
||||
.map(|e| format!("\n**error**: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
format!(
|
||||
"{level} | Swarm **{name}** is **unhealthy** ❌\n{link}{err}"
|
||||
@@ -34,28 +36,6 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Periphery version now matches Core version ✅\n{link}"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Version mismatch detected ⚠️\nPeriphery: **{server_version}** | Core: **{core_version}**\n{link}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
@@ -82,6 +62,28 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Periphery version now matches Core version ✅\n{link}"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | **{name}**{region} | Version mismatch detected ⚠️\nPeriphery: **{server_version}** | Core: **{core_version}**\n{link}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AlertData::ServerCpu {
|
||||
id,
|
||||
name,
|
||||
@@ -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}"
|
||||
|
||||
+32
-30
@@ -260,35 +260,15 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
SeverityLevel::Critical => {
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e}"))
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
format!("{level} | Swarm {name} is unhealthy ❌\n{link}{err}")
|
||||
format!(
|
||||
"{level} | Swarm {name} is unhealthy ❌\n{link}{err}"
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | {name}{region} | Periphery version now matches Core version ✅\n{link}"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | {name}{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}\n{link}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
@@ -313,6 +293,28 @@ fn standard_alert_content(alert: &Alert) -> String {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let link = resource_link(ResourceTargetVariant::Server, id);
|
||||
match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | {name}{region} | Periphery version now matches Core version ✅\n{link}"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | {name}{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}\n{link}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
AlertData::ServerCpu {
|
||||
id,
|
||||
name,
|
||||
@@ -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}",
|
||||
|
||||
+36
-36
@@ -42,7 +42,7 @@ pub async fn send_alert(
|
||||
format!("{level} | Swarm *{name}* is *unhealthy* ❌");
|
||||
let err = err
|
||||
.as_ref()
|
||||
.map(|e| format!("\nerror: {e}"))
|
||||
.map(|e| format!("\nerror: {e:#?}"))
|
||||
.unwrap_or_default();
|
||||
let blocks = vec![
|
||||
Block::header(level),
|
||||
@@ -59,35 +59,6 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let text = match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Periphery version now matches Core version ✅"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}"
|
||||
)
|
||||
}
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Server,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::ServerUnreachable {
|
||||
id,
|
||||
name,
|
||||
@@ -129,6 +100,35 @@ pub async fn send_alert(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AlertData::ServerVersionMismatch {
|
||||
id,
|
||||
name,
|
||||
region,
|
||||
server_version,
|
||||
core_version,
|
||||
} => {
|
||||
let region = fmt_region(region);
|
||||
let text = match alert.level {
|
||||
SeverityLevel::Ok => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Periphery version now matches Core version ✅"
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
format!(
|
||||
"{level} | *{name}*{region} | Version mismatch detected ⚠️\nPeriphery: {server_version} | Core: {core_version}"
|
||||
)
|
||||
}
|
||||
};
|
||||
let blocks = vec![
|
||||
Block::header(text.clone()),
|
||||
Block::section(resource_link(
|
||||
ResourceTargetVariant::Server,
|
||||
id,
|
||||
)),
|
||||
];
|
||||
(text, blocks.into())
|
||||
}
|
||||
AlertData::ServerCpu {
|
||||
id,
|
||||
name,
|
||||
@@ -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()),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -15,7 +15,7 @@ use komodo_client::{
|
||||
},
|
||||
entities::{
|
||||
SwarmOrServer, deployment::DeploymentState, server::ServerState,
|
||||
stack::StackState,
|
||||
stack::StackState, swarm::SwarmState,
|
||||
},
|
||||
};
|
||||
use mogh_error::AddStatusCodeError;
|
||||
@@ -39,7 +39,7 @@ use crate::{
|
||||
resource::rotate_server_keys,
|
||||
state::{
|
||||
db_client, deployment_status_cache, server_status_cache,
|
||||
stack_status_cache,
|
||||
stack_status_cache, swarm_status_cache,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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(
|
||||
@@ -232,6 +244,7 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
.context("Failed to query for stacks from database")?;
|
||||
|
||||
let server_status_cache = server_status_cache();
|
||||
let swarm_status_cache = swarm_status_cache();
|
||||
let stack_status_cache = stack_status_cache();
|
||||
|
||||
// Will be edited later at update.logs[0]
|
||||
@@ -243,7 +256,7 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only pull running stacks.
|
||||
// Only check running stacks.
|
||||
if !matches!(status.curr.state, StackState::Running) {
|
||||
continue;
|
||||
}
|
||||
@@ -255,43 +268,58 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
&servers,
|
||||
)?;
|
||||
|
||||
if let SwarmOrServer::None = &swarm_or_server {
|
||||
continue;
|
||||
// Ensure server / swarm is reachable
|
||||
match &swarm_or_server {
|
||||
SwarmOrServer::None => continue,
|
||||
SwarmOrServer::Server(server) => {
|
||||
if !server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
if !swarm_status_cache
|
||||
.get(&swarm.id)
|
||||
.await
|
||||
.map(|s| {
|
||||
matches!(
|
||||
s.state,
|
||||
SwarmState::Healthy | SwarmState::Unhealthy
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(server) =
|
||||
servers.iter().find(|s| s.id == stack.config.server_id)
|
||||
// This check is probably redundant along with running check
|
||||
// but shouldn't hurt
|
||||
&& server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
if let Err(e) = check_stack_for_update_inner(
|
||||
stack.id,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Err(e) = check_stack_for_update_inner(
|
||||
stack.id,
|
||||
&swarm_or_server,
|
||||
self.skip_auto_update,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
update.push_error_log(
|
||||
&format!("Check Stack {}", stack.name),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
}
|
||||
|
||||
update.logs[0].stdout.push_str(&format!(
|
||||
"Checked Stack {} ✅",
|
||||
bold(&stack.name)
|
||||
));
|
||||
update.push_error_log(
|
||||
&format!("Check Stack {}", stack.name),
|
||||
format_serror(&e.into()),
|
||||
);
|
||||
} else {
|
||||
if !update.logs[0].stdout.is_empty() {
|
||||
update.logs[0].stdout.push('\n');
|
||||
}
|
||||
|
||||
update.logs[0].stdout.push_str(&format!(
|
||||
"Checked Stack {} ✅",
|
||||
bold(&stack.name)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +336,7 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Only pull running deployments.
|
||||
// Only check running deployments.
|
||||
if !matches!(status.curr.state, DeploymentState::Running) {
|
||||
continue;
|
||||
}
|
||||
@@ -320,8 +348,34 @@ impl Resolve<ExecuteArgs> for GlobalAutoUpdate {
|
||||
&servers,
|
||||
)?;
|
||||
|
||||
if let SwarmOrServer::None = &swarm_or_server {
|
||||
continue;
|
||||
// Ensure server / swarm is reachable
|
||||
match &swarm_or_server {
|
||||
SwarmOrServer::None => continue,
|
||||
SwarmOrServer::Server(server) => {
|
||||
if !server_status_cache
|
||||
.get(&server.id)
|
||||
.await
|
||||
.map(|s| matches!(s.state, ServerState::Ok))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
SwarmOrServer::Swarm(swarm) => {
|
||||
if !swarm_status_cache
|
||||
.get(&swarm.id)
|
||||
.await
|
||||
.map(|s| {
|
||||
matches!(
|
||||
s.state,
|
||||
SwarmState::Healthy | SwarmState::Unhealthy
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let name = deployment.name.clone();
|
||||
@@ -368,14 +422,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 +540,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 +548,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(
|
||||
|
||||
@@ -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)]
|
||||
@@ -148,6 +148,7 @@ pub enum ExecuteRequest {
|
||||
|
||||
// ==== SWARM ====
|
||||
RemoveSwarmNodes(RemoveSwarmNodes),
|
||||
UpdateSwarmNode(UpdateSwarmNode),
|
||||
RemoveSwarmStacks(RemoveSwarmStacks),
|
||||
RemoveSwarmServices(RemoveSwarmServices),
|
||||
CreateSwarmConfig(CreateSwarmConfig),
|
||||
@@ -249,11 +250,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 +277,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 +286,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 +305,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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use formatting::format_serror;
|
||||
use komodo_client::{
|
||||
api::execute::{
|
||||
CreateSwarmConfig, CreateSwarmSecret, RemoveSwarmConfigs,
|
||||
RemoveSwarmNodes, RemoveSwarmSecrets, RemoveSwarmServices,
|
||||
RemoveSwarmStacks, RotateSwarmConfig, RotateSwarmSecret,
|
||||
},
|
||||
api::execute::*,
|
||||
entities::{permission::PermissionLevel, swarm::Swarm},
|
||||
};
|
||||
use mogh_resolver::Resolve;
|
||||
@@ -12,7 +8,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 +17,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 +27,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 +55,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",
|
||||
@@ -72,12 +72,78 @@ impl Resolve<ExecuteArgs> for RemoveSwarmNodes {
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for UpdateSwarmNode {
|
||||
#[instrument(
|
||||
"UpdateSwarmNode",
|
||||
skip_all,
|
||||
fields(
|
||||
task_id = task_id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
node = self.node,
|
||||
availability = format!("{:?}", self.availability),
|
||||
label_add = format!("{:?}", self.label_add),
|
||||
label_rm = format!("{:?}", self.label_rm),
|
||||
role = format!("{:?}", self.role),
|
||||
)
|
||||
)]
|
||||
async fn resolve(
|
||||
self,
|
||||
ExecuteArgs {
|
||||
user,
|
||||
update,
|
||||
task_id,
|
||||
}: &ExecuteArgs,
|
||||
) -> Result<Self::Response, Self::Error> {
|
||||
let swarm = get_check_permissions::<Swarm>(
|
||||
&self.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
let mut update = update.clone();
|
||||
|
||||
match swarm_request(
|
||||
&swarm.config.server_ids,
|
||||
periphery_client::api::swarm::UpdateSwarmNode {
|
||||
node: self.node,
|
||||
availability: self.availability,
|
||||
label_add: self.label_add,
|
||||
label_rm: self.label_rm,
|
||||
role: self.role,
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(log) => {
|
||||
update.logs.push(log);
|
||||
refresh_swarm_cache(&swarm, true).await;
|
||||
}
|
||||
Err(e) => update.push_error_log(
|
||||
"Update Swarm Node",
|
||||
format_serror(
|
||||
&e.context("Failed to update swarm node").into(),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
update.finalize();
|
||||
update_update(update.clone()).await?;
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolve<ExecuteArgs> for RemoveSwarmStacks {
|
||||
#[instrument(
|
||||
"RemoveSwarmStacks",
|
||||
skip_all,
|
||||
fields(
|
||||
id = id.to_string(),
|
||||
task_id = task_id.to_string(),
|
||||
operator = user.id,
|
||||
update_id = update.id,
|
||||
swarm = self.swarm,
|
||||
@@ -87,7 +153,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 +181,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 +203,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 +212,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 +239,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 +261,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 +270,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 +300,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 +322,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 +331,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 +359,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 +381,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 +390,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 +417,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 +439,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 +448,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 +479,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 +501,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 +510,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 +538,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 +560,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 +569,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 +596,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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
+119
-339
@@ -1,5 +1,8 @@
|
||||
use anyhow::Context;
|
||||
use database::mungos::find::find_collect;
|
||||
use database::{
|
||||
bson::doc,
|
||||
mungos::{find::find_collect, mongodb::options::FindOptions},
|
||||
};
|
||||
use komodo_client::{
|
||||
api::read::{
|
||||
ExportAllResourcesToToml, ExportAllResourcesToTomlResponse,
|
||||
@@ -38,122 +41,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 +113,7 @@ impl Resolve<ReadArgs> for ExportAllResourcesToToml {
|
||||
targets,
|
||||
user_groups,
|
||||
include_variables: self.include_variables,
|
||||
existing: self.existing,
|
||||
}
|
||||
.resolve(args)
|
||||
.await
|
||||
@@ -202,131 +129,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 +209,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,
|
||||
};
|
||||
}
|
||||
@@ -388,18 +218,21 @@ impl Resolve<ReadArgs> for ExportResourcesToToml {
|
||||
.context("failed to add user groups")?;
|
||||
|
||||
if include_variables {
|
||||
res.variables =
|
||||
find_collect(&db_client().variables, None, None)
|
||||
.await
|
||||
.context("failed to get variables from db")?
|
||||
.into_iter()
|
||||
.map(|mut variable| {
|
||||
if !user.admin && variable.is_secret {
|
||||
variable.value = "#".repeat(variable.value.len())
|
||||
}
|
||||
variable
|
||||
})
|
||||
.collect();
|
||||
res.variables = find_collect(
|
||||
&db_client().variables,
|
||||
None,
|
||||
FindOptions::builder().sort(doc! { "name": 1 }).build(),
|
||||
)
|
||||
.await
|
||||
.context("failed to get variables from db")?
|
||||
.into_iter()
|
||||
.map(|mut variable| {
|
||||
if !user.admin && variable.is_secret {
|
||||
variable.value = "#".repeat(variable.value.len())
|
||||
}
|
||||
variable
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
let toml = serialize_resources_toml(res)
|
||||
@@ -434,85 +267,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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:#}",)
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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},
|
||||
@@ -555,15 +555,25 @@ impl Resolve<WriteArgs> for RefreshStackCache {
|
||||
&mut services,
|
||||
) {
|
||||
warn!(
|
||||
"failed to extract stack services, things won't works correctly. stack: {} | {e:#}",
|
||||
stack.name
|
||||
stack = stack.id,
|
||||
stack_name = stack.name,
|
||||
"Failed to extract stack services | {e:#}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(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 +760,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 +871,7 @@ pub async fn check_stack_for_update_inner(
|
||||
};
|
||||
|
||||
service_with_update.update_available =
|
||||
latest_digest.update_available(¤t_digests);
|
||||
latest_digest.update_available(current_digests);
|
||||
|
||||
if service_with_update.update_available
|
||||
&& (skip_auto_update || !stack.config.auto_update)
|
||||
@@ -919,7 +940,10 @@ pub async fn check_stack_for_update_inner(
|
||||
.retain(|(stack_id, _)| stack_id != &stack.id)
|
||||
.await;
|
||||
|
||||
let deploy_services = if stack.config.auto_update_all_services {
|
||||
let deploy_services = if stack.config.auto_update_all_services
|
||||
// Swarm stacks don't support individual service deploy
|
||||
|| !stack.config.swarm_id.is_empty()
|
||||
{
|
||||
Vec::new()
|
||||
} else {
|
||||
services_with_update
|
||||
@@ -946,33 +970,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
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
@@ -372,6 +375,9 @@ pub fn core_config() -> &'static CoreConfig {
|
||||
.komodo_logging_location
|
||||
.unwrap_or(config.logging.location),
|
||||
ansi: env.komodo_logging_ansi.unwrap_or(config.logging.ansi),
|
||||
timestamps: env
|
||||
.komodo_logging_timestamps
|
||||
.unwrap_or(config.logging.timestamps),
|
||||
otlp_endpoint: env
|
||||
.komodo_logging_otlp_endpoint
|
||||
.unwrap_or(config.logging.otlp_endpoint),
|
||||
@@ -400,9 +406,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),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
@@ -291,6 +307,13 @@ async fn fix_server(
|
||||
} else {
|
||||
None
|
||||
},
|
||||
// Move address to external_address if not set.
|
||||
// This helps preserve container port link behavior.
|
||||
external_address: if !server.config.external_address.is_empty() {
|
||||
Some(server.config.address)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
if !config.is_none() {
|
||||
@@ -333,7 +356,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 +520,7 @@ async fn create_server_maybe_builder(
|
||||
}
|
||||
|
||||
struct OnboardingKeyValidator {
|
||||
fix_existing_servers_required: bool,
|
||||
privileged_required: bool,
|
||||
}
|
||||
|
||||
impl PublicKeyValidator for OnboardingKeyValidator {
|
||||
@@ -519,9 +542,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"
|
||||
));
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+357
-1162
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ use komodo_client::{
|
||||
permission::PermissionLevel,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
terminal::{ContainerTerminalMode, 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(
|
||||
|
||||
+161
-458
@@ -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,
|
||||
};
|
||||
|
||||
@@ -121,464 +123,151 @@ pub async fn init_execution_update(
|
||||
request: &ExecuteRequest,
|
||||
user: &User,
|
||||
) -> anyhow::Result<Update> {
|
||||
let (operation, target) = match &request {
|
||||
// Swarm
|
||||
ExecuteRequest::RemoveSwarmNodes(data) => (
|
||||
Operation::RemoveSwarmNodes,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmStacks(data) => (
|
||||
Operation::RemoveSwarmStacks,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmServices(data) => (
|
||||
Operation::RemoveSwarmServices,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::CreateSwarmConfig(data) => (
|
||||
Operation::CreateSwarmConfig,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RotateSwarmConfig(data) => (
|
||||
Operation::RotateSwarmConfig,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmConfigs(data) => (
|
||||
Operation::RemoveSwarmConfigs,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::CreateSwarmSecret(data) => (
|
||||
Operation::CreateSwarmSecret,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RotateSwarmSecret(data) => (
|
||||
Operation::RotateSwarmSecret,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RemoveSwarmSecrets(data) => (
|
||||
Operation::RemoveSwarmSecrets,
|
||||
ResourceTarget::Swarm(
|
||||
resource::get::<Swarm>(&data.swarm).await?.id,
|
||||
),
|
||||
),
|
||||
macro_rules! init_execution_match {
|
||||
(
|
||||
resource: [$(($Variant:ident, $ResType:ident, $field:ident)),* $(,)?],
|
||||
batch: [$($BatchVariant:ident),* $(,)?],
|
||||
stack_service: [$(($StackVariant:ident, $ServiceOp:ident)),* $(,)?],
|
||||
system: [$($SysVariant:ident),* $(,)?],
|
||||
) => {
|
||||
match &request {
|
||||
$(
|
||||
ExecuteRequest::$Variant(data) => (
|
||||
Operation::$Variant,
|
||||
ResourceTarget::$ResType(
|
||||
resource::get::<$ResType>(&data.$field).await?.id,
|
||||
),
|
||||
),
|
||||
)*
|
||||
$(
|
||||
ExecuteRequest::$BatchVariant(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
)*
|
||||
$(
|
||||
ExecuteRequest::$StackVariant(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::$ServiceOp
|
||||
} else {
|
||||
Operation::$StackVariant
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
)*
|
||||
// DeployStackIfChanged doesn't have a service variant
|
||||
ExecuteRequest::DeployStackIfChanged(data) => (
|
||||
Operation::DeployStack,
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
$(
|
||||
ExecuteRequest::$SysVariant(_data) => {
|
||||
(Operation::$SysVariant, ResourceTarget::system())
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Server
|
||||
ExecuteRequest::StartContainer(data) => (
|
||||
Operation::StartContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RestartContainer(data) => (
|
||||
Operation::RestartContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseContainer(data) => (
|
||||
Operation::PauseContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseContainer(data) => (
|
||||
Operation::UnpauseContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopContainer(data) => (
|
||||
Operation::StopContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DestroyContainer(data) => (
|
||||
Operation::DestroyContainer,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StartAllContainers(data) => (
|
||||
Operation::StartAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RestartAllContainers(data) => (
|
||||
Operation::RestartAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseAllContainers(data) => (
|
||||
Operation::PauseAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseAllContainers(data) => (
|
||||
Operation::UnpauseAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopAllContainers(data) => (
|
||||
Operation::StopAllContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneContainers(data) => (
|
||||
Operation::PruneContainers,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DeleteNetwork(data) => (
|
||||
Operation::DeleteNetwork,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneNetworks(data) => (
|
||||
Operation::PruneNetworks,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DeleteImage(data) => (
|
||||
Operation::DeleteImage,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneImages(data) => (
|
||||
Operation::PruneImages,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DeleteVolume(data) => (
|
||||
Operation::DeleteVolume,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneVolumes(data) => (
|
||||
Operation::PruneVolumes,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneDockerBuilders(data) => (
|
||||
Operation::PruneDockerBuilders,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneBuildx(data) => (
|
||||
Operation::PruneBuildx,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PruneSystem(data) => (
|
||||
Operation::PruneSystem,
|
||||
ResourceTarget::Server(
|
||||
resource::get::<Server>(&data.server).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Deployment
|
||||
ExecuteRequest::Deploy(data) => (
|
||||
Operation::Deploy,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDeploy(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::PullDeployment(data) => (
|
||||
Operation::PullDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StartDeployment(data) => (
|
||||
Operation::StartDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::RestartDeployment(data) => (
|
||||
Operation::RestartDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseDeployment(data) => (
|
||||
Operation::PauseDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseDeployment(data) => (
|
||||
Operation::UnpauseDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopDeployment(data) => (
|
||||
Operation::StopDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DestroyDeployment(data) => (
|
||||
Operation::DestroyDeployment,
|
||||
ResourceTarget::Deployment(
|
||||
resource::get::<Deployment>(&data.deployment).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDestroyDeployment(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
// Build
|
||||
ExecuteRequest::RunBuild(data) => (
|
||||
Operation::RunBuild,
|
||||
ResourceTarget::Build(
|
||||
resource::get::<Build>(&data.build).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchRunBuild(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::CancelBuild(data) => (
|
||||
Operation::CancelBuild,
|
||||
ResourceTarget::Build(
|
||||
resource::get::<Build>(&data.build).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Repo
|
||||
ExecuteRequest::CloneRepo(data) => (
|
||||
Operation::CloneRepo,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchCloneRepo(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::PullRepo(data) => (
|
||||
Operation::PullRepo,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchPullRepo(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::BuildRepo(data) => (
|
||||
Operation::BuildRepo,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchBuildRepo(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::CancelRepoBuild(data) => (
|
||||
Operation::CancelRepoBuild,
|
||||
ResourceTarget::Repo(
|
||||
resource::get::<Repo>(&data.repo).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Procedure
|
||||
ExecuteRequest::RunProcedure(data) => (
|
||||
Operation::RunProcedure,
|
||||
ResourceTarget::Procedure(
|
||||
resource::get::<Procedure>(&data.procedure).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchRunProcedure(_) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
// Action
|
||||
ExecuteRequest::RunAction(data) => (
|
||||
Operation::RunAction,
|
||||
ResourceTarget::Action(
|
||||
resource::get::<Action>(&data.action).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchRunAction(_) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
// Resource Sync
|
||||
ExecuteRequest::RunSync(data) => (
|
||||
Operation::RunSync,
|
||||
ResourceTarget::ResourceSync(
|
||||
resource::get::<ResourceSync>(&data.sync).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Stack
|
||||
ExecuteRequest::DeployStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::DeployStackService
|
||||
} else {
|
||||
Operation::DeployStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDeployStack(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::DeployStackIfChanged(data) => (
|
||||
Operation::DeployStack,
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDeployStackIfChanged(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::StartStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::StartStackService
|
||||
} else {
|
||||
Operation::StartStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PullStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::PullStackService
|
||||
} else {
|
||||
Operation::PullStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchPullStack(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
ExecuteRequest::RestartStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::RestartStackService
|
||||
} else {
|
||||
Operation::RestartStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::PauseStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::PauseStackService
|
||||
} else {
|
||||
Operation::PauseStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::UnpauseStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::UnpauseStackService
|
||||
} else {
|
||||
Operation::UnpauseStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::StopStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::StopStackService
|
||||
} else {
|
||||
Operation::StopStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::DestroyStack(data) => (
|
||||
if !data.services.is_empty() {
|
||||
Operation::DestroyStackService
|
||||
} else {
|
||||
Operation::DestroyStack
|
||||
},
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::BatchDestroyStack(_data) => {
|
||||
return Ok(Default::default());
|
||||
}
|
||||
|
||||
ExecuteRequest::RunStackService(data) => (
|
||||
Operation::RunStackService,
|
||||
ResourceTarget::Stack(
|
||||
resource::get::<Stack>(&data.stack).await?.id,
|
||||
),
|
||||
),
|
||||
|
||||
// Alerter
|
||||
ExecuteRequest::TestAlerter(data) => (
|
||||
Operation::TestAlerter,
|
||||
ResourceTarget::Alerter(
|
||||
resource::get::<Alerter>(&data.alerter).await?.id,
|
||||
),
|
||||
),
|
||||
ExecuteRequest::SendAlert(_) => {
|
||||
(Operation::SendAlert, ResourceTarget::system())
|
||||
}
|
||||
|
||||
// Maintenance
|
||||
ExecuteRequest::ClearRepoCache(_data) => {
|
||||
(Operation::ClearRepoCache, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::BackupCoreDatabase(_data) => {
|
||||
(Operation::BackupCoreDatabase, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::GlobalAutoUpdate(_data) => {
|
||||
(Operation::GlobalAutoUpdate, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::RotateAllServerKeys(_data) => {
|
||||
(Operation::RotateAllServerKeys, ResourceTarget::system())
|
||||
}
|
||||
ExecuteRequest::RotateCoreKeys(_data) => {
|
||||
(Operation::RotateCoreKeys, ResourceTarget::system())
|
||||
}
|
||||
};
|
||||
let (operation, target) = init_execution_match!(
|
||||
resource: [
|
||||
// Swarm
|
||||
(RemoveSwarmNodes, Swarm, swarm),
|
||||
(UpdateSwarmNode, Swarm, swarm),
|
||||
(RemoveSwarmStacks, Swarm, swarm),
|
||||
(RemoveSwarmServices, Swarm, swarm),
|
||||
(CreateSwarmConfig, Swarm, swarm),
|
||||
(RotateSwarmConfig, Swarm, swarm),
|
||||
(RemoveSwarmConfigs, Swarm, swarm),
|
||||
(CreateSwarmSecret, Swarm, swarm),
|
||||
(RotateSwarmSecret, Swarm, swarm),
|
||||
(RemoveSwarmSecrets, Swarm, swarm),
|
||||
// Server
|
||||
(StartContainer, Server, server),
|
||||
(RestartContainer, Server, server),
|
||||
(PauseContainer, Server, server),
|
||||
(UnpauseContainer, Server, server),
|
||||
(StopContainer, Server, server),
|
||||
(DestroyContainer, Server, server),
|
||||
(StartAllContainers, Server, server),
|
||||
(RestartAllContainers, Server, server),
|
||||
(PauseAllContainers, Server, server),
|
||||
(UnpauseAllContainers, Server, server),
|
||||
(StopAllContainers, Server, server),
|
||||
(PruneContainers, Server, server),
|
||||
(DeleteNetwork, Server, server),
|
||||
(PruneNetworks, Server, server),
|
||||
(DeleteImage, Server, server),
|
||||
(PruneImages, Server, server),
|
||||
(DeleteVolume, Server, server),
|
||||
(PruneVolumes, Server, server),
|
||||
(PruneDockerBuilders, Server, server),
|
||||
(PruneBuildx, Server, server),
|
||||
(PruneSystem, Server, server),
|
||||
// Deployment
|
||||
(Deploy, Deployment, deployment),
|
||||
(PullDeployment, Deployment, deployment),
|
||||
(StartDeployment, Deployment, deployment),
|
||||
(RestartDeployment, Deployment, deployment),
|
||||
(PauseDeployment, Deployment, deployment),
|
||||
(UnpauseDeployment, Deployment, deployment),
|
||||
(StopDeployment, Deployment, deployment),
|
||||
(DestroyDeployment, Deployment, deployment),
|
||||
// Build
|
||||
(RunBuild, Build, build),
|
||||
(CancelBuild, Build, build),
|
||||
// Repo
|
||||
(CloneRepo, Repo, repo),
|
||||
(PullRepo, Repo, repo),
|
||||
(BuildRepo, Repo, repo),
|
||||
(CancelRepoBuild, Repo, repo),
|
||||
// Procedure
|
||||
(RunProcedure, Procedure, procedure),
|
||||
// Action
|
||||
(RunAction, Action, action),
|
||||
// Resource Sync
|
||||
(RunSync, ResourceSync, sync),
|
||||
// Stack (simple)
|
||||
(RunStackService, Stack, stack),
|
||||
// Alerter
|
||||
(TestAlerter, Alerter, alerter),
|
||||
],
|
||||
batch: [
|
||||
BatchDeploy,
|
||||
BatchDestroyDeployment,
|
||||
BatchRunBuild,
|
||||
BatchCloneRepo,
|
||||
BatchPullRepo,
|
||||
BatchBuildRepo,
|
||||
BatchRunProcedure,
|
||||
BatchRunAction,
|
||||
BatchDeployStack,
|
||||
BatchDeployStackIfChanged,
|
||||
BatchPullStack,
|
||||
BatchDestroyStack,
|
||||
],
|
||||
stack_service: [
|
||||
(DeployStack, DeployStackService),
|
||||
(PullStack, PullStackService),
|
||||
(StartStack, StartStackService),
|
||||
(RestartStack, RestartStackService),
|
||||
(PauseStack, PauseStackService),
|
||||
(UnpauseStack, UnpauseStackService),
|
||||
(StopStack, StopStackService),
|
||||
(DestroyStack, DestroyStackService),
|
||||
],
|
||||
system: [
|
||||
SendAlert,
|
||||
ClearRepoCache,
|
||||
BackupCoreDatabase,
|
||||
GlobalAutoUpdate,
|
||||
RotateAllServerKeys,
|
||||
RotateCoreKeys,
|
||||
],
|
||||
);
|
||||
|
||||
let mut update = make_update(target, operation, user);
|
||||
update.in_progress();
|
||||
@@ -591,3 +280,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -46,8 +46,10 @@ pub async fn update_swarm_stack_cache(
|
||||
Some(SwarmState::Unknown) | None => StackState::Unknown,
|
||||
})
|
||||
.unwrap_or(StackState::Down);
|
||||
|
||||
let services = extract_services_from_stack(&stack);
|
||||
let service_prefix = format!("{project_name}_");
|
||||
|
||||
let mut services_with_swarm_services = services
|
||||
.iter()
|
||||
.map(
|
||||
@@ -74,27 +76,50 @@ pub async fn update_swarm_stack_cache(
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.cloned();
|
||||
|
||||
let (image, image_digests) = swarm_service
|
||||
.as_ref()
|
||||
.and_then(|swarm_service| swarm_service.image.as_ref())
|
||||
.map(|image| {
|
||||
(
|
||||
image.clone(),
|
||||
ImageDigest::parse(image).map(|d| vec![d]),
|
||||
)
|
||||
})
|
||||
.unwrap_or((
|
||||
if image.contains(':') {
|
||||
image.to_string()
|
||||
} else {
|
||||
format!("{image}:latest")
|
||||
},
|
||||
None,
|
||||
));
|
||||
|
||||
StackService {
|
||||
service: service_name.clone(),
|
||||
image: image.clone(),
|
||||
container: None,
|
||||
swarm_service,
|
||||
image_digests: Default::default(),
|
||||
image,
|
||||
image_digests,
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
services_with_swarm_services
|
||||
.sort_by(|a, b| a.service.cmp(&b.service));
|
||||
|
||||
let prev_state = stack_status_cache
|
||||
.get(&stack.id)
|
||||
.await
|
||||
.map(|s| s.curr.state);
|
||||
|
||||
let status = CachedStackStatus {
|
||||
id: stack.id.clone(),
|
||||
state: current_state,
|
||||
services: services_with_swarm_services,
|
||||
};
|
||||
|
||||
stack_status_cache
|
||||
.insert(
|
||||
stack.id,
|
||||
@@ -207,10 +232,17 @@ pub async fn update_swarm_deployment_cache(
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.cloned();
|
||||
|
||||
let image_digests = service
|
||||
.as_ref()
|
||||
.and_then(|service| service.image.as_ref())
|
||||
.and_then(|image| ImageDigest::parse(image).map(|d| vec![d]));
|
||||
|
||||
let prev_state = deployment_status_cache
|
||||
.get(&deployment.id)
|
||||
.await
|
||||
.map(|s| s.curr.state);
|
||||
|
||||
let current_state = service
|
||||
.as_ref()
|
||||
.map(|service| match service.state {
|
||||
@@ -220,6 +252,7 @@ pub async fn update_swarm_deployment_cache(
|
||||
SwarmState::Unknown => DeploymentState::Unknown,
|
||||
})
|
||||
.unwrap_or(DeploymentState::NotDeployed);
|
||||
|
||||
deployment_status_cache
|
||||
.insert(
|
||||
deployment.id.clone(),
|
||||
@@ -227,9 +260,9 @@ pub async fn update_swarm_deployment_cache(
|
||||
curr: CachedDeploymentStatus {
|
||||
id: deployment.id,
|
||||
state: current_state,
|
||||
service,
|
||||
container: None,
|
||||
image_digests: None,
|
||||
service,
|
||||
image_digests,
|
||||
},
|
||||
prev: prev_state,
|
||||
}
|
||||
@@ -251,6 +284,7 @@ pub async fn update_server_deployment_cache(
|
||||
.iter()
|
||||
.find(|container| container.name == deployment.name)
|
||||
.cloned();
|
||||
|
||||
let image_digests = container
|
||||
.as_ref()
|
||||
.and_then(|container| container.image_id.as_ref())
|
||||
@@ -263,10 +297,12 @@ pub async fn update_server_deployment_cache(
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let prev_state = deployment_status_cache
|
||||
.get(&deployment.id)
|
||||
.await
|
||||
.map(|s| s.curr.state);
|
||||
|
||||
let current_state = container
|
||||
.as_ref()
|
||||
.map(|c| c.state.into())
|
||||
|
||||
@@ -6,7 +6,6 @@ use std::{
|
||||
use anyhow::anyhow;
|
||||
use async_timing_util::wait_until_timelength;
|
||||
use database::mungos::find::find_collect;
|
||||
use formatting::format_serror;
|
||||
use futures_util::future::join_all;
|
||||
use komodo_client::entities::{
|
||||
docker::node::NodeState,
|
||||
@@ -23,7 +22,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 +34,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 +55,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 +63,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 +74,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 +95,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;
|
||||
@@ -108,9 +107,9 @@ pub async fn update_cache_for_swarm(swarm: &Swarm, force: bool) {
|
||||
state: SwarmState::Unknown,
|
||||
inspect: None,
|
||||
lists: None,
|
||||
err: Some(format_serror(
|
||||
&anyhow!("No Servers configured as manager nodes").into(),
|
||||
)),
|
||||
err: Some(
|
||||
anyhow!("No servers configured as manager nodes").into(),
|
||||
),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
@@ -137,7 +136,7 @@ pub async fn update_cache_for_swarm(swarm: &Swarm, force: bool) {
|
||||
state: SwarmState::Unknown,
|
||||
inspect: None,
|
||||
lists: None,
|
||||
err: Some(format_serror(&e.into())),
|
||||
err: Some(e.into()),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
|
||||
@@ -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 => {}
|
||||
}
|
||||
|
||||
+172
-684
@@ -183,695 +183,183 @@ async fn validate_config(
|
||||
};
|
||||
for stage in stages {
|
||||
for exec in &mut stage.executions {
|
||||
match &mut exec.execution {
|
||||
Execution::None(_) => {}
|
||||
Execution::RunProcedure(params) => {
|
||||
let procedure = super::get_check_permissions::<Procedure>(
|
||||
¶ms.procedure,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
match id {
|
||||
Some(id) if procedure.id == id => {
|
||||
return Err(anyhow!(
|
||||
"Cannot have self-referential procedure"
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
params.procedure = procedure.id;
|
||||
}
|
||||
Execution::BatchRunProcedure(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::RunAction(params) => {
|
||||
let action = super::get_check_permissions::<Action>(
|
||||
¶ms.action,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.action = action.id;
|
||||
}
|
||||
Execution::BatchRunAction(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::RunBuild(params) => {
|
||||
let build = super::get_check_permissions::<Build>(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
}
|
||||
Execution::BatchRunBuild(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::CancelBuild(params) => {
|
||||
let build = super::get_check_permissions::<Build>(
|
||||
¶ms.build,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.build = build.id;
|
||||
}
|
||||
Execution::Deploy(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::BatchDeploy(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::PullDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StartDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::RestartDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::PauseDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::UnpauseDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::StopDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::DestroyDeployment(params) => {
|
||||
let deployment =
|
||||
super::get_check_permissions::<Deployment>(
|
||||
¶ms.deployment,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.deployment = deployment.id;
|
||||
}
|
||||
Execution::BatchDestroyDeployment(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::CloneRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::BatchCloneRepo(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::PullRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::BatchPullRepo(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::BuildRepo(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::BatchBuildRepo(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::CancelRepoBuild(params) => {
|
||||
let repo = super::get_check_permissions::<Repo>(
|
||||
¶ms.repo,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.repo = repo.id;
|
||||
}
|
||||
Execution::StartContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RestartContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PauseContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::UnpauseContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::StopContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DestroyContainer(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::StartAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RestartAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PauseAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::UnpauseAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::StopAllContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneContainers(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DeleteNetwork(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneNetworks(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DeleteImage(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneImages(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::DeleteVolume(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneVolumes(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneDockerBuilders(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneBuildx(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::PruneSystem(params) => {
|
||||
let server = super::get_check_permissions::<Server>(
|
||||
¶ms.server,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.server = server.id;
|
||||
}
|
||||
Execution::RunSync(params) => {
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
}
|
||||
Execution::CommitSync(params) => {
|
||||
// This one is actually a write operation.
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
}
|
||||
Execution::DeployStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchDeployStack(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::DeployStackIfChanged(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::PullStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchPullStack(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::StartStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::RestartStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::PauseStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::UnpauseStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::StopStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::DestroyStack(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::RunStackService(params) => {
|
||||
let stack = super::get_check_permissions::<Stack>(
|
||||
¶ms.stack,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.stack = stack.id;
|
||||
}
|
||||
Execution::BatchDestroyStack(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::TestAlerter(params) => {
|
||||
let alerter = super::get_check_permissions::<Alerter>(
|
||||
¶ms.alerter,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.alerter = alerter.id;
|
||||
}
|
||||
Execution::SendAlert(params) => {
|
||||
params.alerters = params
|
||||
.alerters
|
||||
.iter()
|
||||
.map(async |alerter| {
|
||||
let id = super::get_check_permissions::<Alerter>(
|
||||
alerter,
|
||||
macro_rules! check_execution_perms {
|
||||
(
|
||||
execute: [$(($Variant:ident, $Type:ident, $field:ident)),* $(,)?],
|
||||
batch_admin: [$($BatchVariant:ident),* $(,)?],
|
||||
admin_only: [$(($AdminVariant:ident, $msg:literal)),* $(,)?],
|
||||
) => {
|
||||
match &mut exec.execution {
|
||||
$(
|
||||
Execution::$Variant(params) => {
|
||||
let resource = super::get_check_permissions::<$Type>(
|
||||
¶ms.$field,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.$field = resource.id;
|
||||
}
|
||||
)*
|
||||
$(
|
||||
Execution::$BatchVariant(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot configure Batch executions"
|
||||
));
|
||||
}
|
||||
}
|
||||
)*
|
||||
$(
|
||||
Execution::$AdminVariant(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!($msg));
|
||||
}
|
||||
}
|
||||
)*
|
||||
// Special: self-referential procedure check
|
||||
Execution::RunProcedure(params) => {
|
||||
let procedure = super::get_check_permissions::<Procedure>(
|
||||
¶ms.procedure,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?
|
||||
.id;
|
||||
anyhow::Ok(id)
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
}
|
||||
Execution::RemoveSwarmNodes(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmStacks(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmServices(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::CreateSwarmConfig(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RotateSwarmConfig(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::CreateSwarmSecret(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RotateSwarmSecret(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(params) => {
|
||||
let swarm = super::get_check_permissions::<Swarm>(
|
||||
¶ms.swarm,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?;
|
||||
params.swarm = swarm.id;
|
||||
}
|
||||
Execution::ClearRepoCache(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot clear repo cache"
|
||||
));
|
||||
.await?;
|
||||
match id {
|
||||
Some(id) if procedure.id == id => {
|
||||
return Err(anyhow!(
|
||||
"Cannot have self-referential procedure"
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
params.procedure = procedure.id;
|
||||
}
|
||||
// Special: CommitSync uses Write permission
|
||||
Execution::CommitSync(params) => {
|
||||
let sync = super::get_check_permissions::<ResourceSync>(
|
||||
¶ms.sync,
|
||||
user,
|
||||
PermissionLevel::Write.into(),
|
||||
)
|
||||
.await?;
|
||||
params.sync = sync.id;
|
||||
}
|
||||
// Special: SendAlert checks a Vec of alerters
|
||||
Execution::SendAlert(params) => {
|
||||
params.alerters = params
|
||||
.alerters
|
||||
.iter()
|
||||
.map(async |alerter| {
|
||||
let id = super::get_check_permissions::<Alerter>(
|
||||
alerter,
|
||||
user,
|
||||
PermissionLevel::Execute.into(),
|
||||
)
|
||||
.await?
|
||||
.id;
|
||||
anyhow::Ok(id)
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
}
|
||||
Execution::None(_) | Execution::Sleep(_) => {}
|
||||
}
|
||||
}
|
||||
Execution::BackupCoreDatabase(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger core database backup"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::GlobalAutoUpdate(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger global auto update"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::RotateAllServerKeys(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger rotate all server keys"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::RotateCoreKeys(_params) => {
|
||||
if !user.admin {
|
||||
return Err(anyhow!(
|
||||
"Non admin user cannot trigger rotate core keys"
|
||||
));
|
||||
}
|
||||
}
|
||||
Execution::Sleep(_) => {}
|
||||
};
|
||||
}
|
||||
check_execution_perms!(
|
||||
execute: [
|
||||
// Action
|
||||
(RunAction, Action, action),
|
||||
// Build
|
||||
(RunBuild, Build, build),
|
||||
(CancelBuild, Build, build),
|
||||
// Deployment
|
||||
(Deploy, Deployment, deployment),
|
||||
(PullDeployment, Deployment, deployment),
|
||||
(StartDeployment, Deployment, deployment),
|
||||
(RestartDeployment, Deployment, deployment),
|
||||
(PauseDeployment, Deployment, deployment),
|
||||
(UnpauseDeployment, Deployment, deployment),
|
||||
(StopDeployment, Deployment, deployment),
|
||||
(DestroyDeployment, Deployment, deployment),
|
||||
// Repo
|
||||
(CloneRepo, Repo, repo),
|
||||
(PullRepo, Repo, repo),
|
||||
(BuildRepo, Repo, repo),
|
||||
(CancelRepoBuild, Repo, repo),
|
||||
// Server
|
||||
(StartContainer, Server, server),
|
||||
(RestartContainer, Server, server),
|
||||
(PauseContainer, Server, server),
|
||||
(UnpauseContainer, Server, server),
|
||||
(StopContainer, Server, server),
|
||||
(DestroyContainer, Server, server),
|
||||
(StartAllContainers, Server, server),
|
||||
(RestartAllContainers, Server, server),
|
||||
(PauseAllContainers, Server, server),
|
||||
(UnpauseAllContainers, Server, server),
|
||||
(StopAllContainers, Server, server),
|
||||
(PruneContainers, Server, server),
|
||||
(DeleteNetwork, Server, server),
|
||||
(PruneNetworks, Server, server),
|
||||
(DeleteImage, Server, server),
|
||||
(PruneImages, Server, server),
|
||||
(DeleteVolume, Server, server),
|
||||
(PruneVolumes, Server, server),
|
||||
(PruneDockerBuilders, Server, server),
|
||||
(PruneBuildx, Server, server),
|
||||
(PruneSystem, Server, server),
|
||||
// Resource Sync
|
||||
(RunSync, ResourceSync, sync),
|
||||
// Stack
|
||||
(DeployStack, Stack, stack),
|
||||
(DeployStackIfChanged, Stack, stack),
|
||||
(PullStack, Stack, stack),
|
||||
(StartStack, Stack, stack),
|
||||
(RestartStack, Stack, stack),
|
||||
(PauseStack, Stack, stack),
|
||||
(UnpauseStack, Stack, stack),
|
||||
(StopStack, Stack, stack),
|
||||
(DestroyStack, Stack, stack),
|
||||
(RunStackService, Stack, stack),
|
||||
// Alerter
|
||||
(TestAlerter, Alerter, alerter),
|
||||
// Swarm
|
||||
(RemoveSwarmNodes, Swarm, swarm),
|
||||
(UpdateSwarmNode, Swarm, swarm),
|
||||
(RemoveSwarmStacks, Swarm, swarm),
|
||||
(RemoveSwarmServices, Swarm, swarm),
|
||||
(CreateSwarmConfig, Swarm, swarm),
|
||||
(RotateSwarmConfig, Swarm, swarm),
|
||||
(RemoveSwarmConfigs, Swarm, swarm),
|
||||
(CreateSwarmSecret, Swarm, swarm),
|
||||
(RotateSwarmSecret, Swarm, swarm),
|
||||
(RemoveSwarmSecrets, Swarm, swarm),
|
||||
],
|
||||
batch_admin: [
|
||||
BatchRunProcedure,
|
||||
BatchRunAction,
|
||||
BatchRunBuild,
|
||||
BatchDeploy,
|
||||
BatchDestroyDeployment,
|
||||
BatchCloneRepo,
|
||||
BatchPullRepo,
|
||||
BatchBuildRepo,
|
||||
BatchDeployStack,
|
||||
BatchDeployStackIfChanged,
|
||||
BatchPullStack,
|
||||
BatchDestroyStack,
|
||||
],
|
||||
admin_only: [
|
||||
(ClearRepoCache, "Non admin user cannot clear repo cache"),
|
||||
(BackupCoreDatabase, "Non admin user cannot trigger core database backup"),
|
||||
(GlobalAutoUpdate, "Non admin user cannot trigger global auto update"),
|
||||
(RotateAllServerKeys, "Non admin user cannot trigger rotate all server keys"),
|
||||
(RotateCoreKeys, "Non admin user cannot trigger rotate core keys"),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use periphery_client::api;
|
||||
use crate::{
|
||||
config::core_config,
|
||||
helpers::periphery_client,
|
||||
monitor::update_cache_for_server,
|
||||
monitor::refresh_server_cache,
|
||||
state::{
|
||||
action_states, db_client, periphery_connections,
|
||||
server_status_cache,
|
||||
@@ -93,6 +93,7 @@ impl super::KomodoResource for Server {
|
||||
resource_type: ResourceTargetVariant::Server,
|
||||
info: ServerListItemInfo {
|
||||
state: status.as_ref().map(|s| s.state).unwrap_or_default(),
|
||||
err: status.as_ref().and_then(|s| s.err.clone()),
|
||||
region: server.config.region,
|
||||
address: optional_string(server.config.address),
|
||||
external_address: optional_string(
|
||||
@@ -151,7 +152,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 +174,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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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 => {}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -38,8 +38,6 @@ pub fn extract_services_into_res(
|
||||
"failed to parse service names from compose contents",
|
||||
)?;
|
||||
|
||||
let mut services = Vec::with_capacity(compose.services.capacity());
|
||||
|
||||
for (
|
||||
service_name,
|
||||
ComposeService {
|
||||
@@ -49,17 +47,29 @@ pub fn extract_services_into_res(
|
||||
},
|
||||
) in compose.services
|
||||
{
|
||||
let image = image.unwrap_or_default();
|
||||
services.push(StackServiceNames {
|
||||
container_name: container_name
|
||||
.unwrap_or_else(|| format!("{project_name}-{service_name}")),
|
||||
image_digest: service_image_digests.get(&service_name).cloned(),
|
||||
service_name,
|
||||
image,
|
||||
});
|
||||
if let Some(existing) =
|
||||
res.iter_mut().find(|s| s.service_name == service_name)
|
||||
{
|
||||
// Override any defined fields
|
||||
if let Some(container_name) = container_name {
|
||||
existing.container_name = container_name;
|
||||
}
|
||||
if let Some(image) = image {
|
||||
existing.image = image;
|
||||
}
|
||||
} else {
|
||||
res.push(StackServiceNames {
|
||||
container_name: container_name.unwrap_or_else(|| {
|
||||
format!("{project_name}-{service_name}")
|
||||
}),
|
||||
image_digest: service_image_digests
|
||||
.get(&service_name)
|
||||
.cloned(),
|
||||
image: image.unwrap_or_default(),
|
||||
service_name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res.extend(services);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+20
-20
@@ -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(),
|
||||
|
||||
@@ -87,7 +87,7 @@ pub struct CachedSwarmStatus {
|
||||
pub inspect: Option<SwarmInspectInfo>,
|
||||
pub lists: Option<SwarmLists>,
|
||||
/// Store the error in communicating with Swarm
|
||||
pub err: Option<String>,
|
||||
pub err: Option<mogh_error::Serror>,
|
||||
}
|
||||
|
||||
pub type SwarmStatusCache =
|
||||
@@ -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
@@ -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,
|
||||
|
||||
+21
-30
@@ -243,36 +243,27 @@ pub fn extend_resources(
|
||||
more: ResourcesToml,
|
||||
match_tags: &[String],
|
||||
) {
|
||||
resources
|
||||
.servers
|
||||
.extend(filter_by_tag(more.servers, match_tags));
|
||||
resources
|
||||
.stacks
|
||||
.extend(filter_by_tag(more.stacks, match_tags));
|
||||
resources
|
||||
.deployments
|
||||
.extend(filter_by_tag(more.deployments, match_tags));
|
||||
resources
|
||||
.builds
|
||||
.extend(filter_by_tag(more.builds, match_tags));
|
||||
resources
|
||||
.repos
|
||||
.extend(filter_by_tag(more.repos, match_tags));
|
||||
resources
|
||||
.procedures
|
||||
.extend(filter_by_tag(more.procedures, match_tags));
|
||||
resources
|
||||
.actions
|
||||
.extend(filter_by_tag(more.actions, match_tags));
|
||||
resources
|
||||
.alerters
|
||||
.extend(filter_by_tag(more.alerters, match_tags));
|
||||
resources
|
||||
.builders
|
||||
.extend(filter_by_tag(more.builders, match_tags));
|
||||
resources
|
||||
.resource_syncs
|
||||
.extend(filter_by_tag(more.resource_syncs, match_tags));
|
||||
macro_rules! extend_filtered {
|
||||
($($field:ident),* $(,)?) => {
|
||||
$(
|
||||
resources.$field.extend(filter_by_tag(more.$field, match_tags));
|
||||
)*
|
||||
};
|
||||
}
|
||||
// New resource types need to be added here manually.
|
||||
extend_filtered!(
|
||||
servers,
|
||||
swarms,
|
||||
stacks,
|
||||
deployments,
|
||||
builds,
|
||||
repos,
|
||||
procedures,
|
||||
actions,
|
||||
alerters,
|
||||
builders,
|
||||
resource_syncs,
|
||||
);
|
||||
resources.user_groups.extend(more.user_groups);
|
||||
resources.variables.extend(more.variables);
|
||||
}
|
||||
|
||||
+57
-500
@@ -1,29 +1,27 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use formatting::{Color, bold, colored, muted};
|
||||
use komodo_client::{
|
||||
api::execute::Execution,
|
||||
entities::{
|
||||
ResourceTargetVariant,
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
update::Log,
|
||||
user::sync_user,
|
||||
},
|
||||
use komodo_client::entities::{
|
||||
ResourceTargetVariant,
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
update::Log,
|
||||
user::sync_user,
|
||||
};
|
||||
use partial_derive2::{MaybeNone, PartialDiff};
|
||||
|
||||
use crate::{
|
||||
helpers::procedure::replace_procedure_stage_ids_with_names,
|
||||
resource::{KomodoResource, ResourceMetaUpdate},
|
||||
state::all_resources_cache,
|
||||
sync::{ToUpdateItem, execute::run_update_meta},
|
||||
@@ -35,17 +33,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 +44,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 +88,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 +108,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 +139,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 +173,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 +212,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 +321,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,458 +339,7 @@ impl ResourceSyncTrait for Procedure {
|
||||
mut original: Self::Config,
|
||||
update: Self::PartialConfig,
|
||||
) -> anyhow::Result<Self::ConfigDiff> {
|
||||
let resources = all_resources_cache().load();
|
||||
for stage in &mut original.stages {
|
||||
for execution in &mut stage.executions {
|
||||
match &mut execution.execution {
|
||||
Execution::None(_) => {}
|
||||
Execution::RunProcedure(config) => {
|
||||
config.procedure = resources
|
||||
.procedures
|
||||
.get(&config.procedure)
|
||||
.map(|p| p.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchRunProcedure(_config) => {}
|
||||
Execution::RunAction(config) => {
|
||||
config.action = resources
|
||||
.actions
|
||||
.get(&config.action)
|
||||
.map(|p| p.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchRunAction(_config) => {}
|
||||
Execution::RunBuild(config) => {
|
||||
config.build = resources
|
||||
.builds
|
||||
.get(&config.build)
|
||||
.map(|b| b.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchRunBuild(_config) => {}
|
||||
Execution::CancelBuild(config) => {
|
||||
config.build = resources
|
||||
.builds
|
||||
.get(&config.build)
|
||||
.map(|b| b.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::Deploy(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDeploy(_config) => {}
|
||||
Execution::PullDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StartDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DestroyDeployment(config) => {
|
||||
config.deployment = resources
|
||||
.deployments
|
||||
.get(&config.deployment)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDestroyDeployment(_config) => {}
|
||||
Execution::CloneRepo(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchCloneRepo(_config) => {}
|
||||
Execution::PullRepo(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchPullRepo(_config) => {}
|
||||
Execution::BuildRepo(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchBuildRepo(_config) => {}
|
||||
Execution::CancelRepoBuild(config) => {
|
||||
config.repo = resources
|
||||
.repos
|
||||
.get(&config.repo)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StartContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DestroyContainer(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StartAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopAllContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneContainers(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeleteNetwork(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneNetworks(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeleteImage(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneImages(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeleteVolume(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneVolumes(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneDockerBuilders(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneBuildx(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PruneSystem(config) => {
|
||||
config.server = resources
|
||||
.servers
|
||||
.get(&config.server)
|
||||
.map(|d| d.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RunSync(config) => {
|
||||
config.sync = resources
|
||||
.syncs
|
||||
.get(&config.sync)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::CommitSync(config) => {
|
||||
config.sync = resources
|
||||
.syncs
|
||||
.get(&config.sync)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DeployStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDeployStack(_config) => {}
|
||||
Execution::DeployStackIfChanged(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(_config) => {}
|
||||
Execution::PullStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchPullStack(_config) => {}
|
||||
Execution::StartStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RestartStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::PauseStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::UnpauseStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::StopStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::DestroyStack(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RunStackService(config) => {
|
||||
config.stack = resources
|
||||
.stacks
|
||||
.get(&config.stack)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::BatchDestroyStack(_config) => {}
|
||||
Execution::TestAlerter(config) => {
|
||||
config.alerter = resources
|
||||
.alerters
|
||||
.get(&config.alerter)
|
||||
.map(|a| a.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::SendAlert(config) => {
|
||||
config.alerters = config
|
||||
.alerters
|
||||
.iter()
|
||||
.map(|alerter| {
|
||||
resources
|
||||
.alerters
|
||||
.get(alerter)
|
||||
.map(|a| a.name.clone())
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
Execution::RemoveSwarmNodes(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmStacks(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmServices(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::CreateSwarmConfig(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RotateSwarmConfig(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::CreateSwarmSecret(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RotateSwarmSecret(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(config) => {
|
||||
config.swarm = resources
|
||||
.swarms
|
||||
.get(&config.swarm)
|
||||
.map(|s| s.name.clone())
|
||||
.unwrap_or_default();
|
||||
}
|
||||
Execution::ClearRepoCache(_) => {}
|
||||
Execution::BackupCoreDatabase(_) => {}
|
||||
Execution::GlobalAutoUpdate(_) => {}
|
||||
Execution::RotateAllServerKeys(_) => {}
|
||||
Execution::RotateCoreKeys(_) => {}
|
||||
Execution::Sleep(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
replace_procedure_stage_ids_with_names(&mut original.stages);
|
||||
Ok(original.partial_diff(update))
|
||||
}
|
||||
}
|
||||
|
||||
+30
-519
@@ -2,28 +2,28 @@ use std::collections::HashMap;
|
||||
|
||||
use anyhow::Context;
|
||||
use indexmap::IndexMap;
|
||||
use komodo_client::{
|
||||
api::execute::Execution,
|
||||
entities::{
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig, PartialBuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
toml::ResourceToml,
|
||||
},
|
||||
use komodo_client::entities::{
|
||||
action::Action,
|
||||
alerter::Alerter,
|
||||
build::Build,
|
||||
builder::{Builder, BuilderConfig, PartialBuilderConfig},
|
||||
deployment::{Deployment, DeploymentImage},
|
||||
procedure::Procedure,
|
||||
repo::Repo,
|
||||
resource::Resource,
|
||||
server::Server,
|
||||
stack::Stack,
|
||||
swarm::Swarm,
|
||||
sync::ResourceSync,
|
||||
tag::Tag,
|
||||
toml::ResourceToml,
|
||||
};
|
||||
use partial_derive2::{MaybeNone, PartialDiff};
|
||||
|
||||
use crate::{resource::KomodoResource, state::all_resources_cache};
|
||||
use crate::{
|
||||
helpers::procedure::replace_procedure_stage_ids_with_names,
|
||||
resource::KomodoResource, state::all_resources_cache,
|
||||
};
|
||||
|
||||
pub const TOML_PRETTY_OPTIONS: toml_pretty::Options =
|
||||
toml_pretty::Options {
|
||||
@@ -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 {
|
||||
@@ -454,506 +462,9 @@ impl ToToml for Builder {
|
||||
|
||||
impl ToToml for Procedure {
|
||||
fn replace_ids(resource: &mut Resource<Self::Config, Self::Info>) {
|
||||
let all = all_resources_cache().load();
|
||||
for stage in &mut resource.config.stages {
|
||||
for execution in &mut stage.executions {
|
||||
match &mut execution.execution {
|
||||
Execution::RunProcedure(exec) => exec.procedure.clone_from(
|
||||
all
|
||||
.procedures
|
||||
.get(&exec.procedure)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchRunProcedure(_exec) => {}
|
||||
Execution::RunAction(exec) => exec.action.clone_from(
|
||||
all
|
||||
.actions
|
||||
.get(&exec.action)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchRunAction(_exec) => {}
|
||||
Execution::RunBuild(exec) => exec.build.clone_from(
|
||||
all
|
||||
.builds
|
||||
.get(&exec.build)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchRunBuild(_exec) => {}
|
||||
Execution::CancelBuild(exec) => exec.build.clone_from(
|
||||
all
|
||||
.builds
|
||||
.get(&exec.build)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::Deploy(exec) => exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchDeploy(_exec) => {}
|
||||
Execution::PullDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StartDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RestartDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PauseDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::UnpauseDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StopDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::DestroyDeployment(exec) => {
|
||||
exec.deployment.clone_from(
|
||||
all
|
||||
.deployments
|
||||
.get(&exec.deployment)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::BatchDestroyDeployment(_exec) => {}
|
||||
Execution::CloneRepo(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchCloneRepo(_exec) => {}
|
||||
Execution::PullRepo(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchPullRepo(_exec) => {}
|
||||
Execution::BuildRepo(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchBuildRepo(_exec) => {}
|
||||
Execution::CancelRepoBuild(exec) => exec.repo.clone_from(
|
||||
all
|
||||
.repos
|
||||
.get(&exec.repo)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::StartContainer(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RestartContainer(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PauseContainer(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::UnpauseContainer(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StopContainer(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DestroyContainer(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StartAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RestartAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PauseAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::UnpauseAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::StopAllContainers(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PruneContainers(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeleteNetwork(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneNetworks(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeleteImage(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneImages(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeleteVolume(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneVolumes(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneDockerBuilders(exec) => {
|
||||
exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::PruneBuildx(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PruneSystem(exec) => exec.server.clone_from(
|
||||
all
|
||||
.servers
|
||||
.get(&exec.server)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RunSync(exec) => exec.sync.clone_from(
|
||||
all
|
||||
.syncs
|
||||
.get(&exec.sync)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::CommitSync(exec) => exec.sync.clone_from(
|
||||
all
|
||||
.syncs
|
||||
.get(&exec.sync)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DeployStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchDeployStack(_exec) => {}
|
||||
Execution::DeployStackIfChanged(exec) => {
|
||||
exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::BatchDeployStackIfChanged(_exec) => {}
|
||||
Execution::PullStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchPullStack(_exec) => {}
|
||||
Execution::StartStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RestartStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RunStackService(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::PauseStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::UnpauseStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::StopStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::DestroyStack(exec) => exec.stack.clone_from(
|
||||
all
|
||||
.stacks
|
||||
.get(&exec.stack)
|
||||
.map(|r| &r.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::BatchDestroyStack(_exec) => {}
|
||||
Execution::TestAlerter(exec) => exec.alerter.clone_from(
|
||||
all
|
||||
.alerters
|
||||
.get(&exec.alerter)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::SendAlert(exec) => {
|
||||
exec.alerters.iter_mut().for_each(|a| {
|
||||
a.clone_from(
|
||||
all
|
||||
.alerters
|
||||
.get(a)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
})
|
||||
}
|
||||
Execution::RemoveSwarmNodes(exec) => exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
),
|
||||
Execution::RemoveSwarmStacks(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RemoveSwarmServices(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::CreateSwarmConfig(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RotateSwarmConfig(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RemoveSwarmConfigs(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::CreateSwarmSecret(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RotateSwarmSecret(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::RemoveSwarmSecrets(exec) => {
|
||||
exec.swarm.clone_from(
|
||||
all
|
||||
.swarms
|
||||
.get(&exec.swarm)
|
||||
.map(|a| &a.name)
|
||||
.unwrap_or(&String::new()),
|
||||
)
|
||||
}
|
||||
Execution::None(_)
|
||||
| Execution::Sleep(_)
|
||||
| Execution::ClearRepoCache(_)
|
||||
| Execution::BackupCoreDatabase(_)
|
||||
| Execution::GlobalAutoUpdate(_)
|
||||
| Execution::RotateAllServerKeys(_)
|
||||
| Execution::RotateCoreKeys(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
replace_procedure_stage_ids_with_names(
|
||||
&mut resource.config.stages,
|
||||
);
|
||||
}
|
||||
|
||||
fn push_to_toml_string(
|
||||
|
||||
@@ -45,7 +45,7 @@ async fn serve_client_file(
|
||||
|
||||
let contents = fs::read_to_string(format!(
|
||||
"{}/client/{path}",
|
||||
core_config().frontend_path
|
||||
core_config().ui_path
|
||||
))
|
||||
.await
|
||||
.with_context(|| format!("Failed to read file: {path}"))?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## All in one, multi stage compile + runtime Docker build for your architecture.
|
||||
|
||||
FROM rust:1.93.0-trixie AS builder
|
||||
FROM rust:1.94.1-trixie AS builder
|
||||
RUN cargo install cargo-strip
|
||||
|
||||
WORKDIR /builder
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Sets up the necessary runtime container dependencies for Komodo Periphery.
|
||||
## Since theres no heavy build here, QEMU multi-arch builds are fine for this image.
|
||||
|
||||
ARG BINARIES_IMAGE=ghcr.io/moghtech/komodo-binaries: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,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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -99,13 +99,9 @@ impl Resolve<crate::api::Args> for UpdateSwarmNode {
|
||||
}
|
||||
|
||||
if let Some(label_add) = self.label_add {
|
||||
for (key, value) in label_add {
|
||||
for key_value in label_add {
|
||||
command += " --label-add ";
|
||||
command += &key;
|
||||
if let Some(value) = value {
|
||||
command += "=";
|
||||
command += &value;
|
||||
}
|
||||
command += &key_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@ use komodo_client::{
|
||||
entities::{
|
||||
all_logs_success,
|
||||
docker::stack::SwarmStack,
|
||||
stack::{ComposeFile, ComposeService, StackServiceNames},
|
||||
stack::{
|
||||
AdditionalEnvFile, ComposeFile, ComposeService,
|
||||
StackServiceNames,
|
||||
},
|
||||
update::Log,
|
||||
},
|
||||
parsers::parse_multiline_command,
|
||||
@@ -30,6 +33,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,
|
||||
@@ -113,7 +142,7 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
replacers.extend(interpolator.secret_replacers);
|
||||
|
||||
// Env files are not supported by docker stack deploy so are ignored.
|
||||
let (run_directory, _) = match write_stack(
|
||||
let (run_directory, env_file_path) = match write_stack(
|
||||
&stack,
|
||||
repo.as_ref(),
|
||||
git_token,
|
||||
@@ -178,16 +207,53 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
let last_project_name = stack.project_name(false);
|
||||
let project_name = stack.project_name(true);
|
||||
|
||||
// Parse wrapper configuration once for reuse
|
||||
let compose_cmd_wrapper =
|
||||
parse_multiline_command(&stack.config.compose_cmd_wrapper);
|
||||
// If wrapper_include is empty but wrapper is set, use default ["deploy"] for backward compatibility
|
||||
let default_include = vec![String::from("deploy")];
|
||||
let wrapper_include =
|
||||
if stack.config.compose_cmd_wrapper_include.is_empty()
|
||||
&& !compose_cmd_wrapper.is_empty()
|
||||
{
|
||||
&default_include
|
||||
} else {
|
||||
&stack.config.compose_cmd_wrapper_include
|
||||
};
|
||||
|
||||
let env_file_args = env_file_args(
|
||||
env_file_path,
|
||||
&stack.config.additional_env_files,
|
||||
)?;
|
||||
|
||||
// Uses 'docker stack config' command to extract services (including image)
|
||||
// after performing interpolation
|
||||
{
|
||||
let command = format!("docker stack config -c {file_args}",);
|
||||
let command =
|
||||
format!("{env_file_args}docker stack config -c {file_args}",);
|
||||
let (command, wrapped) = match maybe_wrap_command(
|
||||
command,
|
||||
&compose_cmd_wrapper,
|
||||
wrapper_include,
|
||||
"config",
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(log) => {
|
||||
res.logs.push(log);
|
||||
return Ok(res);
|
||||
}
|
||||
};
|
||||
let mode = if wrapped || !env_file_args.is_empty() {
|
||||
KomodoCommandMode::Shell
|
||||
} else {
|
||||
KomodoCommandMode::Standard
|
||||
};
|
||||
let 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)
|
||||
@@ -231,8 +297,9 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
}
|
||||
|
||||
// Run stack deploy
|
||||
let mut command =
|
||||
format!("docker stack deploy --detach=false -c {file_args}");
|
||||
let mut command = format!(
|
||||
"{env_file_args}docker stack deploy --detach=true -c {file_args}"
|
||||
);
|
||||
if use_with_registry_auth {
|
||||
command += " --with-registry-auth";
|
||||
}
|
||||
@@ -240,19 +307,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(
|
||||
@@ -293,6 +359,43 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
|
||||
}
|
||||
}
|
||||
|
||||
/// Prefix docker stack deploy command.
|
||||
///
|
||||
/// Produces either
|
||||
/// - Empty string (no env files defined)
|
||||
/// - 'set -a && . .env1 && . .env2 && '
|
||||
fn env_file_args(
|
||||
env_file_path: Option<&str>,
|
||||
additional_env_files: &[AdditionalEnvFile],
|
||||
) -> anyhow::Result<String> {
|
||||
let mut res = String::new();
|
||||
|
||||
// Add additional env files (except komodo's own, which comes last)
|
||||
for file in additional_env_files
|
||||
.iter()
|
||||
.filter(|f| env_file_path != Some(f.path.as_str()))
|
||||
{
|
||||
let path = &file.path;
|
||||
write!(res, ". {path} && ").with_context(|| {
|
||||
format!("Failed to write env source arg for {path}")
|
||||
})?;
|
||||
}
|
||||
|
||||
// Add komodo's env file last for highest priority
|
||||
if let Some(file) = env_file_path {
|
||||
write!(res, ". {file} && ").with_context(|| {
|
||||
format!("Failed to write env source arg for {file}")
|
||||
})?;
|
||||
}
|
||||
|
||||
if !res.is_empty() {
|
||||
// Add 'set -a' so vars are auto exported to child 'docker stack' process.
|
||||
Ok(format!("set -a && {res}"))
|
||||
} else {
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument("RemoveStack", skip(res))]
|
||||
async fn remove_stack(
|
||||
stack: &str,
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use anyhow::{Context, anyhow};
|
||||
use futures_util::{Stream, StreamExt, TryStreamExt};
|
||||
use komodo_client::entities::{
|
||||
KOMODO_EXIT_CODE, NoData,
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,9 @@ pub fn periphery_config() -> &'static PeripheryConfig {
|
||||
ansi: env
|
||||
.periphery_logging_ansi
|
||||
.unwrap_or(config.logging.ansi),
|
||||
timestamps: env
|
||||
.periphery_logging_timestamps
|
||||
.unwrap_or(config.logging.timestamps),
|
||||
otlp_endpoint: env
|
||||
.periphery_logging_otlp_endpoint
|
||||
.unwrap_or(config.logging.otlp_endpoint),
|
||||
|
||||
@@ -94,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());
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user