Compare commits

..
20 Commits
Author SHA1 Message Date
mbecker20 a246eaf35a fix docker build requires xtask folder copy 2026-05-07 11:32:36 -07:00
0d462fb2e0 2.2.0 (#1387)
* fix login page import

* update to mantine v9, reduce the border radius on some things

* deploy 2.2.0-dev-1

* move theme to mogh ui

* start moving stuff to mogh_ui

* move more components to mogh_ui

* move section to mogh_ui

* move confirm button

* move colors and formatting to mogh_ui

* move more to mogh_ui

* move more components to mogh ui

* move entity header / page to mogh ui

* move monaco to mogh_monaco

* move config / monaco to mogh ui

* move text update modal to mogh ui, empty ui folder

* deploy 2.2.0-dev-2

* move auth hooks to mogh_ui

* move login to mogh ui

* move profile management to mogh ui

* add missing dedupe deps

* bump mogh ui deps

* expand shell process path static load

* move filterBySplit to mogh_ui

* align core aio dockerfile

* bump mogh ui

* add published mogh_auth_client

* comment about onboarding key

* fix lockfile

* bump mogh ui

* improve doc comment on CoreInfo public_key

* UI: topbar home butotn use Link instead of nav, cmd click open in new tab

* Minor: Update periphery.config.toml (#1365)

* Minor: Update periphery.config.toml

Make docs on ROOT_DIRECTORY more clear

This change relates to line:
> ## Each specific sub-directory (like ./stacks) can be overridden below.

The change includes `./builds` directory to the list as it also can be overridden. Thus, a reader will have better understanding of the resulting root directory structure

* Update compose.env

* fix cli multi arch dockerfile and bump other dockerfiles

* Add OIDC auto-redirect config to Core (#1339)

* Add OIDC auto-redirect config to Core

When KOMODO_OIDC_AUTO_REDIRECT=true, the login options response
includes oidc_auto_redirect, enabling the UI (via mogh-lib) to
automatically redirect unauthenticated users to the OIDC provider.
Users can bypass by appending ?disableAutoLogin to the login URL.

Depends on moghtech/lib UI change adding auto-redirect to LoginPage.

Closes #311

* Skip OIDC auto-redirect for Add Account link

The user dropdown's Add Account button navigates to /login to add
another account. When OIDC auto-redirect is enabled, this link must
opt out so the user can actually sign in as a different user instead
of being redirected back to the already-authenticated OIDC session.

* bump deps

* deploy 2.2.0-dev-3

* Fix swarm deployment Tasks and Inspect tabs

* Fix build cancel button variant default

* RunSync: Make sure resource sync changes run after repo changes due to dependency

* deploy 2.2.0-dev-4

* deploy 2.2.0-dev-5

* fmt

* impl mogh server 1.5.0 configuration security headers

* to the user

* agents send

* let user specify if they want command to be run in shell-mode (#1265)

* allow users to add commands as shell-mode

* revert package.json yarn auto-change

* cargo fmt

* bump bollard and other deps

* gen client

* deploy 2.2.0-dev-6

* bump dockerfile rust version 1.95.0

* document new core configurations

* deploy 2.2.0-dev-7

* 2 small UI fixes (#1379)

* fix: prevent horizontal page scroll on long action log lines

Add white-space: pre-wrap to Code component pre elements rendering
stdout/stderr in action last-run, update details, and build info views.

* fix: UI Defined and Files on Server text too large in stacks table

---------

Co-authored-by: twalts <t.mwalton@yahoo.com>

* X-Frame-Options

* Add support to generate resource json schema (#1337)

* add support to generate resource json schema

* remove unnecessary renames

---------

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

* generate resource schema

* resources.json

* deploy 2.2.0-dev-8

* fix build.version schema (can be string)

* add resource toml comments

* deploy 2.2.0-dev-9

* use draft 07 for better taplo compat

* deploy 2.2.0-dev-10

* builder fix exported toml server_id -> server

* deploy 2.2.0-dev-11

* bump mogh auth

* deploy 2.2.0-dev-12

* Allow excluding services from global auto update  (#1279)

* add ignore_polling_services for global auto update

* rename ignore_polling_services to auto_update_skip_services and reorganize

* fix stack auto_update check show skip services

* deploy 2.2.0-dev-13

* 2.2.0

* bold "relative to the root of the repo"

---------

Co-authored-by: Andrii Bratanin <20169213+andriibratanin@users.noreply.github.com>
Co-authored-by: John Maguire <contact@johnmaguire.me>
Co-authored-by: Shen Li <dustet@gmail.com>
Co-authored-by: T <github@mail.taylor.media>
Co-authored-by: twalts <t.mwalton@yahoo.com>
Co-authored-by: bytedream <git+github@bytedream.dev>
Co-authored-by: mateuszziolkowski <88323671+mateuszziolkowski@users.noreply.github.com>
2026-05-07 11:19:55 -07:00
John MaguireandGitHub ca01407090 Add per-provider user registration control (#1321)
Add granular registration flags:
- KOMODO_DISABLE_LOCAL_USER_REGISTRATION — blocks local signups,
  hides Sign Up button, while allowing OIDC registration
- KOMODO_DISABLE_OIDC_USER_REGISTRATION — blocks OIDC signups
  while allowing local registration

Both are optional and fall back to the existing
KOMODO_DISABLE_USER_REGISTRATION when not set. This enables the
common pattern of letting your OIDC provider control access while
preventing direct local account creation.

Depends on mogh-lib change adding per-provider registration methods
to the AuthImpl trait.

Closes #1087
2026-05-06 16:06:55 -07:00
Maxwell BeckerandGitHub 20b9d16d4b 2.1.2 (#1347)
* 2.1.2 fix multi file service override UI crash

* deploy 2.1.2-dev-1

* 2.1.2
2026-04-10 11:32:17 -07:00
Maxwell BeckerandGitHub 7c95823eb0 2.1.1 (#1314)
* start 2.1.1 to fix swarm auto updates

* correctly extract image and digest from swarm stacks / deployments

* 2.1.1 fix swarm auto updates not picking up

* fmt
2026-04-02 14:55:44 -07:00
mbecker20 254622078e rust 1.94.1 2026-04-01 16:11:27 -07:00
38421d6719 2.1.0 (#1285)
* fix swarm config content hidden

* 2.0.1-dev-1

* bump ts deps

* align swarm config links

* StackServiceRun command is actually optional

* fix swarm not included in read resource toml

* deploy 2.0.1-dev-2

* logger support disabling timestamps (eg if they are already provided by docker logs)

* full opacity tag color selector

* Add UpdateSwarmNode execution and convert several large repetive blocks to macro handling

* add node update functionality

* deploy 2.0.1-dev-3

* deploy 2.1.0-dev-1

* toml variable export sorted by name

* deploy 2.1.0-dev-2

* dont use detach=false because it can hang indefinitely on misconfigured stacks

* deploy 2.1.0-dev-3

* swarm stack deploy explicitly '--detach=true' to avoid future changes in behavior

* fmt

* UI: Stack config: Fix Add Env File button

* fix swarm error propogation, add server connection error hover message

* deploy 2.1.0-dev-4

* fix service rm (should be stack rm

* fix container ports not displaying when server address not available for link

* fix build registry custom org configuration

* github action: comment out cache step (leads to no space on device)

* fix missing nullish check when selecting stack (#1287)

* fix container selector null crash

* provider selector custom input label adheres to showLabel

* fix: show/hide button non-functional in stack, sync, and build info views (#1267)

Co-authored-by: twalts <t.mwalton@yahoo.com>

* swarm stack support env vars through shell source file method

* tweaks

* bump deps

* deploy 2.1.0-dev-5

* auto update should work with swarm

* deploy 2.1.0-dev-6

* 2.1.0

---------

Co-authored-by: Shen Li <dustet@gmail.com>
Co-authored-by: T <github@mail.taylor.media>
Co-authored-by: twalts <t.mwalton@yahoo.com>
2026-04-01 16:08:29 -07:00
mbecker20 0f96794559 add ghpages CNAME 2026-03-26 13:05:06 -07:00
mbecker20 d79fefff17 add breaking notice about bullseye and periphery compose example with correct key path 2026-03-26 13:02:39 -07:00
mbecker20 4626b4a477 fix deep compare short path for falsy / null 2026-03-25 02:27:46 -07:00
mbecker20 150a8a160d fix terminals page new terminals menu 2026-03-25 01:56:20 -07:00
mbecker20 5b3bb9b8bb Use :2 to stay up to date
with all future Komodo v2 releases.
2026-03-24 18:40:19 -07:00
mbecker20 79831f29af Improved auto updates 2026-03-24 18:38:03 -07:00
mbecker20 b68f5abd25 fix: max width on git commit messages 2026-03-24 17:46:00 -07:00
mbecker20 2325899ebf quick fix swarm config (UI only) 2026-03-24 17:42:09 -07:00
mbecker20 23fd208f0f cli docker alias need :2 in the image 2026-03-24 16:53:45 -07:00
mbecker20 19904e00b9 docs on terminals 2026-03-24 16:48:58 -07:00
mbecker20 8880b1c948 km exec finds server name for connection log 2026-03-24 16:45:33 -07:00
mbecker20 e47c8d2b62 docsite fix privileged typo 2026-03-24 16:17:31 -07:00
mbecker20 1addb8fccd update roadmap with swarm support 2026-03-24 07:04:25 -07:00
438 changed files with 12540 additions and 14057 deletions
+4 -1
View File
@@ -1,2 +1,5 @@
[alias]
xtask = "run --package xtask --"
[build]
rustflags = ["-Wunused-crate-dependencies"]
rustflags = ["-Wunused-crate-dependencies"]
+10 -10
View File
@@ -22,16 +22,16 @@ jobs:
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: 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
Generated
+1688 -852
View File
File diff suppressed because it is too large Load Diff
+33 -31
View File
@@ -3,21 +3,19 @@ resolver = "2"
members = [
"bin/*",
"lib/*",
"xtask",
"client/core/rs",
"client/periphery/rs",
]
[workspace.package]
version = "2.0.0"
version = "2.2.0"
edition = "2024"
authors = ["mbecker20 <becker.maxh@gmail.com>"]
license = "GPL-3.0-or-later"
repository = "https://github.com/moghtech/komodo"
homepage = "https://komo.do"
[profile.release]
strip = "debuginfo"
[workspace.dependencies]
# LOCAL
komodo_client = { path = "client/core/rs" }
@@ -33,29 +31,29 @@ git = { path = "lib/git" }
# MOGH
slack = { version = "2.0.0", package = "slack_client_rs", default-features = false, features = ["rustls"] }
mogh_error = { version = "1.0.3", default-features = false }
mogh_error = { version = "1.0.4", default-features = false }
derive_default_builder = "0.1.8"
async_timing_util = "1.1.0"
mogh_auth_client = "1.2.2"
mogh_auth_server = "1.2.13"
mogh_auth_client = "1.5.0"
mogh_auth_server = "1.5.0"
mogh_secret_file = "1.0.1"
mogh_validations = "1.0.1"
mogh_rate_limit = "1.0.1"
partial_derive2 = "0.4.5"
partial_derive2 = "0.5.0"
mongo_indexed = "2.0.2"
mogh_resolver = "1.0.0"
mogh_config = "1.0.4"
mogh_logger = "1.3.2"
mogh_server = "1.4.5"
mogh_config = "1.1.0"
mogh_logger = "1.3.3"
mogh_server = "1.5.0"
toml_pretty = "2.0.0"
mogh_cache = "1.1.1"
mogh_cache = "1.1.2"
mogh_pki = "1.1.3"
mungos = "3.2.2"
svi = "1.2.0"
# ASYNC
reqwest = { version = "0.13.2", default-features = false, features = ["json", "stream", "form", "query", "rustls"] }
tokio = { version = "1.50.0", features = ["full"] }
reqwest = { version = "0.13.3", default-features = false, features = ["json", "stream", "form", "query", "rustls"] }
tokio = { version = "1.52.2", features = ["full"] }
tokio-util = { version = "0.7.18", features = ["io", "codec"] }
tokio-stream = { version = "0.1.18", features = ["sync"] }
pin-project-lite = "0.2.17"
@@ -64,20 +62,20 @@ arc-swap = "1.9.0"
# SERVER
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"] }
axum = { version = "0.8.9", features = ["ws", "json", "macros"] }
axum-extra = { version = "0.12.6", features = ["typed-header"] }
# OPENAPI
utoipa-scalar = { version = "0.3.0", features = ["axum"] }
utoipa = "5.4.0"
utoipa = "5.5.0"
# SER/DE
ipnetwork = { version = "0.21.1", features = ["serde"] }
indexmap = { version = "2.13.0", features = ["serde"] }
indexmap = { version = "2.14.0", features = ["serde"] }
serde = { version = "1.0.227", features = ["derive"] }
strum = { version = "0.28.0", features = ["derive"] }
bson = { version = "2.15.0" } # must keep in sync with mongodb version
toml = "1.1.0"
toml = "1.1.2"
serde_yaml_ng = "0.10.0"
serde_json = "1.0.149"
serde_qs = "1.1.0"
@@ -91,34 +89,34 @@ thiserror = "2.0.18"
tracing = "0.1.44"
# CONFIG
clap = { version = "4.5.60", features = ["derive"] }
clap = { version = "4.6.1", features = ["derive"] }
dotenvy = "0.15.7"
envy = "0.4.2"
# CRYPTO / AUTH
uuid = { version = "1.21.0", features = ["v4", "fast-rng", "serde"] }
rustls = { version = "0.23.37", features = ["aws-lc-rs"] }
uuid = { version = "1.23.1", features = ["v4", "fast-rng", "serde"] }
rustls = { version = "0.23.40", features = ["aws-lc-rs"] }
data-encoding = "2.10.0"
urlencoding = "2.1.3"
bcrypt = "0.19.0"
hmac = "0.12.1"
sha1 = "0.10.6"
sha2 = "0.10.9"
rand = "0.10.0"
hmac = "0.13.0"
sha1 = "0.11.0"
sha2 = "0.11.0"
rand = "0.10.1"
hex = "0.4.3"
# SYSTEM
hickory-resolver = "0.25.2"
hickory-resolver = "0.26.1"
portable-pty = "0.9.0"
shell-escape = "0.1.5"
crossterm = "0.29.0"
bollard = "0.20.2"
bollard = "0.21.0"
sysinfo = "0.38.4"
shlex = "1.3.0"
# CLOUD
aws-config = "1.8.15"
aws-sdk-ec2 = "1.220.0"
aws-config = "1.8.16"
aws-sdk-ec2 = "1.224.0"
aws-credential-types = "1.2.14"
## CRON
@@ -128,7 +126,8 @@ chrono = "0.4.44"
croner = "3.0.1"
# MISC
async-compression = { version = "0.4.41", features = ["tokio", "gzip"] }
async-compression = { version = "0.4.42", features = ["tokio", "gzip"] }
schemars = { version = "1.2.1", features = ["indexmap2"] }
derive_builder = "0.20.2"
comfy-table = "7.2.2"
typeshare = "1.0.5"
@@ -136,3 +135,6 @@ wildcard = "0.3.0"
colored = "3.1.1"
bytes = "1.11.1"
regex = "1.12.3"
[profile.release]
strip = "debuginfo"
+2 -1
View File
@@ -1,7 +1,7 @@
## Builds the Komodo Core, Periphery, and Util binaries
## for a specific architecture. Requires OpenSSL 3 or later.
FROM rust:1.94.0-bookworm AS builder
FROM rust:1.95.0-bookworm AS builder
RUN cargo install cargo-strip
WORKDIR /builder
@@ -12,6 +12,7 @@ COPY ./client/periphery ./client/periphery
COPY ./bin/core ./bin/core
COPY ./bin/periphery ./bin/periphery
COPY ./bin/cli ./bin/cli
COPY ./xtask ./xtask
# Compile bin
RUN \
+1 -1
View File
@@ -3,7 +3,7 @@
## Uses chef for dependency caching to help speed up back-to-back builds.
FROM lukemathwalker/cargo-chef:latest-rust-1.94.0-bookworm AS chef
FROM lukemathwalker/cargo-chef:latest-rust-1.95.0-bookworm AS chef
WORKDIR /builder
# Plan just the RECIPE to see if things have changed
+1 -1
View File
@@ -1,4 +1,4 @@
FROM rust:1.94.0-bullseye AS builder
FROM rust:1.95.0-trixie AS builder
RUN cargo install cargo-strip
WORKDIR /builder
+1 -1
View File
@@ -9,7 +9,7 @@ ARG AARCH64_BINARIES=${BINARIES_IMAGE}-aarch64
FROM ${X86_64_BINARIES} AS x86_64
FROM ${AARCH64_BINARIES} AS aarch64
FROM debian:bullseye-slim
FROM debian:trixie-slim
WORKDIR /app
+131 -550
View File
@@ -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)) => {
+6 -3
View File
@@ -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::{
@@ -138,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
+3
View File
@@ -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),
+4 -2
View File
@@ -1,7 +1,7 @@
## All in one, multi stage compile + runtime Docker build for your architecture.
# Build Core
FROM rust:1.94.0-trixie AS core-builder
FROM rust:1.95.0-trixie AS core-builder
RUN cargo install cargo-strip
WORKDIR /builder
@@ -11,6 +11,7 @@ COPY ./client/core/rs ./client/core/rs
COPY ./client/periphery ./client/periphery
COPY ./bin/core ./bin/core
COPY ./bin/cli ./bin/cli
COPY ./xtask ./xtask
# Compile app
RUN cargo build -p komodo_core --release && \
@@ -58,7 +59,8 @@ ENV KOMODO_CLI_CONFIG_PATHS="/config"
# This ensures any `komodo.cli.*` takes precedence over the Core `/config/*config.*`
ENV KOMODO_CLI_CONFIG_KEYWORDS="*config.*,*komodo.cli*.*"
CMD [ "/bin/bash", "-c", "update-ca-certificates && core" ]
ENTRYPOINT [ "entrypoint.sh" ]
CMD [ "core" ]
# Label to prevent Komodo from stopping with StopAllContainers
LABEL komodo.skip="true"
+23 -23
View File
@@ -27,7 +27,7 @@ pub async fn send_alert(
SeverityLevel::Critical => {
let err = err
.as_ref()
.map(|e| format!("\n**error**: {e}"))
.map(|e| format!("\n**error**: {e:#?}"))
.unwrap_or_default();
format!(
"{level} | Swarm **{name}** is **unhealthy** ❌\n{link}{err}"
@@ -36,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,
@@ -84,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,
+25 -25
View File
@@ -260,7 +260,7 @@ 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}"
@@ -269,6 +269,30 @@ fn standard_alert_content(alert: &Alert) -> String {
_ => unreachable!(),
}
}
AlertData::ServerUnreachable {
id,
name,
region,
err,
} => {
let region = fmt_region(region);
let link = resource_link(ResourceTargetVariant::Server, id);
match alert.level {
SeverityLevel::Ok => {
format!("{level} | {name}{region} is now connected\n{link}")
}
SeverityLevel::Critical => {
let err = err
.as_ref()
.map(|e| format!("\nerror: {e:#?}"))
.unwrap_or_default();
format!(
"{level} | {name}{region} is unreachable ❌\n{link}{err}"
)
}
_ => unreachable!(),
}
}
AlertData::ServerVersionMismatch {
id,
name,
@@ -291,30 +315,6 @@ fn standard_alert_content(alert: &Alert) -> String {
}
}
}
AlertData::ServerUnreachable {
id,
name,
region,
err,
} => {
let region = fmt_region(region);
let link = resource_link(ResourceTargetVariant::Server, id);
match alert.level {
SeverityLevel::Ok => {
format!("{level} | {name}{region} is now connected\n{link}")
}
SeverityLevel::Critical => {
let err = err
.as_ref()
.map(|e| format!("\nerror: {e:#?}"))
.unwrap_or_default();
format!(
"{level} | {name}{region} is unreachable ❌\n{link}{err}"
)
}
_ => unreachable!(),
}
}
AlertData::ServerCpu {
id,
name,
+30 -30
View File
@@ -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,
+4 -4
View File
@@ -509,10 +509,10 @@ pub async fn validate_cancel_build(
)?;
match (latest_build, latest_cancel) {
(Some(build), Some(cancel)) => {
if cancel.start_ts > build.start_ts {
return Err(anyhow!("Build has already been cancelled"));
}
(Some(build), Some(cancel))
if cancel.start_ts > build.start_ts =>
{
return Err(anyhow!("Build has already been cancelled"));
}
(None, _) => return Err(anyhow!("No build in progress")),
_ => {}
+81 -39
View File
@@ -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,
},
};
@@ -244,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]
@@ -255,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;
}
@@ -267,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)
));
}
}
@@ -320,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;
}
@@ -332,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();
+1
View File
@@ -148,6 +148,7 @@ pub enum ExecuteRequest {
// ==== SWARM ====
RemoveSwarmNodes(RemoveSwarmNodes),
UpdateSwarmNode(UpdateSwarmNode),
RemoveSwarmStacks(RemoveSwarmStacks),
RemoveSwarmServices(RemoveSwarmServices),
CreateSwarmConfig(CreateSwarmConfig),
+5 -6
View File
@@ -698,13 +698,12 @@ pub async fn validate_cancel_repo_build(
)?;
match (latest_build, latest_cancel) {
(Some(build), Some(cancel)) => {
if cancel.start_ts > build.start_ts {
return Err(anyhow!(
"Repo build has already been cancelled"
));
}
(Some(build), Some(cancel))
if cancel.start_ts > build.start_ts =>
{
return Err(anyhow!("Repo build has already been cancelled"));
}
(None, _) => return Err(anyhow!("No repo build in progress")),
_ => {}
};
+67 -5
View File
@@ -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;
@@ -76,6 +72,72 @@ 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",
+18 -15
View File
@@ -283,6 +283,7 @@ impl Resolve<ExecuteArgs> for RunSync {
Default::default()
};
// New resource types need to be added here manually.
if deploy_cache.is_empty()
&& resource_sync_deltas.no_changes()
&& server_deltas.no_changes()
@@ -339,10 +340,7 @@ impl Resolve<ExecuteArgs> for RunSync {
)
.await,
);
maybe_extend(
&mut update.logs,
ResourceSync::execute_sync_updates(resource_sync_deltas).await,
);
maybe_extend(
&mut update.logs,
Server::execute_sync_updates(server_deltas).await,
@@ -356,38 +354,43 @@ impl Resolve<ExecuteArgs> for RunSync {
Action::execute_sync_updates(action_deltas).await,
);
// Dependent on server
// Depends on server
maybe_extend(
&mut update.logs,
Swarm::execute_sync_updates(swarm_deltas).await,
);
// Depends on server
maybe_extend(
&mut update.logs,
Builder::execute_sync_updates(builder_deltas).await,
);
// Depends on server / builder
maybe_extend(
&mut update.logs,
Repo::execute_sync_updates(repo_deltas).await,
);
// Dependant on builder
// Depends on builder / repo
maybe_extend(
&mut update.logs,
Build::execute_sync_updates(build_deltas).await,
);
// Dependant on server / build
maybe_extend(
&mut update.logs,
Deployment::execute_sync_updates(deployment_deltas).await,
);
// stack only depends on server, but maybe will depend on build later.
// Depends on server / repo
maybe_extend(
&mut update.logs,
Stack::execute_sync_updates(stack_deltas).await,
);
// Dependant on everything
// Depends on repo
maybe_extend(
&mut update.logs,
ResourceSync::execute_sync_updates(resource_sync_deltas).await,
);
// Depends on server / build
maybe_extend(
&mut update.logs,
Deployment::execute_sync_updates(deployment_deltas).await,
);
// Depends on everything
maybe_extend(
&mut update.logs,
Procedure::execute_sync_updates(procedure_deltas).await,
@@ -1,7 +1,7 @@
use anyhow::{Context, anyhow};
use axum::http::HeaderMap;
use hex::ToHex;
use hmac::{Hmac, Mac};
use hmac::{Hmac, KeyInit as _, Mac};
use serde::Deserialize;
use sha2::Sha256;
+19 -13
View File
@@ -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,
@@ -215,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)
+14 -4
View File
@@ -555,8 +555,9 @@ 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:#}",
);
}
}
@@ -775,7 +776,13 @@ pub async fn check_stack_for_update_inner(
if image.is_empty() ||
// Images with a hardcoded digest can't have update.
image.contains('@')
image.contains('@') ||
// Services explicitly excluded from global auto-update checks.
// Manual checks should still evaluate all services.
(wait_for_auto_update &&
stack.config.auto_update_skip_services.contains(
&service.service_name,
))
{
service.image_digest = None;
continue;
@@ -939,7 +946,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
+15
View File
@@ -205,6 +205,20 @@ impl AuthImpl for KomodoAuthImpl {
core_config().disable_user_registration
}
fn local_registration_disabled(&self) -> bool {
let config = core_config();
config
.disable_local_user_registration
.unwrap_or(config.disable_user_registration)
}
fn oidc_registration_disabled(&self) -> bool {
let config = core_config();
config
.disable_oidc_user_registration
.unwrap_or(config.disable_user_registration)
}
fn validate_username(
&self,
username: &str,
@@ -348,6 +362,7 @@ impl AuthImpl for KomodoAuthImpl {
additional_audiences: config
.oidc_additional_audiences
.clone(),
auto_redirect: config.oidc_auto_redirect,
}
});
Some(&OIDC_CONFIG)
+27
View File
@@ -235,6 +235,9 @@ pub fn core_config() -> &'static CoreConfig {
env.komodo_oidc_additional_audiences,
)
.unwrap_or(config.oidc_additional_audiences),
oidc_auto_redirect: env
.komodo_oidc_auto_redirect
.unwrap_or(config.oidc_auto_redirect),
google_oauth: NamedOauthConfig {
enabled: env
.komodo_google_oauth_enabled
@@ -312,6 +315,21 @@ pub fn core_config() -> &'static CoreConfig {
session_allow_cross_site: env
.komodo_session_allow_cross_site
.unwrap_or(config.session_allow_cross_site),
x_content_type_options: env
.komodo_x_content_type_options
.unwrap_or(config.x_content_type_options),
x_frame_options: env
.komodo_x_frame_options
.unwrap_or(config.x_frame_options),
x_xss_protection: env
.komodo_x_xss_protection
.unwrap_or(config.x_xss_protection),
referrer_policy: env
.komodo_referrer_policy
.unwrap_or(config.referrer_policy),
content_security_policy: env
.komodo_content_security_policy
.unwrap_or(config.content_security_policy),
resource_poll_interval: env
.komodo_resource_poll_interval
.unwrap_or(config.resource_poll_interval),
@@ -345,6 +363,12 @@ pub fn core_config() -> &'static CoreConfig {
disable_user_registration: env
.komodo_disable_user_registration
.unwrap_or(config.disable_user_registration),
disable_local_user_registration: env
.komodo_disable_local_user_registration
.or(config.disable_local_user_registration),
disable_oidc_user_registration: env
.komodo_disable_oidc_user_registration
.or(config.disable_oidc_user_registration),
disable_non_admin_create: env
.komodo_disable_non_admin_create
.unwrap_or(config.disable_non_admin_create),
@@ -375,6 +399,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),
+7
View File
@@ -307,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() {
File diff suppressed because it is too large Load Diff
+144 -457
View File
@@ -123,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();
+40 -4
View File
@@ -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())
+4 -5
View File
@@ -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,
@@ -108,9 +107,9 @@ pub async fn refresh_swarm_cache(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 refresh_swarm_cache(swarm: &Swarm, force: bool) {
state: SwarmState::Unknown,
inspect: None,
lists: None,
err: Some(format_serror(&e.into())),
err: Some(e.into()),
}
.into(),
)
+172 -684
View File
@@ -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>(
&params.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>(
&params.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>(
&params.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>(
&params.build,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.build = build.id;
}
Execution::Deploy(params) => {
let deployment =
super::get_check_permissions::<Deployment>(
&params.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>(
&params.deployment,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.deployment = deployment.id;
}
Execution::StartDeployment(params) => {
let deployment =
super::get_check_permissions::<Deployment>(
&params.deployment,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.deployment = deployment.id;
}
Execution::RestartDeployment(params) => {
let deployment =
super::get_check_permissions::<Deployment>(
&params.deployment,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.deployment = deployment.id;
}
Execution::PauseDeployment(params) => {
let deployment =
super::get_check_permissions::<Deployment>(
&params.deployment,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.deployment = deployment.id;
}
Execution::UnpauseDeployment(params) => {
let deployment =
super::get_check_permissions::<Deployment>(
&params.deployment,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.deployment = deployment.id;
}
Execution::StopDeployment(params) => {
let deployment =
super::get_check_permissions::<Deployment>(
&params.deployment,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.deployment = deployment.id;
}
Execution::DestroyDeployment(params) => {
let deployment =
super::get_check_permissions::<Deployment>(
&params.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>(
&params.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>(
&params.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>(
&params.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>(
&params.repo,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.repo = repo.id;
}
Execution::StartContainer(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::RestartContainer(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PauseContainer(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::UnpauseContainer(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::StopContainer(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::DestroyContainer(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::StartAllContainers(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::RestartAllContainers(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PauseAllContainers(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::UnpauseAllContainers(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::StopAllContainers(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PruneContainers(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::DeleteNetwork(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PruneNetworks(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::DeleteImage(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PruneImages(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::DeleteVolume(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PruneVolumes(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PruneDockerBuilders(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PruneBuildx(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::PruneSystem(params) => {
let server = super::get_check_permissions::<Server>(
&params.server,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.server = server.id;
}
Execution::RunSync(params) => {
let sync = super::get_check_permissions::<ResourceSync>(
&params.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>(
&params.sync,
user,
PermissionLevel::Write.into(),
)
.await?;
params.sync = sync.id;
}
Execution::DeployStack(params) => {
let stack = super::get_check_permissions::<Stack>(
&params.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>(
&params.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>(
&params.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>(
&params.stack,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.stack = stack.id;
}
Execution::RestartStack(params) => {
let stack = super::get_check_permissions::<Stack>(
&params.stack,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.stack = stack.id;
}
Execution::PauseStack(params) => {
let stack = super::get_check_permissions::<Stack>(
&params.stack,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.stack = stack.id;
}
Execution::UnpauseStack(params) => {
let stack = super::get_check_permissions::<Stack>(
&params.stack,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.stack = stack.id;
}
Execution::StopStack(params) => {
let stack = super::get_check_permissions::<Stack>(
&params.stack,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.stack = stack.id;
}
Execution::DestroyStack(params) => {
let stack = super::get_check_permissions::<Stack>(
&params.stack,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.stack = stack.id;
}
Execution::RunStackService(params) => {
let stack = super::get_check_permissions::<Stack>(
&params.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>(
&params.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>(
&params.$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>(
&params.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>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::RemoveSwarmStacks(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::RemoveSwarmServices(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::CreateSwarmConfig(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::RotateSwarmConfig(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::RemoveSwarmConfigs(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::CreateSwarmSecret(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::RotateSwarmSecret(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.swarm,
user,
PermissionLevel::Execute.into(),
)
.await?;
params.swarm = swarm.id;
}
Execution::RemoveSwarmSecrets(params) => {
let swarm = super::get_check_permissions::<Swarm>(
&params.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>(
&params.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"),
],
);
}
}
+1
View File
@@ -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(
+22 -12
View File
@@ -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(())
}
+1 -1
View File
@@ -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 =
+21 -30
View File
@@ -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);
}
+18 -136
View File
@@ -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},
@@ -341,123 +339,7 @@ impl ResourceSyncTrait for Procedure {
mut original: Self::Config,
update: Self::PartialConfig,
) -> anyhow::Result<Self::ConfigDiff> {
let all = all_resources_cache().load();
for stage in &mut original.stages {
for execution in &mut stage.executions {
// Replaces an id field on config with the resource name from `all`.
macro_rules! replace_id_with_name {
($($Variant:ident => $field:ident, $collection:ident);* $(;)?) => {
match &mut execution.execution {
$(
Execution::$Variant(config) => {
config.$field = all
.$collection
.get(&config.$field)
.map(|r| r.name.clone())
.unwrap_or_default();
}
)*
// SendAlert maps a Vec of alerter ids
Execution::SendAlert(config) => {
config.alerters = config
.alerters
.iter()
.map(|alerter| {
all
.alerters
.get(alerter)
.map(|a| a.name.clone())
.unwrap_or_default()
})
.collect();
}
// No-op variants
Execution::None(_)
| Execution::BatchRunProcedure(_)
| Execution::BatchRunAction(_)
| Execution::BatchRunBuild(_)
| Execution::BatchDeploy(_)
| Execution::BatchDestroyDeployment(_)
| Execution::BatchCloneRepo(_)
| Execution::BatchPullRepo(_)
| Execution::BatchBuildRepo(_)
| Execution::BatchDeployStack(_)
| Execution::BatchDeployStackIfChanged(_)
| Execution::BatchPullStack(_)
| Execution::BatchDestroyStack(_)
| Execution::ClearRepoCache(_)
| Execution::BackupCoreDatabase(_)
| Execution::GlobalAutoUpdate(_)
| Execution::RotateAllServerKeys(_)
| Execution::RotateCoreKeys(_)
| Execution::Sleep(_) => {}
}
};
}
replace_id_with_name!(
RunProcedure => procedure, procedures;
RunAction => action, actions;
RunBuild => build, builds;
CancelBuild => build, builds;
Deploy => deployment, deployments;
PullDeployment => deployment, deployments;
StartDeployment => deployment, deployments;
RestartDeployment => deployment, deployments;
PauseDeployment => deployment, deployments;
UnpauseDeployment => deployment, deployments;
StopDeployment => deployment, deployments;
DestroyDeployment => deployment, deployments;
CloneRepo => repo, repos;
PullRepo => repo, repos;
BuildRepo => repo, repos;
CancelRepoBuild => repo, repos;
StartContainer => server, servers;
RestartContainer => server, servers;
PauseContainer => server, servers;
UnpauseContainer => server, servers;
StopContainer => server, servers;
DestroyContainer => server, servers;
StartAllContainers => server, servers;
RestartAllContainers => server, servers;
PauseAllContainers => server, servers;
UnpauseAllContainers => server, servers;
StopAllContainers => server, servers;
PruneContainers => server, servers;
DeleteNetwork => server, servers;
PruneNetworks => server, servers;
DeleteImage => server, servers;
PruneImages => server, servers;
DeleteVolume => server, servers;
PruneVolumes => server, servers;
PruneDockerBuilders => server, servers;
PruneBuildx => server, servers;
PruneSystem => server, servers;
RunSync => sync, syncs;
CommitSync => sync, syncs;
DeployStack => stack, stacks;
DeployStackIfChanged => stack, stacks;
PullStack => stack, stacks;
StartStack => stack, stacks;
RestartStack => stack, stacks;
PauseStack => stack, stacks;
UnpauseStack => stack, stacks;
StopStack => stack, stacks;
DestroyStack => stack, stacks;
RunStackService => stack, stacks;
TestAlerter => alerter, alerters;
RemoveSwarmNodes => swarm, swarms;
RemoveSwarmStacks => swarm, swarms;
RemoveSwarmServices => swarm, swarms;
CreateSwarmConfig => swarm, swarms;
RotateSwarmConfig => swarm, swarms;
RemoveSwarmConfigs => swarm, swarms;
CreateSwarmSecret => swarm, swarms;
RotateSwarmSecret => swarm, swarms;
RemoveSwarmSecrets => swarm, swarms;
);
}
}
replace_procedure_stage_ids_with_names(&mut original.stages);
Ok(original.partial_diff(update))
}
}
+57 -519
View File
@@ -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 {
@@ -458,510 +458,48 @@ impl ToToml for Builder {
toml.push_str("\nparams = {}");
}
}
fn edit_config_object(
_resource: &ResourceToml<Self::PartialConfig>,
config: IndexMap<String, serde_json::Value>,
) -> anyhow::Result<IndexMap<String, serde_json::Value>> {
config
.into_iter()
.map(|(key, value)| {
#[allow(clippy::single_match)]
match key.as_str() {
"params" => match value {
serde_json::Value::Object(obj) => Ok((
key,
serde_json::Value::Object(
obj
.into_iter()
.map(|(key, value)| {
match key.as_str() {
"server_id" => {
return (String::from("server"), value);
}
_ => {}
}
(key, value)
})
.collect(),
),
)),
value => Ok((key, value)),
},
_ => Ok((key, value)),
}
})
.collect()
}
}
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(
+2 -1
View File
@@ -1,6 +1,6 @@
## All in one, multi stage compile + runtime Docker build for your architecture.
FROM rust:1.94.0-trixie AS builder
FROM rust:1.95.0-trixie AS builder
RUN cargo install cargo-strip
WORKDIR /builder
@@ -9,6 +9,7 @@ COPY ./lib ./lib
COPY ./client/core/rs ./client/core/rs
COPY ./client/periphery ./client/periphery
COPY ./bin/periphery ./bin/periphery
COPY ./xtask ./xtask
# Compile app
RUN cargo build -p komodo_periphery --release && cargo strip
+5 -1
View File
@@ -271,7 +271,11 @@ impl Resolve<crate::api::Args> for build::Build {
"Pre Build",
pre_build_path.as_path(),
&pre_build.command,
KomodoCommandMode::Multiline,
if pre_build.shell_mode {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Multiline
},
&replacers,
)
.instrument(span)
+10 -2
View File
@@ -490,7 +490,11 @@ impl Resolve<crate::api::Args> for ComposeUp {
"Pre Deploy",
pre_deploy_path.as_path(),
&stack.config.pre_deploy.command,
KomodoCommandMode::Multiline,
if stack.config.pre_deploy.shell_mode {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Multiline
},
&replacers,
)
.instrument(span)
@@ -760,7 +764,11 @@ impl Resolve<crate::api::Args> for ComposeUp {
"Post Deploy",
post_deploy_path.as_path(),
&stack.config.post_deploy.command,
KomodoCommandMode::Multiline,
if stack.config.post_deploy.shell_mode {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Multiline
},
&replacers,
)
.instrument(span)
+2 -6
View File
@@ -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;
}
}
+53 -6
View File
@@ -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,
@@ -139,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,
@@ -218,10 +221,16 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
&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,
@@ -234,7 +243,7 @@ impl Resolve<crate::api::Args> for DeploySwarmStack {
return Ok(res);
}
};
let mode = if wrapped {
let mode = if wrapped || !env_file_args.is_empty() {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Standard
@@ -288,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";
}
@@ -349,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
View File
@@ -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),
+8 -5
View File
@@ -10,7 +10,7 @@ use crate::state::container_stats;
use super::{
DockerClient, convert_health_config, convert_mount,
convert_mount_point_type, convert_resources_ulimits,
convert_resources_ulimits,
};
impl DockerClient {
@@ -360,10 +360,7 @@ impl DockerClient {
.unwrap_or_default()
.into_iter()
.map(|mount| MountPoint {
typ: mount
.typ
.map(convert_mount_point_type)
.unwrap_or_default(),
typ: mount.typ,
name: mount.name,
source: mount.source,
destination: mount.destination,
@@ -482,6 +479,9 @@ fn convert_summary_container_state(
bollard::config::ContainerSummaryStateEnum::EXITED => {
ContainerStateStatusEnum::Exited
}
bollard::config::ContainerSummaryStateEnum::STOPPING => {
ContainerStateStatusEnum::Stopping
}
bollard::config::ContainerSummaryStateEnum::REMOVING => {
ContainerStateStatusEnum::Removing
}
@@ -516,6 +516,9 @@ fn convert_container_state_status(
bollard::config::ContainerStateStatusEnum::REMOVING => {
ContainerStateStatusEnum::Removing
}
bollard::config::ContainerStateStatusEnum::STOPPING => {
ContainerStateStatusEnum::Stopping
}
bollard::config::ContainerStateStatusEnum::DEAD => {
ContainerStateStatusEnum::Dead
}
+7 -36
View File
@@ -150,17 +150,14 @@ fn convert_mount(mount: bollard::models::Mount) -> Mount {
}
}
fn convert_mount_type(
typ: bollard::config::MountTypeEnum,
) -> MountTypeEnum {
fn convert_mount_type(typ: bollard::config::MountType) -> MountType {
match typ {
bollard::config::MountTypeEnum::EMPTY => MountTypeEnum::Empty,
bollard::config::MountTypeEnum::BIND => MountTypeEnum::Bind,
bollard::config::MountTypeEnum::VOLUME => MountTypeEnum::Volume,
bollard::config::MountTypeEnum::IMAGE => MountTypeEnum::Image,
bollard::config::MountTypeEnum::TMPFS => MountTypeEnum::Tmpfs,
bollard::config::MountTypeEnum::NPIPE => MountTypeEnum::Npipe,
bollard::config::MountTypeEnum::CLUSTER => MountTypeEnum::Cluster,
bollard::config::MountType::BIND => MountType::Bind,
bollard::config::MountType::VOLUME => MountType::Volume,
bollard::config::MountType::IMAGE => MountType::Image,
bollard::config::MountType::TMPFS => MountType::Tmpfs,
bollard::config::MountType::NPIPE => MountType::Npipe,
bollard::config::MountType::CLUSTER => MountType::Cluster,
}
}
@@ -192,32 +189,6 @@ fn convert_mount_propogation(
}
}
fn convert_mount_point_type(
typ: bollard::config::MountPointTypeEnum,
) -> MountTypeEnum {
match typ {
bollard::config::MountPointTypeEnum::EMPTY => {
MountTypeEnum::Empty
}
bollard::config::MountPointTypeEnum::BIND => MountTypeEnum::Bind,
bollard::config::MountPointTypeEnum::VOLUME => {
MountTypeEnum::Volume
}
bollard::config::MountPointTypeEnum::IMAGE => {
MountTypeEnum::Image
}
bollard::config::MountPointTypeEnum::TMPFS => {
MountTypeEnum::Tmpfs
}
bollard::config::MountPointTypeEnum::NPIPE => {
MountTypeEnum::Npipe
}
bollard::config::MountPointTypeEnum::CLUSTER => {
MountTypeEnum::Cluster
}
}
}
fn convert_health_config(
config: bollard::models::HealthConfig,
) -> HealthConfig {
+4 -4
View File
@@ -51,24 +51,24 @@ impl DockerClient {
fn convert_node_list_item(
node: bollard::models::Node,
) -> SwarmNodeListItem {
let (name, role, availability) = node
let (name, role, availability, labels) = node
.spec
.map(|spec| {
(
spec.name,
spec.role.map(convert_role),
spec.availability.map(convert_availability),
spec.labels,
)
})
.unwrap_or_default();
SwarmNodeListItem {
id: node.id,
name,
hostname: node.description.and_then(|d| d.hostname),
role,
availability,
hostname: node
.description
.and_then(|description| description.hostname),
labels,
state: node
.status
.and_then(|status| status.state.map(convert_state)),
+4 -4
View File
@@ -1,6 +1,6 @@
use bollard::query_parameters::ListVolumesOptions;
use komodo_client::entities::docker::{
PortBinding, container::ContainerListItem, volume::*,
Topology, container::ContainerListItem, volume::*,
};
use crate::docker::DockerClient;
@@ -108,8 +108,8 @@ impl DockerClient {
}).collect(),
accessibility_requirements: mode
.accessibility_requirements.map(|req| ClusterVolumeSpecAccessModeAccessibilityRequirements {
requisite: req.requisite.unwrap_or_default().into_iter().map(|map| map.into_iter().map(|(k, v)| (k, v.unwrap_or_default().into_iter().map(|p| PortBinding { host_ip: p.host_ip, host_port: p.host_port }).collect())).collect()).collect(),
preferred: req.preferred.unwrap_or_default().into_iter().map(|map| map.into_iter().map(|(k, v)| (k, v.unwrap_or_default().into_iter().map(|p| PortBinding { host_ip: p.host_ip, host_port: p.host_port }).collect())).collect()).collect(),
requisite: req.requisite.map(|v| v.into_iter().map(|t| Topology { segments: t.segments }).collect()),
preferred: req.preferred.map(|v| v.into_iter().map(|t| Topology { segments: t.segments }).collect()),
}),
capacity_range: mode.capacity_range.map(|range| ClusterVolumeSpecAccessModeCapacityRange {
required_bytes: range.required_bytes,
@@ -128,7 +128,7 @@ impl DockerClient {
capacity_bytes: info.capacity_bytes,
volume_context: info.volume_context.unwrap_or_default(),
volume_id: info.volume_id,
accessible_topology: info.accessible_topology.unwrap_or_default().into_iter().map(|map| map.into_iter().map(|(k, v)| (k, v.unwrap_or_default().into_iter().map(|p| PortBinding { host_ip: p.host_ip, host_port: p.host_port }).collect())).collect()).collect(),
accessible_topology: info.accessible_topology.map(|v| v.into_iter().map(|t| Topology { segments: t.segments }).collect()),
}),
publish_status: volume
.publish_status
+21 -19
View File
@@ -189,7 +189,11 @@ pub async fn handle_post_repo_execution(
"On Clone",
path.as_path(),
on_clone.command,
KomodoCommandMode::Multiline,
if on_clone.shell_mode {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Multiline
},
&replacers,
)
.await
@@ -214,7 +218,11 @@ pub async fn handle_post_repo_execution(
"On Pull",
path.as_path(),
on_pull.command,
KomodoCommandMode::Multiline,
if on_pull.shell_mode {
KomodoCommandMode::Shell
} else {
KomodoCommandMode::Multiline
},
&replacers,
)
.await
@@ -276,37 +284,31 @@ pub fn registry_token(
// Public IP over DNS
// ====================
type OpenDNSResolver = hickory_resolver::Resolver<
hickory_resolver::name_server::TokioConnectionProvider,
>;
type OpenDNSResolver = hickory_resolver::TokioResolver;
fn opendns_resolver() -> &'static OpenDNSResolver {
static OPENDNS_RESOLVER: OnceLock<OpenDNSResolver> =
OnceLock::new();
OPENDNS_RESOLVER.get_or_init(|| {
// OpenDNS resolver ipv4s
let ips = [
// OpenDNS resolver ipv4s.
let name_servers = [
IpAddr::from_str("208.67.220.220").unwrap(),
IpAddr::from_str("208.67.222.222").unwrap(),
];
// trust_negative_responses=true means NXDOMAIN/empty NOERROR from an
// authoritative upstream wont be retried on other servers.
let ns =
hickory_resolver::config::NameServerConfigGroup::from_ips_clear(
&ips, 53, true,
);
]
.into_iter()
.map(hickory_resolver::config::NameServerConfig::udp_and_tcp)
.collect();
hickory_resolver::Resolver::builder_with_config(
hickory_resolver::config::ResolverConfig::from_parts(
None,
vec![],
ns,
),
hickory_resolver::name_server::TokioConnectionProvider::default(
name_servers,
),
hickory_resolver::net::runtime::TokioRuntimeProvider::default(),
)
.build()
.expect("Failed to build OpenDNS resolver")
})
}
@@ -319,7 +321,7 @@ pub async fn resolve_host_public_ip() -> anyhow::Result<String> {
.context(
"Failed to query OpenDNS resolvers for host public IP",
)?
.into_iter()
.iter()
.map(|ip| ip.to_string())
.next()
.context("OpenDNS call for public IP didn't return anything")
+2
View File
@@ -14,6 +14,7 @@ repository.workspace = true
blocking = ["reqwest/blocking"]
mongo = ["dep:mongo_indexed"]
utoipa = ["dep:utoipa", "mogh_auth_client/utoipa", "mogh_error/utoipa"]
schemars = ["dep:schemars"]
logger = ["dep:mogh_logger"]
cli = ["logger", "dep:mogh_pki",]
core = ["mongo", "utoipa", "logger", "dep:mogh_server"]
@@ -33,6 +34,7 @@ partial_derive2.workspace = true
mogh_resolver.workspace = true
# external
ipnetwork = { workspace = true, optional = true }
schemars = { workspace = true, optional = true }
utoipa = { workspace = true, optional = true }
tokio-tungstenite.workspace = true
derive_builder.workspace = true
+2
View File
@@ -28,6 +28,7 @@ pub fn run_action() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -68,6 +69,7 @@ pub fn batch_run_action() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -27,6 +27,7 @@ pub fn test_alerter() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -56,6 +57,7 @@ pub fn send_alert() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
+3
View File
@@ -39,6 +39,7 @@ pub fn run_build() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -67,6 +68,7 @@ pub fn batch_run_build() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -106,6 +108,7 @@ pub fn cancel_build() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -34,6 +34,7 @@ pub fn deploy() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -68,6 +69,7 @@ pub fn batch_deploy() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -105,6 +107,7 @@ pub fn pull_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -135,6 +138,7 @@ pub fn start_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -165,6 +169,7 @@ pub fn restart_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -195,6 +200,7 @@ pub fn pause_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -228,6 +234,7 @@ pub fn unpause_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -258,6 +265,7 @@ pub fn stop_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -295,6 +303,7 @@ pub fn destroy_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -327,6 +336,7 @@ pub fn batch_destroy_deployment() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -28,6 +28,7 @@ pub fn clear_repo_cache() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -60,6 +61,7 @@ pub fn backup_core_database() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -92,6 +94,7 @@ pub fn global_auto_update() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -125,6 +128,7 @@ pub fn rotate_all_server_keys() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -155,6 +159,7 @@ pub fn rotate_core_keys() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
+3
View File
@@ -78,6 +78,7 @@ pub trait KomodoExecuteRequest: HasResponse {}
utoipa::ToSchema
))
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(tag = "type", content = "params")]
pub enum Execution {
/// The "null" execution. Does nothing.
@@ -183,6 +184,7 @@ pub enum Execution {
// SWARM
RemoveSwarmNodes(RemoveSwarmNodes),
UpdateSwarmNode(UpdateSwarmNode),
RemoveSwarmStacks(RemoveSwarmStacks),
RemoveSwarmServices(RemoveSwarmServices),
CreateSwarmConfig(CreateSwarmConfig),
@@ -214,6 +216,7 @@ pub enum Execution {
#[typeshare]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Parser)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Sleep {
#[serde(default)]
pub duration_ms: I64,
@@ -7,6 +7,7 @@ use crate::api::execute;
paths(
// swarm
execute::remove_swarm_nodes,
execute::update_swarm_node,
execute::remove_swarm_stacks,
execute::remove_swarm_services,
execute::create_swarm_config,
@@ -27,6 +27,7 @@ pub fn run_procedure() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -55,6 +56,7 @@ pub fn batch_run_procedure() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
+7
View File
@@ -35,6 +35,7 @@ pub fn clone_repo() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -63,6 +64,7 @@ pub fn batch_clone_repo() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -105,6 +107,7 @@ pub fn pull_repo() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -133,6 +136,7 @@ pub fn batch_pull_repo() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -179,6 +183,7 @@ pub fn build_repo() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -207,6 +212,7 @@ pub fn batch_build_repo() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -246,6 +252,7 @@ pub fn cancel_repo_build() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
+21
View File
@@ -31,6 +31,7 @@ pub fn start_container() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -63,6 +64,7 @@ pub fn restart_container() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -95,6 +97,7 @@ pub fn pause_container() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -129,6 +132,7 @@ pub fn unpause_container() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -161,6 +165,7 @@ pub fn stop_container() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -198,6 +203,7 @@ pub fn destroy_container() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -232,6 +238,7 @@ pub fn start_all_containers() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -260,6 +267,7 @@ pub fn restart_all_containers() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -288,6 +296,7 @@ pub fn pause_all_containers() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -316,6 +325,7 @@ pub fn unpause_all_containers() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -344,6 +354,7 @@ pub fn stop_all_containers() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -374,6 +385,7 @@ pub fn prune_containers() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -405,6 +417,7 @@ pub fn delete_network() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -437,6 +450,7 @@ pub fn prune_networks() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -466,6 +480,7 @@ pub fn delete_image() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -498,6 +513,7 @@ pub fn prune_images() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -527,6 +543,7 @@ pub fn delete_volume() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -559,6 +576,7 @@ pub fn prune_volumes() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -589,6 +607,7 @@ pub fn prune_docker_builders() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -619,6 +638,7 @@ pub fn prune_buildx() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -649,6 +669,7 @@ pub fn prune_system() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
+14
View File
@@ -27,6 +27,7 @@ pub fn deploy_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -64,6 +65,7 @@ pub fn batch_deploy_stack() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -103,6 +105,7 @@ pub fn deploy_stack_if_changed() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -134,6 +137,7 @@ pub fn batch_deploy_stack_if_changed() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -171,6 +175,7 @@ pub fn pull_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -203,6 +208,7 @@ pub fn batch_pull_stack() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
@@ -240,6 +246,7 @@ pub fn start_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -272,6 +279,7 @@ pub fn restart_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -304,6 +312,7 @@ pub fn pause_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -338,6 +347,7 @@ pub fn unpause_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -370,6 +380,7 @@ pub fn stop_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -404,6 +415,7 @@ pub fn destroy_stack() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -441,6 +453,7 @@ pub fn run_stack_service() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -505,6 +518,7 @@ pub fn batch_destroy_stack() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(BatchExecutionResponse)]
#[error(mogh_error::Error)]
+57 -1
View File
@@ -4,7 +4,11 @@ use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::{
api::execute::KomodoExecuteRequest, entities::update::Update,
api::execute::KomodoExecuteRequest,
entities::{
docker::node::{NodeSpecAvailabilityEnum, NodeSpecRoleEnum},
update::Update,
},
};
// ========
@@ -31,6 +35,7 @@ pub fn remove_swarm_nodes() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -45,6 +50,49 @@ pub struct RemoveSwarmNodes {
pub force: bool,
}
#[cfg(feature = "utoipa")]
#[utoipa::path(
post,
path = "/UpdateSwarmNode",
description = "Update a swarm node's availability, labels, and role.",
request_body(content = UpdateSwarmNode),
responses(
(status = 200, description = "The update", body = Update),
),
)]
pub fn update_swarm_node() {}
/// `docker node update [OPTIONS] NODE`
///
/// https://docs.docker.com/reference/cli/docker/node/update/
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
pub struct UpdateSwarmNode {
/// Name or id
pub swarm: String,
/// Node hostname or id
pub node: String,
/// Update the node's availability: 'active', 'pause', or 'drain'
#[arg(long, short = 'a')]
pub availability: Option<NodeSpecAvailabilityEnum>,
/// Add labels to node (`key=value`).
#[arg(long, short = 'l')]
pub label_add: Option<Vec<String>>,
/// Add labels to node (`key=value`). (alias: `lr`)
#[arg(long, alias = "lr")]
pub label_rm: Option<Vec<String>>,
/// Update the node's role: 'worker' or 'manager'
#[arg(long, short = 'r')]
pub role: Option<NodeSpecRoleEnum>,
}
// =========
// = Stack =
// =========
@@ -69,6 +117,7 @@ pub fn remove_swarm_stacks() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -111,6 +160,7 @@ pub fn remove_swarm_services() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -145,6 +195,7 @@ pub fn create_swarm_config() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -194,6 +245,7 @@ pub fn rotate_swarm_config() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -228,6 +280,7 @@ pub fn remove_swarm_configs() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -262,6 +315,7 @@ pub fn create_swarm_secret() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -313,6 +367,7 @@ pub fn rotate_swarm_secret() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
@@ -347,6 +402,7 @@ pub fn remove_swarm_secrets() {}
Serialize, Deserialize, Debug, Clone, PartialEq, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
+1
View File
@@ -27,6 +27,7 @@ pub fn run_sync() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoExecuteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
+1 -1
View File
@@ -144,7 +144,7 @@ pub struct GetCoreInfoResponse {
pub enable_fancy_toml: bool,
/// TZ identifier Core is using, if manually set.
pub timezone: String,
/// Default public key allowing this Core to authenticate to Periphery agents.
/// Public key for Core / Periphery authentication.
pub public_key: String,
}
@@ -40,7 +40,8 @@ pub struct CreateOnboardingKey {
#[serde(default)]
pub expires: I64,
/// Optionally specify an existing private key, otherwise
/// generate fresh key.
/// generate fresh key. This key is not stored directly,
/// only the public key.
pub private_key: Option<String>,
/// Default tags to apply to Servers created using this key.
#[serde(default)]
+1
View File
@@ -242,6 +242,7 @@ pub fn commit_sync() {}
Debug, Clone, PartialEq, Serialize, Deserialize, Resolve, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[empty_traits(KomodoWriteRequest)]
#[response(Update)]
#[error(mogh_error::Error)]
+4
View File
@@ -81,6 +81,10 @@ pub type _PartialActionConfig = PartialActionConfig;
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct ActionConfig {
+6 -1
View File
@@ -74,6 +74,10 @@ pub struct Alert {
utoipa::ToSchema
))
)]
#[cfg_attr(
feature = "schemars",
strum_discriminants(derive(schemars::JsonSchema))
)]
#[serde(tag = "type", content = "data")]
pub enum AlertData {
/// A null alert
@@ -95,7 +99,7 @@ pub enum AlertData {
/// The name of the swarm
name: String,
/// The error data
err: Option<String>,
err: Option<_Serror>,
},
/// A server could not be reached.
@@ -382,6 +386,7 @@ impl Default for AlertDataVariant {
EnumString,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "UPPERCASE")]
#[strum(serialize_all = "UPPERCASE")]
pub enum SeverityLevel {
+10
View File
@@ -45,6 +45,10 @@ pub type _PartialAlerterConfig = PartialAlerterConfig;
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct AlerterConfig {
@@ -149,6 +153,7 @@ impl utoipa::ToSchema for PartialAlerterConfig {}
utoipa::ToSchema
))
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(tag = "type", content = "params")]
pub enum AlerterEndpoint {
/// Send alert serialized to JSON to an http endpoint.
@@ -179,6 +184,7 @@ impl Default for AlerterEndpoint {
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct CustomAlerterEndpoint {
/// The http/s endpoint to send the POST to
#[serde(default = "default_custom_url")]
@@ -204,6 +210,7 @@ fn default_custom_url() -> String {
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct SlackAlerterEndpoint {
/// The Slack app webhook url
#[serde(default = "default_slack_url")]
@@ -231,6 +238,7 @@ fn default_slack_url() -> String {
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct DiscordAlerterEndpoint {
/// The Discord webhook url
#[serde(default = "default_discord_url")]
@@ -258,6 +266,7 @@ fn default_discord_url() -> String {
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct NtfyAlerterEndpoint {
/// The ntfy topic URL
#[serde(default = "default_ntfy_url")]
@@ -288,6 +297,7 @@ fn default_ntfy_url() -> String {
Debug, Clone, PartialEq, Serialize, Deserialize, Builder,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PushoverAlerterEndpoint {
/// The pushover URL including application and user tokens in parameters.
#[serde(default = "default_pushover_url")]
+33
View File
@@ -260,18 +260,30 @@ pub type _PartialBuildConfig = PartialBuildConfig;
#[derive(Debug, Clone, Serialize, Deserialize, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[partial(skip_serializing_none, from, diff)]
pub struct BuildConfig {
/// Which builder is used to build the image.
#[serde(default, alias = "builder")]
#[partial_attr(serde(alias = "builder"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "builder"))
)]
#[builder(default)]
pub builder_id: String,
/// The current version of the build.
#[serde(default)]
#[builder(default)]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(default, schema_with = "version_schema"))
)]
pub version: Version,
/// Whether to automatically increment the patch on every build.
@@ -523,6 +535,26 @@ fn default_webhook_enabled() -> bool {
true
}
#[cfg(feature = "schemars")]
fn version_schema(
_: &mut schemars::SchemaGenerator,
) -> schemars::Schema {
schemars::json_schema!({
"description": "The current version of the build.",
"anyOf": [
{
"$ref": "#/$defs/Version"
},
{
"type": "string"
},
{
"type": "null"
}
]
})
}
impl Default for BuildConfig {
fn default() -> Self {
Self {
@@ -577,6 +609,7 @@ impl utoipa::ToSchema for PartialBuildConfig {}
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ImageRegistryConfig {
/// Specify the registry provider domain, eg `docker.io`.
/// If not provided, will not push to any registry.
+16 -6
View File
@@ -125,6 +125,7 @@ impl Default for BuilderConfig {
utoipa::ToSchema
))
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(tag = "type", content = "params")]
#[allow(clippy::large_enum_variant)]
pub enum PartialBuilderConfig {
@@ -370,13 +371,10 @@ pub type _PartialUrlBuilderConfig = PartialUrlBuilderConfig;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
not(feature = "utoipa"),
partial_derive(Serialize, Deserialize, Debug, Clone, Default)
)]
#[cfg_attr(
feature = "utoipa",
partial_derive(Serialize, Deserialize, Debug, Clone, Default,)
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
@@ -451,12 +449,20 @@ pub type _PartialServerBuilderConfig = PartialServerBuilderConfig;
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct ServerBuilderConfig {
/// The server id of the builder
#[serde(default, alias = "server")]
#[partial_attr(serde(alias = "server"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "server"))
)]
pub server_id: String,
}
@@ -485,6 +491,10 @@ pub type _PartialAwsBuilderConfig = PartialAwsBuilderConfig;
#[derive(Debug, Clone, Serialize, Deserialize, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct AwsBuilderConfig {
@@ -96,6 +96,8 @@ pub struct Env {
pub komodo_cli_logging_pretty: Option<bool>,
/// Override `logging.ansi`
pub komodo_cli_logging_ansi: Option<bool>,
/// Override `logging.timestamps`
pub komodo_cli_logging_timestamps: Option<bool>,
/// Override `logging.otlp_endpoint`
pub komodo_cli_logging_otlp_endpoint: Option<String>,
/// Override `logging.opentelemetry_service_name`
+140 -21
View File
@@ -116,27 +116,6 @@ pub struct Env {
/// Override `webhook_base_url`
pub komodo_webhook_base_url: Option<String>,
/// Override `logging.level`
pub komodo_logging_level: Option<LogLevel>,
/// Override `logging.stdio`
pub komodo_logging_stdio: Option<StdioLogMode>,
/// Override `logging.pretty`
pub komodo_logging_pretty: Option<bool>,
/// Override `logging.location`
pub komodo_logging_location: Option<bool>,
/// Override `logging.ansi`
pub komodo_logging_ansi: Option<bool>,
/// Override `logging.otlp_endpoint`
pub komodo_logging_otlp_endpoint: Option<String>,
/// Override `logging.opentelemetry_service_name`
pub komodo_logging_opentelemetry_service_name: Option<String>,
/// Override `logging.opentelemetry_scope_name`
pub komodo_logging_opentelemetry_scope_name: Option<String>,
/// Override `pretty_startup_config`
pub komodo_pretty_startup_config: Option<bool>,
/// Override `unsafe_unsanitized_startup_config`
pub komodo_unsafe_unsanitized_startup_config: Option<bool>,
/// Override `transparent_mode`
pub komodo_transparent_mode: Option<bool>,
/// Override `ui_write_disabled`
@@ -145,6 +124,10 @@ pub struct Env {
pub komodo_enable_new_users: Option<bool>,
/// Override `disable_user_registration`
pub komodo_disable_user_registration: Option<bool>,
/// Override `disable_local_user_registration`
pub komodo_disable_local_user_registration: Option<bool>,
/// Override `disable_oidc_user_registration`
pub komodo_disable_oidc_user_registration: Option<bool>,
/// Override `lock_login_credentials_for`
pub komodo_lock_login_credentials_for: Option<Vec<String>>,
/// Override `disable_confirm_dialog`
@@ -191,6 +174,8 @@ pub struct Env {
pub komodo_oidc_additional_audiences: Option<Vec<String>>,
/// Override `oidc_additional_audiences` from file
pub komodo_oidc_additional_audiences_file: Option<PathBuf>,
/// Override `oidc_auto_redirect`
pub komodo_oidc_auto_redirect: Option<bool>,
/// Override `google_oauth.enabled`
pub komodo_google_oauth_enabled: Option<bool>,
@@ -228,6 +213,17 @@ pub struct Env {
/// Override `session_allow_cross_site`
pub komodo_session_allow_cross_site: Option<bool>,
/// Override `x_content_type_options`
pub komodo_x_content_type_options: Option<String>,
/// Override `x_frame_options`
pub komodo_x_frame_options: Option<String>,
/// Override `x_xss_protection`
pub komodo_x_xss_protection: Option<String>,
/// Override `x_referrer_policy`
pub komodo_referrer_policy: Option<String>,
/// Override `content_security_policy`
pub komodo_content_security_policy: Option<String>,
/// Override `database.uri`
#[serde(alias = "komodo_mongo_uri")]
pub komodo_database_uri: Option<String>,
@@ -265,6 +261,29 @@ pub struct Env {
/// Override `aws.secret_access_key` with file
pub komodo_aws_secret_access_key_file: Option<PathBuf>,
/// Override `logging.level`
pub komodo_logging_level: Option<LogLevel>,
/// Override `logging.stdio`
pub komodo_logging_stdio: Option<StdioLogMode>,
/// Override `logging.pretty`
pub komodo_logging_pretty: Option<bool>,
/// Override `logging.location`
pub komodo_logging_location: Option<bool>,
/// Override `logging.ansi`
pub komodo_logging_ansi: Option<bool>,
/// Override `logging.timestamps`
pub komodo_logging_timestamps: Option<bool>,
/// Override `logging.otlp_endpoint`
pub komodo_logging_otlp_endpoint: Option<String>,
/// Override `logging.opentelemetry_service_name`
pub komodo_logging_opentelemetry_service_name: Option<String>,
/// Override `logging.opentelemetry_scope_name`
pub komodo_logging_opentelemetry_scope_name: Option<String>,
/// Override `pretty_startup_config`
pub komodo_pretty_startup_config: Option<bool>,
/// Override `unsafe_unsanitized_startup_config`
pub komodo_unsafe_unsanitized_startup_config: Option<bool>,
/// Override `internet_interface`
pub komodo_internet_interface: Option<String>,
@@ -455,6 +474,20 @@ pub struct CoreConfig {
#[serde(default)]
pub disable_user_registration: bool,
/// Disable local (username/password) user registration only.
/// When set, the "Sign Up" button is hidden and local signups are blocked,
/// but OIDC and other external provider signups are still allowed.
/// If not set, falls back to `disable_user_registration`.
#[serde(default)]
pub disable_local_user_registration: Option<bool>,
/// Disable OIDC user registration only.
/// When set, new users cannot register via OIDC,
/// but local and other provider signups are still allowed.
/// If not set, falls back to `disable_user_registration`.
#[serde(default)]
pub disable_oidc_user_registration: Option<bool>,
/// List of usernames for which the update username / password
/// APIs are disabled. Used by demo to lock the 'demo' : 'demo' login.
///
@@ -523,6 +556,12 @@ pub struct CoreConfig {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub oidc_additional_audiences: Vec<String>,
/// Automatically redirect unauthenticated users to the OIDC provider
/// instead of showing the login page.
/// Users can bypass the redirect by appending `?disableAutoLogin` to the login URL.
#[serde(default)]
pub oidc_auto_redirect: bool,
// =========
// = Oauth =
// =========
@@ -569,6 +608,37 @@ pub struct CoreConfig {
#[serde(default)]
pub session_allow_cross_site: bool,
// ====================
// = Security Headers =
// ====================
/// `X-Content-Type-Options` header value.
/// Default is `nosniff`. Set as empty string
/// to omit the header.
#[serde(default = "default_x_content_type_options")]
pub x_content_type_options: String,
/// `X-Frame-Options` header value. Return an empty string to
/// omit the header entirely and allow iframe on any origin. Use `"SAMEORIGIN"` to allow
/// same-origin embedding only. Defaults to `"DENY"`.
#[serde(default = "default_x_frame_options")]
pub x_frame_options: String,
/// `X-XSS-PROTECTION` header value. Return an empty string to
/// omit the header entirely. Default: `1; mode=block`
#[serde(default = "default_x_xss_protection")]
pub x_xss_protection: String,
/// Apply Referrer Policy directives.
/// If empty string, no header is applied.
/// Default: `strict-origin-when-cross-origin`
#[serde(default = "default_referrer_policy")]
pub referrer_policy: String,
/// Apply Content Security Policy directives.
/// If empty string, no header is applied.
/// Default: None
///
/// Example:
/// `default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'`
#[serde(default)]
pub content_security_policy: String,
// ============
// = Webhooks =
// ============
@@ -765,6 +835,22 @@ fn default_auth_rate_limit_window_seconds() -> u64 {
15
}
fn default_x_content_type_options() -> String {
String::from("nosniff")
}
fn default_x_frame_options() -> String {
String::from("DENY")
}
fn default_x_xss_protection() -> String {
String::from("1; mode=block")
}
fn default_referrer_policy() -> String {
String::from("strict-origin-when-cross-origin")
}
fn default_sync_directory() -> PathBuf {
PathBuf::from("/syncs")
}
@@ -824,6 +910,8 @@ impl Default for CoreConfig {
transparent_mode: Default::default(),
enable_new_users: Default::default(),
disable_user_registration: Default::default(),
disable_local_user_registration: Default::default(),
disable_oidc_user_registration: Default::default(),
lock_login_credentials_for: Default::default(),
disable_non_admin_create: Default::default(),
jwt_secret: Default::default(),
@@ -835,6 +923,7 @@ impl Default for CoreConfig {
oidc_client_secret: Default::default(),
oidc_use_full_email: Default::default(),
oidc_additional_audiences: Default::default(),
oidc_auto_redirect: Default::default(),
google_oauth: Default::default(),
github_oauth: Default::default(),
auth_rate_limit_disabled: Default::default(),
@@ -845,6 +934,11 @@ impl Default for CoreConfig {
cors_allowed_origins: Default::default(),
cors_allow_credentials: Default::default(),
session_allow_cross_site: Default::default(),
x_content_type_options: default_x_content_type_options(),
x_frame_options: default_x_frame_options(),
x_xss_protection: default_x_xss_protection(),
referrer_policy: default_referrer_policy(),
content_security_policy: Default::default(),
webhook_secret: Default::default(),
webhook_base_url: Default::default(),
logging: Default::default(),
@@ -907,6 +1001,10 @@ impl CoreConfig {
enable_fancy_toml: config.enable_fancy_toml,
enable_new_users: config.enable_new_users,
disable_user_registration: config.disable_user_registration,
disable_local_user_registration: config
.disable_local_user_registration,
disable_oidc_user_registration: config
.disable_oidc_user_registration,
disable_non_admin_create: config.disable_non_admin_create,
lock_login_credentials_for: config.lock_login_credentials_for,
local_auth: config.local_auth,
@@ -930,6 +1028,7 @@ impl CoreConfig {
.iter()
.map(|aud| empty_or_redacted(aud))
.collect(),
oidc_auto_redirect: config.oidc_auto_redirect,
google_oauth: NamedOauthConfig {
enabled: config.google_oauth.enabled,
client_id: empty_or_redacted(&config.google_oauth.client_id),
@@ -952,6 +1051,11 @@ impl CoreConfig {
cors_allowed_origins: config.cors_allowed_origins,
cors_allow_credentials: config.cors_allow_credentials,
session_allow_cross_site: config.session_allow_cross_site,
x_content_type_options: config.x_content_type_options,
x_frame_options: config.x_frame_options,
x_xss_protection: config.x_xss_protection,
referrer_policy: config.referrer_policy,
content_security_policy: config.content_security_policy,
webhook_secret: empty_or_redacted(&config.webhook_secret),
webhook_base_url: config.webhook_base_url,
database: config.database.sanitized(),
@@ -1030,6 +1134,21 @@ impl mogh_server::ServerConfig for &CoreConfig {
fn ssl_cert_file(&self) -> &str {
&self.ssl_cert_file
}
fn x_content_type_options(&self) -> &str {
&self.x_content_type_options
}
fn x_frame_options(&self) -> &str {
&self.x_frame_options
}
fn x_xss_protection(&self) -> &str {
&self.x_xss_protection
}
fn referrer_policy(&self) -> &str {
&self.referrer_policy
}
fn content_security_policy(&self) -> &str {
&self.content_security_policy
}
}
impl mogh_server::cors::CorsConfig for &CoreConfig {
@@ -119,6 +119,7 @@ impl DatabaseConfig {
Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct GitProvider {
/// The git provider domain. Default: `github.com`.
#[serde(default = "default_git_provider")]
@@ -152,6 +153,7 @@ fn default_git_https() -> bool {
Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct DockerRegistry {
/// The docker provider domain. Default: `docker.io`.
#[serde(default = "default_docker_provider")]
@@ -182,6 +184,7 @@ fn default_docker_provider() -> String {
Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ProviderAccount {
/// The account username. Required.
#[serde(alias = "account")]
@@ -199,6 +199,8 @@ pub struct Env {
pub periphery_logging_location: Option<bool>,
/// Override `logging.ansi`
pub periphery_logging_ansi: Option<bool>,
/// Override `logging.timestamps`
pub periphery_logging_timestamps: Option<bool>,
/// Override `logging.otlp_endpoint`
pub periphery_logging_otlp_endpoint: Option<String>,
/// Override `logging.opentelemetry_service_name`
+18
View File
@@ -80,6 +80,10 @@ pub type _PartialDeploymentConfig = PartialDeploymentConfig;
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct DeploymentConfig {
@@ -89,6 +93,10 @@ pub struct DeploymentConfig {
/// swarm_id overrides server_id and the Deployment will be in Swarm mode.
#[serde(default, alias = "swarm")]
#[partial_attr(serde(alias = "swarm"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "swarm"))
)]
#[builder(default)]
pub swarm_id: String,
@@ -98,6 +106,10 @@ pub struct DeploymentConfig {
/// swarm_id overrides server_id and the Deployment will be in Swarm mode.
#[serde(default, alias = "server")]
#[partial_attr(serde(alias = "server"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "server"))
)]
#[builder(default)]
pub server_id: String,
@@ -355,6 +367,7 @@ impl utoipa::ToSchema for PartialDeploymentConfig {}
utoipa::ToSchema
))
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(tag = "type", content = "params")]
pub enum DeploymentImage {
/// Deploy any external image.
@@ -368,6 +381,7 @@ pub enum DeploymentImage {
Build {
/// The id of the Build
#[serde(default, alias = "build")]
#[cfg_attr(feature = "schemars", schemars(rename = "build"))]
build_id: String,
/// Use a custom / older version of the image produced by the build.
/// if version is 0.0.0, this means `latest` image.
@@ -450,6 +464,8 @@ pub enum DeploymentState {
Created,
/// Server mode only. Container is in restart loop
Restarting,
/// Server mode only. Container is in the process of stopping
Stopping,
/// Server mode only. Container is being removed
Removing,
/// Server mode only. Container is paused
@@ -477,6 +493,7 @@ impl From<ContainerStateStatusEnum> for DeploymentState {
ContainerStateStatusEnum::Restarting => {
DeploymentState::Restarting
}
ContainerStateStatusEnum::Stopping => DeploymentState::Stopping,
ContainerStateStatusEnum::Removing => DeploymentState::Removing,
ContainerStateStatusEnum::Exited => DeploymentState::Exited,
ContainerStateStatusEnum::Dead => DeploymentState::Dead,
@@ -500,6 +517,7 @@ impl From<ContainerStateStatusEnum> for DeploymentState {
AsRefStr,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum RestartMode {
#[default]
#[serde(rename = "no")]
@@ -8,8 +8,8 @@ use typeshare::typeshare;
use crate::entities::{I64, Usize};
use super::{
ContainerConfig, GraphDriverData, Mount, MountTypeEnum,
PortBinding, ResourcesUlimits,
ContainerConfig, GraphDriverData, Mount, PortBinding,
ResourcesUlimits,
};
/// Container summary returned by container list apis.
@@ -294,6 +294,7 @@ pub enum ContainerStateStatusEnum {
Paused,
Restarting,
Exited,
Stopping,
Removing,
Dead,
#[default]
@@ -858,8 +859,8 @@ pub enum HostConfigCgroupnsModeEnum {
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct MountPoint {
/// The mount type: - `bind` a mount of a file or directory from the host into the container. - `volume` a docker volume with the given `Name`. - `tmpfs` a `tmpfs`. - `npipe` a named pipe from the host into the container. - `cluster` a Swarm cluster volume
#[serde(default, rename = "Type")]
pub typ: MountTypeEnum,
#[serde(rename = "Type")]
pub typ: Option<String>,
/// Name is the name reference to the underlying data defined by `Source` e.g., the volume name.
#[serde(rename = "Name")]
+14 -4
View File
@@ -267,7 +267,7 @@ pub struct Mount {
/// - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. - `npipe` Mounts a named pipe from the host into the container. Must exist prior to creating the container.
/// - `cluster` a Swarm cluster volume
#[serde(default, rename = "Type")]
pub typ: MountTypeEnum,
pub typ: MountType,
/// Whether the mount should be read-only.
#[serde(rename = "ReadOnly")]
@@ -301,10 +301,8 @@ pub struct Mount {
Default,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum MountTypeEnum {
pub enum MountType {
#[default]
#[serde(rename = "")]
Empty,
#[serde(rename = "bind")]
Bind,
#[serde(rename = "volume")]
@@ -643,3 +641,15 @@ pub struct TlsInfo {
#[serde(rename = "CertIssuerPublicKey")]
pub cert_issuer_public_key: Option<String>,
}
/// A map of topological domains to topological segments. For in depth details, see documentation for the Topology object in the CSI specification.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct Topology {
#[serde(rename = "Segments")]
#[serde(skip_serializing_if = "Option::is_none")]
pub segments: Option<HashMap<String, String>>,
}
@@ -2,6 +2,7 @@
use std::collections::HashMap;
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use strum::AsRefStr;
use typeshare::typeshare;
@@ -22,6 +23,7 @@ pub struct SwarmNodeListItem {
#[serde(rename = "Name")]
pub name: Option<String>,
/// Node hostname, more commonly used than Name
#[serde(rename = "Hostname")]
pub hostname: Option<String>,
@@ -33,6 +35,10 @@ pub struct SwarmNodeListItem {
#[serde(rename = "Availability")]
pub availability: Option<NodeSpecAvailabilityEnum>,
/// Labels of the node
#[serde(rename = "Labels")]
pub labels: Option<HashMap<String, String>>,
/// State of the node
#[serde(rename = "State")]
pub state: Option<NodeState>,
@@ -120,8 +126,10 @@ pub struct NodeSpec {
Serialize,
Deserialize,
AsRefStr,
ValueEnum,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum NodeSpecRoleEnum {
#[default]
#[serde(rename = "")]
@@ -145,8 +153,10 @@ pub enum NodeSpecRoleEnum {
Serialize,
Deserialize,
AsRefStr,
ValueEnum,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum NodeSpecAvailabilityEnum {
#[default]
#[serde(rename = "")]
+8 -11
View File
@@ -3,9 +3,9 @@ use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::I64;
use crate::entities::{I64, docker::Topology};
use super::{ObjectVersion, PortBinding};
use super::ObjectVersion;
#[typeshare]
#[derive(
@@ -145,8 +145,8 @@ pub struct ClusterVolumeInfo {
pub volume_id: Option<String>,
/// The topology this volume is actually accessible from.
#[serde(default, rename = "AccessibleTopology")]
pub accessible_topology: Vec<Topology>,
#[serde(rename = "AccessibleTopology")]
pub accessible_topology: Option<Vec<Topology>>,
}
#[typeshare]
@@ -319,17 +319,14 @@ pub struct ClusterVolumeSpecAccessModeSecrets {
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct ClusterVolumeSpecAccessModeAccessibilityRequirements {
/// A list of required topologies, at least one of which the volume must be accessible from.
#[serde(default, rename = "Requisite")]
pub requisite: Vec<Topology>,
#[serde(rename = "Requisite")]
pub requisite: Option<Vec<Topology>>,
/// A list of topologies that the volume should attempt to be provisioned in.
#[serde(default, rename = "Preferred")]
pub preferred: Vec<Topology>,
#[serde(rename = "Preferred")]
pub preferred: Option<Vec<Topology>>,
}
#[typeshare]
pub type Topology = HashMap<String, Vec<PortBinding>>;
/// The desired capacity that the volume should be created with. If empty, the plugin will decide the capacity.
#[typeshare]
#[derive(
+17 -5
View File
@@ -17,14 +17,18 @@ pub struct LogConfig {
pub pretty: bool,
/// Including information about the log location (ie the function which produced the log).
/// Tracing refers to this as the 'target'.
/// Tracing refers to this as the 'target'. (default: false)
#[serde(default = "default_location")]
pub location: bool,
/// Logs use ansi colors for readability.
/// Logs use ansi colors for readability. (default: true)
#[serde(default = "default_ansi")]
pub ansi: bool,
/// Logs include timestamps. (default: true)
#[serde(default = "default_timestamps")]
pub timestamps: bool,
/// Enable opentelemetry exporting
#[serde(default)]
pub otlp_endpoint: String,
@@ -52,6 +56,10 @@ fn default_ansi() -> bool {
true
}
fn default_timestamps() -> bool {
true
}
impl Default for LogConfig {
fn default() -> Self {
Self {
@@ -60,6 +68,7 @@ impl Default for LogConfig {
pretty: Default::default(),
location: default_location(),
ansi: default_ansi(),
timestamps: default_timestamps(),
otlp_endpoint: Default::default(),
opentelemetry_service_name: default_opentelemetry_service_name(
),
@@ -166,15 +175,18 @@ impl mogh_logger::LogConfig for &LogConfig {
&TARGETS
}
fn ansi(&self) -> bool {
self.ansi
}
fn level(&self) -> tracing::Level {
self.level.into()
}
fn location(&self) -> bool {
self.location
}
fn ansi(&self) -> bool {
self.ansi
}
fn timestamps(&self) -> bool {
self.timestamps
}
fn opentelemetry_scope_name(&self) -> String {
self.opentelemetry_scope_name.clone()
}
+16
View File
@@ -103,6 +103,7 @@ pub type _Serror = Serror;
Debug, Clone, Default, PartialEq, Serialize, Deserialize, Parser,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct NoData {}
pub trait MergePartial: Sized {
@@ -210,11 +211,14 @@ pub struct __Serror {
Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct SystemCommand {
#[serde(default)]
pub path: String,
#[serde(default, deserialize_with = "file_contents_deserializer")]
pub command: String,
#[serde(default)]
pub shell_mode: bool,
}
impl SystemCommand {
@@ -238,6 +242,7 @@ impl SystemCommand {
#[typeshare]
#[derive(Serialize, Debug, Clone, Copy, Default, PartialEq)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Version {
pub major: i32,
pub minor: i32,
@@ -488,6 +493,7 @@ impl ImageDigest {
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct MaintenanceWindow {
/// Name for the maintenance window (required)
pub name: String,
@@ -920,6 +926,7 @@ pub enum DayOfWeek {
Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum MaintenanceScheduleType {
/// Daily at the specified time
#[default]
@@ -1167,6 +1174,7 @@ pub enum Operation {
RenameSwarm,
DeleteSwarm,
RemoveSwarmNodes,
UpdateSwarmNode,
RemoveSwarmStacks,
RemoveSwarmServices,
CreateSwarmConfig,
@@ -1363,6 +1371,7 @@ pub enum SearchCombinator {
EnumString,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "UPPERCASE")]
#[strum(serialize_all = "UPPERCASE")]
pub enum TerminationSignal {
@@ -1420,6 +1429,11 @@ pub enum TerminationSignal {
utoipa::ToSchema,
))
)]
#[cfg_attr(
feature = "schemars",
strum_discriminants(derive(schemars::JsonSchema))
)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(tag = "type", content = "id")]
pub enum ResourceTarget {
System(String),
@@ -1576,6 +1590,7 @@ impl ResourceTargetVariant {
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum ScheduleFormat {
#[default]
English,
@@ -1587,6 +1602,7 @@ pub enum ScheduleFormat {
Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum FileFormat {
#[default]
@@ -118,6 +118,7 @@ impl UserTarget {
Default,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum PermissionLevel {
/// No permissions.
#[default]
@@ -156,6 +157,7 @@ impl Default for &PermissionLevel {
Ord,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum SpecificPermission {
/// On **Server**
/// - Access the terminal apis
@@ -193,6 +195,7 @@ impl SpecificPermission {
#[typeshare]
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PermissionLevelAndSpecifics {
pub level: PermissionLevel,
#[cfg_attr(feature = "utoipa", schema(value_type = Vec<SpecificPermission>))]
+10
View File
@@ -82,12 +82,20 @@ pub type _PartialProcedureConfig = PartialProcedureConfig;
#[derive(Debug, Clone, Serialize, Deserialize, Partial, Builder)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[partial(skip_serializing_none, from, diff)]
pub struct ProcedureConfig {
/// The stages to be run by the procedure.
#[serde(default, alias = "stage")]
#[partial_attr(serde(alias = "stage"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "stage"))
)]
#[builder(default)]
pub stages: Vec<ProcedureStage>,
@@ -207,6 +215,7 @@ impl utoipa::ToSchema for PartialProcedureConfig {}
#[typeshare]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ProcedureStage {
/// A name for the procedure
pub name: String,
@@ -222,6 +231,7 @@ pub struct ProcedureStage {
#[typeshare]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct EnabledExecution {
/// The execution request to run.
pub execution: Execution,
+12
View File
@@ -113,18 +113,30 @@ pub type _PartialRepoConfig = PartialRepoConfig;
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct RepoConfig {
/// The server to clone the repo on.
#[serde(default, alias = "server")]
#[partial_attr(serde(alias = "server"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "server"))
)]
#[builder(default)]
pub server_id: String,
/// Attach a builder to 'build' the repo.
#[serde(default, alias = "builder")]
#[partial_attr(serde(alias = "builder"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "builder"))
)]
#[builder(default)]
pub builder_id: String,
+8 -1
View File
@@ -10,7 +10,7 @@ use crate::{
deserializers::{
option_string_list_deserializer, string_list_deserializer,
},
entities::{MaintenanceWindow, Timelength},
entities::{_Serror, MaintenanceWindow, Timelength},
};
use super::{
@@ -37,6 +37,9 @@ pub type ServerListItem = ResourceListItem<ServerListItemInfo>;
pub struct ServerListItemInfo {
/// The server's state.
pub state: ServerState,
/// If there is an error reaching
/// the server, message will be given here.
pub err: Option<_Serror>,
/// Region of the server.
pub region: String,
/// Address of the server, or null if empty.
@@ -93,6 +96,10 @@ pub type _PartialServerConfig = PartialServerConfig;
#[derive(Serialize, Deserialize, Debug, Clone, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct ServerConfig {
+27
View File
@@ -293,6 +293,10 @@ pub type _PartialStackConfig = PartialStackConfig;
#[derive(Debug, Clone, Serialize, Deserialize, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[partial(skip_serializing_none, from, diff)]
pub struct StackConfig {
@@ -302,6 +306,10 @@ pub struct StackConfig {
/// swarm_id overrides server_id and the Stack will be in Swarm mode.
#[serde(default, alias = "swarm")]
#[partial_attr(serde(alias = "swarm"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "swarm"))
)]
#[builder(default)]
pub swarm_id: String,
@@ -311,6 +319,10 @@ pub struct StackConfig {
/// swarm_id overrides server_id and the Stack will be in Swarm mode.
#[serde(default, alias = "server")]
#[partial_attr(serde(alias = "server"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "server"))
)]
#[builder(default)]
pub server_id: String,
@@ -371,6 +383,17 @@ pub struct StackConfig {
#[builder(default)]
pub auto_update_all_services: bool,
/// Ignore certain services during Global Auto Update polling.
/// Services listed here are skipped only in the global auto-update flow.
/// Manual checks still include all services.
#[serde(default, deserialize_with = "string_list_deserializer")]
#[partial_attr(serde(
default,
deserialize_with = "option_string_list_deserializer"
))]
#[builder(default)]
pub auto_update_skip_services: Vec<String>,
/// Whether to run `docker compose down` before `compose up`.
#[serde(default)]
#[builder(default)]
@@ -688,6 +711,7 @@ impl Default for StackConfig {
poll_for_updates: Default::default(),
auto_update: Default::default(),
auto_update_all_services: Default::default(),
auto_update_skip_services: Default::default(),
ignore_services: Default::default(),
pre_deploy: Default::default(),
post_deploy: Default::default(),
@@ -915,6 +939,7 @@ pub struct StackRemoteFileContents {
Deserialize,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum StackFileRequires {
/// Diff requires service redeploy.
#[serde(alias = "redeploy")]
@@ -933,6 +958,7 @@ pub enum StackFileRequires {
#[typeshare]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct AdditionalEnvFile {
/// File path relative to run directory
pub path: String,
@@ -1012,6 +1038,7 @@ impl<'de> Deserialize<'de> for AdditionalEnvFile {
#[typeshare]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct StackFileDependency {
/// Specify the file
pub path: String,
+10 -2
View File
@@ -10,7 +10,7 @@ use crate::{
deserializers::{
option_string_list_deserializer, string_list_deserializer,
},
entities::MaintenanceWindow,
entities::{_Serror, MaintenanceWindow},
};
use super::resource::{Resource, ResourceListItem, ResourceQuery};
@@ -28,7 +28,7 @@ pub struct SwarmListItemInfo {
pub state: SwarmState,
/// If there is an error reaching
/// Swarm, message will be given here.
pub err: Option<String>,
pub err: Option<_Serror>,
}
#[typeshare]
@@ -82,6 +82,10 @@ pub type _PartialSwarmConfig = PartialSwarmConfig;
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Serialize, Deserialize, Debug, Clone, Default)]
#[partial(skip_serializing_none, from, diff)]
pub struct SwarmConfig {
@@ -90,6 +94,10 @@ pub struct SwarmConfig {
/// tries the next Server.
#[serde(default, alias = "servers")]
#[partial_attr(serde(alias = "servers"))]
#[cfg_attr(
feature = "schemars",
partial_attr(schemars(rename = "servers"))
)]
#[builder(default)]
pub server_ids: Vec<String>,
+4
View File
@@ -186,6 +186,10 @@ pub type _PartialResourceSyncConfig = PartialResourceSyncConfig;
#[derive(Debug, Clone, Serialize, Deserialize, Builder, Partial)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[partial_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(
feature = "schemars",
partial_derive(schemars::JsonSchema)
)]
#[diff_derive(Debug, Clone, Default, Serialize, Deserialize)]
#[partial(skip_serializing_none, from, diff)]
pub struct ResourceSyncConfig {
+41 -7
View File
@@ -26,102 +26,134 @@ use super::{
#[typeshare]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schemars", schemars(rename = "Resources"))]
pub struct ResourcesToml {
/// Declare a swarm
#[serde(
default,
alias = "swarm",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "swarm"))]
pub swarms: Vec<ResourceToml<_PartialSwarmConfig>>,
/// Declare a server
#[serde(
default,
alias = "server",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "server"))]
pub servers: Vec<ResourceToml<_PartialServerConfig>>,
#[serde(
default,
alias = "deployment",
skip_serializing_if = "Vec::is_empty"
)]
pub deployments: Vec<ResourceToml<_PartialDeploymentConfig>>,
/// Declare a stack
#[serde(
default,
alias = "stack",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "stack"))]
pub stacks: Vec<ResourceToml<_PartialStackConfig>>,
/// Declare a deployment
#[serde(
default,
alias = "deployment",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "deployment"))]
pub deployments: Vec<ResourceToml<_PartialDeploymentConfig>>,
/// Declare a build
#[serde(
default,
alias = "build",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "build"))]
pub builds: Vec<ResourceToml<_PartialBuildConfig>>,
/// Declare a repo
#[serde(
default,
alias = "repo",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "repo"))]
pub repos: Vec<ResourceToml<_PartialRepoConfig>>,
/// Declare a procedure
#[serde(
default,
alias = "procedure",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "procedure"))]
pub procedures: Vec<ResourceToml<_PartialProcedureConfig>>,
/// Declare an action
#[serde(
default,
alias = "action",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "action"))]
pub actions: Vec<ResourceToml<_PartialActionConfig>>,
/// Declare an alerter
#[serde(
default,
alias = "alerter",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "alerter"))]
pub alerters: Vec<ResourceToml<_PartialAlerterConfig>>,
/// Declare a builder
#[serde(
default,
alias = "builder",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "builder"))]
pub builders: Vec<ResourceToml<_PartialBuilderConfig>>,
/// Declare a resource sync
#[serde(
default,
alias = "resource_sync",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(
feature = "schemars",
schemars(rename = "resource_sync")
)]
pub resource_syncs: Vec<ResourceToml<_PartialResourceSyncConfig>>,
/// Declare a user group
#[serde(
default,
alias = "user_group",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "user_group"))]
pub user_groups: Vec<UserGroupToml>,
/// Declare a variable
#[serde(
default,
alias = "variable",
skip_serializing_if = "Vec::is_empty"
)]
#[cfg_attr(feature = "schemars", schemars(rename = "variable"))]
pub variables: Vec<Variable>,
}
#[typeshare]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ResourceToml<PartialConfig: Default> {
/// The resource name. Required
pub name: String,
@@ -165,6 +197,7 @@ fn is_false(b: &bool) -> bool {
#[typeshare]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct UserGroupToml {
/// User group name
pub name: String,
@@ -191,6 +224,7 @@ pub struct UserGroupToml {
#[typeshare]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct PermissionToml {
/// Id can be:
/// - resource name. `id = "abcd-build"`
+1
View File
@@ -6,6 +6,7 @@ use typeshare::typeshare;
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[cfg_attr(
feature = "mongo",
derive(mongo_indexed::derive::MongoIndexed)
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "komodo_client",
"version": "2.0.0",
"version": "2.2.0",
"description": "Komodo client package",
"homepage": "https://komo.do",
"main": "dist/lib.js",
@@ -13,10 +13,10 @@
"build": "tsc"
},
"dependencies": {
"mogh_auth_client": "^1.2.1"
"mogh_auth_client": "^1.5.0"
},
"devDependencies": {
"typescript": "^5.6.3"
"typescript": "^6.0.2"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
+1
View File
@@ -433,6 +433,7 @@ export type ExecuteResponses = {
// ==== SWARM ====
RemoveSwarmNodes: Types.Update;
UpdateSwarmNode: Types.Update;
RemoveSwarmStacks: Types.Update;
RemoveSwarmServices: Types.Update;
CreateSwarmConfig: Types.Update;
+74 -17
View File
@@ -378,6 +378,7 @@ export enum Operation {
RenameSwarm = "RenameSwarm",
DeleteSwarm = "DeleteSwarm",
RemoveSwarmNodes = "RemoveSwarmNodes",
UpdateSwarmNode = "UpdateSwarmNode",
RemoveSwarmStacks = "RemoveSwarmStacks",
RemoveSwarmServices = "RemoveSwarmServices",
CreateSwarmConfig = "CreateSwarmConfig",
@@ -611,6 +612,7 @@ export interface ImageRegistryConfig {
export interface SystemCommand {
path?: string;
command?: string;
shell_mode?: boolean;
}
/** The build configuration. */
@@ -927,6 +929,7 @@ export type Execution =
| { type: "PruneBuildx", params: PruneBuildx }
| { type: "PruneSystem", params: PruneSystem }
| { type: "RemoveSwarmNodes", params: RemoveSwarmNodes }
| { type: "UpdateSwarmNode", params: UpdateSwarmNode }
| { type: "RemoveSwarmStacks", params: RemoveSwarmStacks }
| { type: "RemoveSwarmServices", params: RemoveSwarmServices }
| { type: "CreateSwarmConfig", params: CreateSwarmConfig }
@@ -1425,6 +1428,8 @@ export enum DeploymentState {
Created = "created",
/** Server mode only. Container is in restart loop */
Restarting = "restarting",
/** Server mode only. Container is in the process of stopping */
Stopping = "stopping",
/** Server mode only. Container is being removed */
Removing = "removing",
/** Server mode only. Container is paused */
@@ -1561,7 +1566,7 @@ export type AlertData =
/** The name of the swarm */
name: string;
/** The error data */
err?: string;
err?: _Serror;
}}
/** A server could not be reached. */
| { type: "ServerUnreachable", data: {
@@ -2468,6 +2473,12 @@ export interface StackConfig {
* Komodo will redeploy the whole Stack (all services).
*/
auto_update_all_services?: boolean;
/**
* Ignore certain services during Global Auto Update polling.
* Services listed here are skipped only in the global auto-update flow.
* Manual checks still include all services.
*/
auto_update_skip_services?: string[];
/** Whether to run `docker compose down` before `compose up`. */
destroy_before_deploy?: boolean;
/** Whether to skip secret interpolation into the stack environment variables. */
@@ -2954,6 +2965,7 @@ export enum ContainerStateStatusEnum {
Paused = "paused",
Restarting = "restarting",
Exited = "exited",
Stopping = "stopping",
Removing = "removing",
Dead = "dead",
Empty = "",
@@ -3085,8 +3097,7 @@ export interface RestartPolicy {
MaximumRetryCount?: I64;
}
export enum MountTypeEnum {
Empty = "",
export enum MountType {
Bind = "bind",
Volume = "volume",
Image = "image",
@@ -3158,7 +3169,7 @@ export interface Mount {
* - `tmpfs` Create a tmpfs with the given options. The mount source cannot be specified for tmpfs. - `npipe` Mounts a named pipe from the host into the container. Must exist prior to creating the container.
* - `cluster` a Swarm cluster volume
*/
Type?: MountTypeEnum;
Type?: MountType;
/** Whether the mount should be read-only. */
ReadOnly?: boolean;
/** The consistency requirement for the mount: `default`, `consistent`, `cached`, or `delegated`. */
@@ -3331,7 +3342,7 @@ export interface GraphDriverData {
/** MountPoint represents a mount point configuration inside the container. This is used for reporting the mountpoints in use by a container. */
export interface MountPoint {
/** The mount type: - `bind` a mount of a file or directory from the host into the container. - `volume` a docker volume with the given `Name`. - `tmpfs` a `tmpfs`. - `npipe` a named pipe from the host into the container. - `cluster` a Swarm cluster volume */
Type?: MountTypeEnum;
Type?: string;
/** Name is the name reference to the underlying data defined by `Source` e.g., the volume name. */
Name?: string;
/** Source location of the mount. For volumes, this contains the storage location of the volume (within `/var/lib/docker/volumes/`). For bind-mounts, and `npipe`, this contains the source (host) part of the bind-mount. For `tmpfs` mount points, this field is empty. */
@@ -4291,7 +4302,10 @@ export interface ClusterVolumeSpecAccessModeSecrets {
Secret?: string;
}
export type Topology = Record<string, PortBinding[]>;
/** A map of topological domains to topological segments. For in depth details, see documentation for the Topology object in the CSI specification. */
export interface Topology {
Segments?: Record<string, string>;
}
/** Requirements for the accessible topology of the volume. These fields are optional. For an in-depth description of what these fields mean, see the CSI specification. */
export interface ClusterVolumeSpecAccessModeAccessibilityRequirements {
@@ -5328,9 +5342,21 @@ export enum ServerState {
Disabled = "Disabled",
}
export interface __Serror {
error: string;
trace: string[];
}
export type _Serror = __Serror;
export interface ServerListItemInfo {
/** The server's state. */
state: ServerState;
/**
* If there is an error reaching
* the server, message will be given here.
*/
err?: _Serror;
/** Region of the server. */
region: string;
/** Address of the server, or null if empty. */
@@ -5492,11 +5518,14 @@ export interface SwarmNodeListItem {
ID?: string;
/** Name for the node. */
Name?: string;
/** Node hostname, more commonly used than Name */
Hostname?: string;
/** Role of the node. */
Role?: NodeSpecRoleEnum;
/** Availability of the node. */
Availability?: NodeSpecAvailabilityEnum;
/** Labels of the node */
Labels?: Record<string, string>;
/** State of the node */
State?: NodeState;
/** For manager nodes, include the manager addr. */
@@ -5569,7 +5598,7 @@ export interface SwarmListItemInfo {
* If there is an error reaching
* Swarm, message will be given here.
*/
err?: string;
err?: _Serror;
}
export type SwarmListItem = ResourceListItem<SwarmListItemInfo>;
@@ -5778,13 +5807,6 @@ export type _PartialTag = Partial<Tag>;
export type _PartialUrlBuilderConfig = Partial<UrlBuilderConfig>;
export interface __Serror {
error: string;
trace: string[];
}
export type _Serror = __Serror;
/** **Admin only.** Add a user to a user group. Response: [UserGroup] */
export interface AddUserToUserGroup {
/** The name or id of UserGroup that user should be added to. */
@@ -6806,7 +6828,8 @@ export interface CreateOnboardingKey {
expires?: I64;
/**
* Optionally specify an existing private key, otherwise
* generate fresh key.
* generate fresh key. This key is not stored directly,
* only the public key.
*/
private_key?: string;
/** Default tags to apply to Servers created using this key. */
@@ -7428,18 +7451,31 @@ export interface UserGroupToml {
/** Specifies resources to sync on Komodo */
export interface ResourcesToml {
/** Declare a swarm */
swarms?: ResourceToml<_PartialSwarmConfig>[];
/** Declare a server */
servers?: ResourceToml<_PartialServerConfig>[];
deployments?: ResourceToml<_PartialDeploymentConfig>[];
/** Declare a stack */
stacks?: ResourceToml<_PartialStackConfig>[];
/** Declare a deployment */
deployments?: ResourceToml<_PartialDeploymentConfig>[];
/** Declare a build */
builds?: ResourceToml<_PartialBuildConfig>[];
/** Declare a repo */
repos?: ResourceToml<_PartialRepoConfig>[];
/** Declare a procedure */
procedures?: ResourceToml<_PartialProcedureConfig>[];
/** Declare an action */
actions?: ResourceToml<_PartialActionConfig>[];
/** Declare an alerter */
alerters?: ResourceToml<_PartialAlerterConfig>[];
/** Declare a builder */
builders?: ResourceToml<_PartialBuilderConfig>[];
/** Declare a resource sync */
resource_syncs?: ResourceToml<_PartialResourceSyncConfig>[];
/** Declare a user group */
user_groups?: UserGroupToml[];
/** Declare a variable */
variables?: Variable[];
}
@@ -7728,7 +7764,7 @@ export interface GetCoreInfoResponse {
enable_fancy_toml: boolean;
/** TZ identifier Core is using, if manually set. */
timezone: string;
/** Default public key allowing this Core to authenticate to Periphery agents. */
/** Public key for Core / Periphery authentication. */
public_key: string;
}
@@ -10477,6 +10513,26 @@ export interface UpdateSwarm {
config: _PartialSwarmConfig;
}
/**
* `docker node update [OPTIONS] NODE`
*
* https://docs.docker.com/reference/cli/docker/node/update/
*/
export interface UpdateSwarmNode {
/** Name or id */
swarm: string;
/** Node hostname or id */
node: string;
/** Update the node's availability: 'active', 'pause', or 'drain' */
availability?: NodeSpecAvailabilityEnum;
/** Add labels to node (`key=value`). */
label_add?: string[];
/** Add labels to node (`key=value`). (alias: `lr`) */
label_rm?: string[];
/** Update the node's role: 'worker' or 'manager' */
role?: NodeSpecRoleEnum;
}
/** Update color for tag. Response: [Tag]. */
export interface UpdateTagColor {
/** The name or id of the tag to update. */
@@ -10666,6 +10722,7 @@ export type ExecuteRequest =
| { type: "PruneBuildx", params: PruneBuildx }
| { type: "PruneSystem", params: PruneSystem }
| { type: "RemoveSwarmNodes", params: RemoveSwarmNodes }
| { type: "UpdateSwarmNode", params: UpdateSwarmNode }
| { type: "RemoveSwarmStacks", params: RemoveSwarmStacks }
| { type: "RemoveSwarmServices", params: RemoveSwarmServices }
| { type: "CreateSwarmConfig", params: CreateSwarmConfig }
+2 -1
View File
@@ -3,10 +3,11 @@
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"rootDir": "src/",
"outDir": "dist",
"declaration": true
}
+8 -8
View File
@@ -7,14 +7,14 @@ jwt-decode@^4.0.0:
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-4.0.0.tgz#2270352425fd413785b2faf11f6e755c5151bd4b"
integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==
mogh_auth_client@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/mogh_auth_client/-/mogh_auth_client-1.2.1.tgz#c8b5e9da101dc8da7b30586e5c5463f5f7a95edb"
integrity sha512-8uUjgqagwbMW8BKtTRzfQ4txpw+54hqLszbIvcYf99murVIQu5+NsRZ8/vjP/fOMawgXJXCVsR6O2lamm1BCnQ==
mogh_auth_client@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/mogh_auth_client/-/mogh_auth_client-1.5.0.tgz#f54b8ae35648d5bf1e60d7b67fcdcef45b0ccd7a"
integrity sha512-eUQmx682AzJRaY4QJSALtIGxXzg2r2Tr8exnDeFMrNe0Ibn4Oqm3h9SkOzYiqqZjKQ13wvHm0RoxT2QKo+908g==
dependencies:
jwt-decode "^4.0.0"
typescript@^5.6.3:
version "5.6.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
typescript@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.2.tgz#0b1bfb15f68c64b97032f3d78abbf98bdbba501f"
integrity sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==
+3 -4
View File
@@ -1,5 +1,3 @@
use std::collections::HashMap;
use komodo_client::entities::{
SearchCombinator,
deployment::Deployment,
@@ -63,11 +61,12 @@ pub struct RemoveSwarmNodes {
#[response(Log)]
#[error(anyhow::Error)]
pub struct UpdateSwarmNode {
/// Node hostname or id
pub node: String,
/// Update the node's availability: 'active', 'pause', or 'drain'
pub availability: Option<NodeSpecAvailabilityEnum>,
/// Add metadata to a swarm node using node labels (`key=value`).
/// You can specify a node label as a key with an empty value.
pub label_add: Option<HashMap<String, Option<String>>>,
pub label_add: Option<Vec<String>>,
/// Remove labels by the label key.
pub label_rm: Option<Vec<String>>,
/// Update the node role (`worker`, `manager`)
+2 -1
View File
@@ -145,6 +145,7 @@ PERIPHERY_CORE_PUBLIC_KEYS=file:/config/keys/core.pub
## ------ ./my_stack_2
## --- ./repos
## ------ ./my_repo_1
## --- ./builds
PERIPHERY_ROOT_DIRECTORY=/etc/komodo
## Specify whether to disable the terminals feature
@@ -164,4 +165,4 @@ PERIPHERY_INCLUDE_DISK_MOUNTS=/etc/hostname
## Prettier logging with empty lines between logs
PERIPHERY_LOGGING_PRETTY=false
## More human readable logging of startup config (multi-line)
PERIPHERY_PRETTY_STARTUP_CONFIG=false
PERIPHERY_PRETTY_STARTUP_CONFIG=false
+60 -3
View File
@@ -167,6 +167,20 @@ init_admin_password = "changeme"
## Default: false
disable_user_registration = false
## Disable local (username/password) user registration only.
## When set to true, the "Sign Up" button is hidden and local signups are blocked,
## but OIDC and other external provider signups may still be allowed.
## If not set, falls back to `disable_user_registration`.
## Env: KOMODO_DISABLE_LOCAL_USER_REGISTRATION
# disable_local_user_registration = true
## Disable OIDC user registration only.
## When set to true, new users cannot register via OIDC,
## but local and other provider signups may still be allowed.
## If not set, falls back to `disable_user_registration`.
## Env: KOMODO_DISABLE_OIDC_USER_REGISTRATION
# disable_oidc_user_registration = true
## New users will be automatically enabled when they sign up.
## Otherwise, new users will be disabled on first login.
## The first user to login will always be enabled on creation.
@@ -267,6 +281,13 @@ oidc_use_full_email = false
## Default: empty
oidc_additional_audiences = []
## Automatically redirect unauthenticated users to the OIDC provider
## instead of showing the login page.
## Users can bypass the redirect by appending `?disableAutoLogin` to the login URL.
## Env: KOMODO_OIDC_AUTO_REDIRECT
## Default: false
oidc_auto_redirect = false
#########
# OAUTH #
#########
@@ -324,9 +345,9 @@ auth_rate_limit_max_attempts = 5
## Default: 15
auth_rate_limit_window_seconds = 15
##################
# CORS / SESSION #
##################
############################
# CORS / SESSION / HEADERS #
############################
## Specifically set list of CORS allowed origins.
## If empty, allows all origins (`*`).
@@ -343,8 +364,44 @@ cors_allow_credentials = false
## Enabling this sets 'SameSite=None', which allows externally
## hosted UIs to use the login flows.
## Env: KOMODO_SESSION_ALLOW_CROSS_SITE
## Default: false
session_allow_cross_site = false
## `X-Content-Type-Options` header value.
## Set as empty string to omit the header.
## Env: KOMODO_X_CONTENT_TYPE_OPTIONS
## Default: "nosniff"
x_content_type_options = "nosniff"
## `X-Frame-Options` header value.
## Set as empty string to omit the header.
## Use "SAMEORIGIN" to allow same-origin embedding only.
## Env: KOMODO_X_FRAME_OPTIONS
## Default: "DENY"
x_frame_options = "DENY"
## `X-Xss-Protection` header value.
## Set as empty string to omit the header.
## Env: KOMODO_X_XSS_PROTECTION
## Default: "1; mode=block"
x_xss_protection = "1; mode=block"
## Apply Referrer Policy directives.
## If empty string, no header is applied.
## Env: KOMODO_REFERRER_POLICY
## Default: "strict-origin-when-cross-origin"
referrer_policy = "strict-origin-when-cross-origin"
## Apply Content Security Policy directives.
## If empty string, no header is applied.
## Env: KOMODO_CONTENT_SECURITY_POLICY
## Default: ""
##
## Example:
## `default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'`
content_security_policy = ""
##################
# POLL INTERVALS #
##################
+2 -1
View File
@@ -33,6 +33,7 @@
## ------ ./my_stack_2
## --- ./repos
## ------ ./my_repo_1
## --- ./builds
## Each specific sub-directory (like ./stacks) can be overridden below.
## Env: PERIPHERY_ROOT_DIRECTORY
## Default: /etc/komodo
@@ -291,4 +292,4 @@ pretty_startup_config = false
## Provide periphery-based secrets
# [secrets]
# SECRET_1 = "value_1"
# SECRET_2 = "value_2"
# SECRET_2 = "value_2"

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