prog on swarm

This commit is contained in:
mbecker20
2025-11-19 01:48:43 -08:00
parent a9f55bb8e6
commit d5e03d6d16
52 changed files with 2670 additions and 1139 deletions

40
Cargo.lock generated
View File

@@ -908,7 +908,7 @@ dependencies = [
[[package]]
name = "cache"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"tokio",
@@ -1100,7 +1100,7 @@ dependencies = [
[[package]]
name = "command"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"komodo_client",
"shlex",
@@ -1127,7 +1127,7 @@ checksum = "3a9b614a5787ef0c8802a55766480563cb3a93b435898c422ed2a359cf811582"
[[package]]
name = "config"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"colored",
"indexmap 2.12.0",
@@ -1449,7 +1449,7 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
[[package]]
name = "database"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"async-compression",
@@ -1748,7 +1748,7 @@ dependencies = [
[[package]]
name = "encoding"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"bytes",
@@ -1790,7 +1790,7 @@ dependencies = [
[[package]]
name = "environment"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"formatting",
@@ -1800,7 +1800,7 @@ dependencies = [
[[package]]
name = "environment_file"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"thiserror 2.0.17",
]
@@ -1902,7 +1902,7 @@ dependencies = [
[[package]]
name = "formatting"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"serror",
]
@@ -2068,7 +2068,7 @@ dependencies = [
[[package]]
name = "git"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"cache",
@@ -2702,7 +2702,7 @@ dependencies = [
[[package]]
name = "interpolate"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"komodo_client",
@@ -2824,7 +2824,7 @@ dependencies = [
[[package]]
name = "komodo_cli"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"chrono",
@@ -2852,7 +2852,7 @@ dependencies = [
[[package]]
name = "komodo_client"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"async_timing_util",
@@ -2888,7 +2888,7 @@ dependencies = [
[[package]]
name = "komodo_core"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"arc-swap",
@@ -2962,7 +2962,7 @@ dependencies = [
[[package]]
name = "komodo_periphery"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"arc-swap",
@@ -3083,7 +3083,7 @@ checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "logger"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"komodo_client",
@@ -3375,7 +3375,7 @@ dependencies = [
[[package]]
name = "noise"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"arc-swap",
@@ -3799,7 +3799,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "periphery_client"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"derive_variants",
@@ -4280,7 +4280,7 @@ dependencies = [
[[package]]
name = "response"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"axum",
@@ -4550,7 +4550,7 @@ dependencies = [
[[package]]
name = "secret_file"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"tokio",
]
@@ -5583,7 +5583,7 @@ dependencies = [
[[package]]
name = "transport"
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
dependencies = [
"anyhow",
"axum",

View File

@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
version = "2.0.0-dev-90"
version = "2.0.0-dev-91"
edition = "2024"
authors = ["mbecker20 <becker.maxh@gmail.com>"]
license = "GPL-3.0-or-later"

View File

@@ -142,13 +142,13 @@ pub enum ExecuteRequest {
RunAction(RunAction),
BatchRunAction(BatchRunAction),
// ==== SYNC ====
RunSync(RunSync),
// ==== ALERTER ====
TestAlerter(TestAlerter),
SendAlert(SendAlert),
// ==== SYNC ====
RunSync(RunSync),
// ==== MAINTENANCE ====
ClearRepoCache(ClearRepoCache),
BackupCoreDatabase(BackupCoreDatabase),

View File

@@ -49,6 +49,7 @@ mod repo;
mod schedule;
mod server;
mod stack;
mod swarm;
mod sync;
mod tag;
mod terminal;
@@ -75,36 +76,12 @@ enum ReadRequest {
ListGitProvidersFromConfig(ListGitProvidersFromConfig),
ListDockerRegistriesFromConfig(ListDockerRegistriesFromConfig),
// ==== USER ====
GetUsername(GetUsername),
GetPermission(GetPermission),
FindUser(FindUser),
ListUsers(ListUsers),
ListApiKeys(ListApiKeys),
ListApiKeysForServiceUser(ListApiKeysForServiceUser),
ListPermissions(ListPermissions),
ListUserTargetPermissions(ListUserTargetPermissions),
// ==== USER GROUP ====
GetUserGroup(GetUserGroup),
ListUserGroups(ListUserGroups),
// ==== PROCEDURE ====
GetProceduresSummary(GetProceduresSummary),
GetProcedure(GetProcedure),
GetProcedureActionState(GetProcedureActionState),
ListProcedures(ListProcedures),
ListFullProcedures(ListFullProcedures),
// ==== ACTION ====
GetActionsSummary(GetActionsSummary),
GetAction(GetAction),
GetActionActionState(GetActionActionState),
ListActions(ListActions),
ListFullActions(ListFullActions),
// ==== SCHEDULE ====
ListSchedules(ListSchedules),
// ==== SWARM ====
GetSwarmsSummary(GetSwarmsSummary),
GetSwarm(GetSwarm),
GetSwarmActionState(GetSwarmActionState),
ListSwarms(ListSwarms),
ListFullSwarms(ListFullSwarms),
// ==== SERVER ====
GetServersSummary(GetServersSummary),
@@ -112,7 +89,6 @@ enum ReadRequest {
GetServerState(GetServerState),
GetPeripheryInformation(GetPeripheryInformation),
GetServerActionState(GetServerActionState),
GetHistoricalServerStats(GetHistoricalServerStats),
ListServers(ListServers),
ListFullServers(ListFullServers),
@@ -139,6 +115,7 @@ enum ReadRequest {
// ==== SERVER STATS ====
GetSystemInformation(GetSystemInformation),
GetSystemStats(GetSystemStats),
GetHistoricalServerStats(GetHistoricalServerStats),
ListSystemProcesses(ListSystemProcesses),
// ==== STACK ====
@@ -184,6 +161,23 @@ enum ReadRequest {
ListRepos(ListRepos),
ListFullRepos(ListFullRepos),
// ==== PROCEDURE ====
GetProceduresSummary(GetProceduresSummary),
GetProcedure(GetProcedure),
GetProcedureActionState(GetProcedureActionState),
ListProcedures(ListProcedures),
ListFullProcedures(ListFullProcedures),
// ==== ACTION ====
GetActionsSummary(GetActionsSummary),
GetAction(GetAction),
GetActionActionState(GetActionActionState),
ListActions(ListActions),
ListFullActions(ListFullActions),
// ==== SCHEDULE ====
ListSchedules(ListSchedules),
// ==== SYNC ====
GetResourceSyncsSummary(GetResourceSyncsSummary),
GetResourceSync(GetResourceSync),
@@ -211,6 +205,20 @@ enum ReadRequest {
GetTag(GetTag),
ListTags(ListTags),
// ==== USER ====
GetUsername(GetUsername),
GetPermission(GetPermission),
FindUser(FindUser),
ListUsers(ListUsers),
ListApiKeys(ListApiKeys),
ListApiKeysForServiceUser(ListApiKeysForServiceUser),
ListPermissions(ListPermissions),
ListUserTargetPermissions(ListUserTargetPermissions),
// ==== USER GROUP ====
GetUserGroup(GetUserGroup),
ListUserGroups(ListUserGroups),
// ==== UPDATE ====
GetUpdate(GetUpdate),
ListUpdates(ListUpdates),

View File

@@ -383,8 +383,8 @@ impl Resolve<ReadArgs> for ListDockerContainers {
let cache = server_status_cache()
.get_or_insert_default(&server.id)
.await;
if let Some(containers) = &cache.containers {
Ok(containers.clone())
if let Some(docker) = &cache.docker {
Ok(docker.containers.clone())
} else {
Ok(Vec::new())
}
@@ -410,10 +410,11 @@ impl Resolve<ReadArgs> for ListAllDockerContainers {
let cache = server_status_cache()
.get_or_insert_default(&server.id)
.await;
let Some(more) = &cache.containers else {
let Some(docker) = &cache.docker else {
continue;
};
let more = more
let more = docker
.containers
.iter()
.filter(|container| {
self.containers.is_empty()
@@ -448,8 +449,8 @@ impl Resolve<ReadArgs> for GetDockerContainersSummary {
.get_or_insert_default(&server.id)
.await;
if let Some(containers) = &cache.containers {
for container in containers {
if let Some(docker) = &cache.docker {
for container in &docker.containers {
res.total += 1;
match container.state {
ContainerStateStatusEnum::Created
@@ -641,8 +642,8 @@ impl Resolve<ReadArgs> for ListDockerNetworks {
let cache = server_status_cache()
.get_or_insert_default(&server.id)
.await;
if let Some(networks) = &cache.networks {
Ok(networks.clone())
if let Some(docker) = &cache.docker {
Ok(docker.networks.clone())
} else {
Ok(Vec::new())
}
@@ -694,8 +695,8 @@ impl Resolve<ReadArgs> for ListDockerImages {
let cache = server_status_cache()
.get_or_insert_default(&server.id)
.await;
if let Some(images) = &cache.images {
Ok(images.clone())
if let Some(docker) = &cache.docker {
Ok(docker.images.clone())
} else {
Ok(Vec::new())
}
@@ -775,8 +776,8 @@ impl Resolve<ReadArgs> for ListDockerVolumes {
let cache = server_status_cache()
.get_or_insert_default(&server.id)
.await;
if let Some(volumes) = &cache.volumes {
Ok(volumes.clone())
if let Some(docker) = &cache.docker {
Ok(docker.volumes.clone())
} else {
Ok(Vec::new())
}
@@ -825,8 +826,8 @@ impl Resolve<ReadArgs> for ListComposeProjects {
let cache = server_status_cache()
.get_or_insert_default(&server.id)
.await;
if let Some(projects) = &cache.projects {
Ok(projects.clone())
if let Some(docker) = &cache.docker {
Ok(docker.projects.clone())
} else {
Ok(Vec::new())
}

View File

@@ -0,0 +1,140 @@
use anyhow::Context;
use komodo_client::{
api::read::*,
entities::{
permission::PermissionLevel,
swarm::{Swarm, SwarmActionState, SwarmListItem, SwarmState},
},
};
use resolver_api::Resolve;
use crate::{
helpers::query::get_all_tags, permission::get_check_permissions,
resource, state::{action_states, swarm_status_cache},
};
use super::ReadArgs;
impl Resolve<ReadArgs> for GetSwarm {
async fn resolve(
self,
ReadArgs { user }: &ReadArgs,
) -> serror::Result<Swarm> {
Ok(
get_check_permissions::<Swarm>(
&self.swarm,
user,
PermissionLevel::Read.into(),
)
.await?,
)
}
}
impl Resolve<ReadArgs> for ListSwarms {
async fn resolve(
self,
ReadArgs { user }: &ReadArgs,
) -> serror::Result<Vec<SwarmListItem>> {
let all_tags = if self.query.tags.is_empty() {
vec![]
} else {
get_all_tags(None).await?
};
Ok(
resource::list_for_user::<Swarm>(
self.query,
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?,
)
}
}
impl Resolve<ReadArgs> for ListFullSwarms {
async fn resolve(
self,
ReadArgs { user }: &ReadArgs,
) -> serror::Result<ListFullSwarmsResponse> {
let all_tags = if self.query.tags.is_empty() {
vec![]
} else {
get_all_tags(None).await?
};
Ok(
resource::list_full_for_user::<Swarm>(
self.query,
user,
PermissionLevel::Read.into(),
&all_tags,
)
.await?,
)
}
}
impl Resolve<ReadArgs> for GetSwarmActionState {
async fn resolve(
self,
ReadArgs { user }: &ReadArgs,
) -> serror::Result<SwarmActionState> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
user,
PermissionLevel::Read.into(),
)
.await?;
let action_state = action_states()
.swarm
.get(&swarm.id)
.await
.unwrap_or_default()
.get()?;
Ok(action_state)
}
}
impl Resolve<ReadArgs> for GetSwarmsSummary {
async fn resolve(
self,
ReadArgs { user }: &ReadArgs,
) -> serror::Result<GetSwarmsSummaryResponse> {
let swarms = resource::list_full_for_user::<Swarm>(
Default::default(),
user,
PermissionLevel::Read.into(),
&[],
)
.await
.context("failed to get swarms from db")?;
let mut res = GetSwarmsSummaryResponse::default();
let cache = swarm_status_cache();
for swarm in swarms {
res.total += 1;
match cache
.get(&swarm.id)
.await
.map(|status| status.state)
.unwrap_or_default()
{
SwarmState::Unknown => {
res.unknown += 1;
}
SwarmState::Healthy => {
res.healthy += 1;
}
SwarmState::Unhealthy => {
res.unhealthy += 1;
}
}
}
Ok(res)
}
}

View File

@@ -32,6 +32,7 @@ mod resource;
mod server;
mod service_user;
mod stack;
mod swarm;
mod sync;
mod tag;
mod terminal;
@@ -53,36 +54,16 @@ pub struct WriteArgs {
#[error(serror::Error)]
#[serde(tag = "type", content = "params")]
pub enum WriteRequest {
// ==== USER ====
CreateLocalUser(CreateLocalUser),
UpdateUserUsername(UpdateUserUsername),
UpdateUserPassword(UpdateUserPassword),
DeleteUser(DeleteUser),
// ==== SERVICE USER ====
CreateServiceUser(CreateServiceUser),
UpdateServiceUserDescription(UpdateServiceUserDescription),
CreateApiKeyForServiceUser(CreateApiKeyForServiceUser),
DeleteApiKeyForServiceUser(DeleteApiKeyForServiceUser),
// ==== USER GROUP ====
CreateUserGroup(CreateUserGroup),
RenameUserGroup(RenameUserGroup),
DeleteUserGroup(DeleteUserGroup),
AddUserToUserGroup(AddUserToUserGroup),
RemoveUserFromUserGroup(RemoveUserFromUserGroup),
SetUsersInUserGroup(SetUsersInUserGroup),
SetEveryoneUserGroup(SetEveryoneUserGroup),
// ==== PERMISSIONS ====
UpdateUserAdmin(UpdateUserAdmin),
UpdateUserBasePermissions(UpdateUserBasePermissions),
UpdatePermissionOnResourceType(UpdatePermissionOnResourceType),
UpdatePermissionOnTarget(UpdatePermissionOnTarget),
// ==== RESOURCE ====
UpdateResourceMeta(UpdateResourceMeta),
// ==== SWARM ====
CreateSwarm(CreateSwarm),
CopySwarm(CopySwarm),
DeleteSwarm(DeleteSwarm),
UpdateSwarm(UpdateSwarm),
RenameSwarm(RenameSwarm),
// ==== SERVER ====
CreateServer(CreateServer),
CopyServer(CopyServer),
@@ -93,6 +74,12 @@ pub enum WriteRequest {
UpdateServerPublicKey(UpdateServerPublicKey),
RotateServerKeys(RotateServerKeys),
// ==== TERMINAL ====
CreateTerminal(CreateTerminal),
DeleteTerminal(DeleteTerminal),
DeleteAllTerminals(DeleteAllTerminals),
BatchDeleteAllTerminals(BatchDeleteAllTerminals),
// ==== STACK ====
CreateStack(CreateStack),
CopyStack(CopyStack),
@@ -119,13 +106,6 @@ pub enum WriteRequest {
WriteBuildFileContents(WriteBuildFileContents),
RefreshBuildCache(RefreshBuildCache),
// ==== BUILDER ====
CreateBuilder(CreateBuilder),
CopyBuilder(CopyBuilder),
DeleteBuilder(DeleteBuilder),
UpdateBuilder(UpdateBuilder),
RenameBuilder(RenameBuilder),
// ==== REPO ====
CreateRepo(CreateRepo),
CopyRepo(CopyRepo),
@@ -134,13 +114,6 @@ pub enum WriteRequest {
RenameRepo(RenameRepo),
RefreshRepoCache(RefreshRepoCache),
// ==== ALERTER ====
CreateAlerter(CreateAlerter),
CopyAlerter(CopyAlerter),
DeleteAlerter(DeleteAlerter),
UpdateAlerter(UpdateAlerter),
RenameAlerter(RenameAlerter),
// ==== PROCEDURE ====
CreateProcedure(CreateProcedure),
CopyProcedure(CopyProcedure),
@@ -165,11 +138,51 @@ pub enum WriteRequest {
CommitSync(CommitSync),
RefreshResourceSyncPending(RefreshResourceSyncPending),
// ==== TERMINAL ====
CreateTerminal(CreateTerminal),
DeleteTerminal(DeleteTerminal),
DeleteAllTerminals(DeleteAllTerminals),
BatchDeleteAllTerminals(BatchDeleteAllTerminals),
// ==== BUILDER ====
CreateBuilder(CreateBuilder),
CopyBuilder(CopyBuilder),
DeleteBuilder(DeleteBuilder),
UpdateBuilder(UpdateBuilder),
RenameBuilder(RenameBuilder),
// ==== ALERTER ====
CreateAlerter(CreateAlerter),
CopyAlerter(CopyAlerter),
DeleteAlerter(DeleteAlerter),
UpdateAlerter(UpdateAlerter),
RenameAlerter(RenameAlerter),
// ==== ONBOARDING KEY ====
CreateOnboardingKey(CreateOnboardingKey),
UpdateOnboardingKey(UpdateOnboardingKey),
DeleteOnboardingKey(DeleteOnboardingKey),
// ==== USER ====
CreateLocalUser(CreateLocalUser),
UpdateUserUsername(UpdateUserUsername),
UpdateUserPassword(UpdateUserPassword),
DeleteUser(DeleteUser),
// ==== SERVICE USER ====
CreateServiceUser(CreateServiceUser),
UpdateServiceUserDescription(UpdateServiceUserDescription),
CreateApiKeyForServiceUser(CreateApiKeyForServiceUser),
DeleteApiKeyForServiceUser(DeleteApiKeyForServiceUser),
// ==== USER GROUP ====
CreateUserGroup(CreateUserGroup),
RenameUserGroup(RenameUserGroup),
DeleteUserGroup(DeleteUserGroup),
AddUserToUserGroup(AddUserToUserGroup),
RemoveUserFromUserGroup(RemoveUserFromUserGroup),
SetUsersInUserGroup(SetUsersInUserGroup),
SetEveryoneUserGroup(SetEveryoneUserGroup),
// ==== PERMISSIONS ====
UpdateUserAdmin(UpdateUserAdmin),
UpdateUserBasePermissions(UpdateUserBasePermissions),
UpdatePermissionOnResourceType(UpdatePermissionOnResourceType),
UpdatePermissionOnTarget(UpdatePermissionOnTarget),
// ==== TAG ====
CreateTag(CreateTag),
@@ -192,11 +205,6 @@ pub enum WriteRequest {
UpdateDockerRegistryAccount(UpdateDockerRegistryAccount),
DeleteDockerRegistryAccount(DeleteDockerRegistryAccount),
// ==== ONBOARDING KEY ====
CreateOnboardingKey(CreateOnboardingKey),
UpdateOnboardingKey(UpdateOnboardingKey),
DeleteOnboardingKey(DeleteOnboardingKey),
// ==== ALERT ====
CloseAlert(CloseAlert),
}

View File

@@ -0,0 +1,108 @@
use komodo_client::{
api::write::*,
entities::{
permission::PermissionLevel, swarm::Swarm, update::Update,
},
};
use resolver_api::Resolve;
use crate::{permission::get_check_permissions, resource};
use super::WriteArgs;
impl Resolve<WriteArgs> for CreateSwarm {
#[instrument(
"CreateSwarm",
skip_all,
fields(
operator = user.id,
swarm = self.name,
config = serde_json::to_string(&self.config).unwrap(),
)
)]
async fn resolve(
self,
WriteArgs { user }: &WriteArgs,
) -> serror::Result<Swarm> {
resource::create::<Swarm>(&self.name, self.config, None, user)
.await
}
}
impl Resolve<WriteArgs> for CopySwarm {
#[instrument(
"CopySwarm",
skip_all,
fields(
operator = user.id,
swarm = self.name,
copy_swarm = self.id,
)
)]
async fn resolve(
self,
WriteArgs { user }: &WriteArgs,
) -> serror::Result<Swarm> {
let Swarm { config, .. } = get_check_permissions::<Swarm>(
&self.id,
user,
PermissionLevel::Read.into(),
)
.await?;
resource::create::<Swarm>(&self.name, config.into(), None, user)
.await
}
}
impl Resolve<WriteArgs> for DeleteSwarm {
#[instrument(
"DeleteSwarm",
skip_all,
fields(
operator = user.id,
swarm = self.id,
)
)]
async fn resolve(
self,
WriteArgs { user }: &WriteArgs,
) -> serror::Result<Swarm> {
Ok(resource::delete::<Swarm>(&self.id, user).await?)
}
}
impl Resolve<WriteArgs> for UpdateSwarm {
#[instrument(
"UpdateSwarm",
skip_all,
fields(
operator = user.id,
swarm = self.id,
update = serde_json::to_string(&self.config).unwrap()
)
)]
async fn resolve(
self,
WriteArgs { user }: &WriteArgs,
) -> serror::Result<Swarm> {
Ok(resource::update::<Swarm>(&self.id, self.config, user).await?)
}
}
impl Resolve<WriteArgs> for RenameSwarm {
#[instrument(
"RenameSwarm",
skip_all,
fields(
operator = user.id,
swarm = self.id,
new_name = self.name
)
)]
async fn resolve(
self,
WriteArgs { user }: &WriteArgs,
) -> serror::Result<Update> {
Ok(resource::rename::<Swarm>(&self.id, &self.name, user).await?)
}
}

View File

@@ -45,7 +45,6 @@ impl PeripheryConnectionArgs<'_> {
periphery_connections().insert(id.clone(), self).await;
let responses = connection.responses.clone();
let terminals = connection.terminals.clone();
tokio::spawn(async move {
loop {
@@ -94,7 +93,6 @@ impl PeripheryConnectionArgs<'_> {
Ok(PeripheryClient {
id,
responses,
terminals,
})
}
}

View File

@@ -9,12 +9,13 @@ use komodo_client::{
deployment::DeploymentActionState,
procedure::ProcedureActionState, repo::RepoActionState,
server::ServerActionState, stack::StackActionState,
sync::ResourceSyncActionState,
swarm::SwarmActionState, sync::ResourceSyncActionState,
},
};
#[derive(Default)]
pub struct ActionStates {
pub swarm: CloneCache<String, Arc<ActionState<SwarmActionState>>>,
pub server: CloneCache<String, Arc<ActionState<ServerActionState>>>,
pub stack: CloneCache<String, Arc<ActionState<StackActionState>>>,
pub deployment:

View File

@@ -76,7 +76,7 @@ async fn app() -> anyhow::Result<()> {
startup::on_startup().await;
// Spawn background tasks
monitor::spawn_monitor_loop();
monitor::spawn_monitoring_loops();
resource::spawn_resource_refresh_loop();
resource::spawn_all_resources_cache_refresh_loop();
resource::spawn_build_state_refresh_loop();

View File

@@ -1,29 +1,73 @@
use komodo_client::entities::{
alert::SeverityLevel,
deployment::{Deployment, DeploymentState},
docker::{
container::ContainerListItem, image::ImageListItem,
network::NetworkListItem, volume::VolumeListItem,
},
docker::DockerLists,
repo::Repo,
server::{
PeripheryInformation, Server, ServerConfig, ServerHealth,
ServerHealthState, ServerState,
},
stack::{ComposeProject, Stack, StackState},
stack::{Stack, StackState},
stats::{SingleDiskUsage, SystemInformation, SystemStats},
};
use serror::Serror;
use crate::state::{
deployment_status_cache, repo_status_cache, server_status_cache,
stack_status_cache,
CachedDeploymentStatus, CachedRepoStatus, CachedServerStatus,
CachedStackStatus, History, deployment_status_cache,
repo_status_cache, server_status_cache, stack_status_cache,
};
use super::{
CachedDeploymentStatus, CachedRepoStatus, CachedServerStatus,
CachedStackStatus, History,
};
pub async fn insert_server_status(
server: &Server,
state: ServerState,
periphery_info: Option<PeripheryInformation>,
system_info: Option<SystemInformation>,
system_stats: Option<SystemStats>,
docker: Option<DockerLists>,
err: impl Into<Option<Serror>>,
) {
let health =
system_stats.as_ref().map(|s| get_server_health(server, s));
server_status_cache()
.insert(
server.id.clone(),
CachedServerStatus {
id: server.id.clone(),
state,
periphery_info,
system_info,
system_stats,
health,
docker,
err: err.into(),
}
.into(),
)
.await;
}
pub async fn insert_stacks_status_unknown(stacks: Vec<Stack>) {
let status_cache = stack_status_cache();
for stack in stacks {
let prev =
status_cache.get(&stack.id).await.map(|s| s.curr.state);
status_cache
.insert(
stack.id.clone(),
History {
curr: CachedStackStatus {
id: stack.id,
state: StackState::Unknown,
services: Vec::new(),
},
prev,
}
.into(),
)
.await;
}
}
pub async fn insert_deployments_status_unknown(
deployments: Vec<Deployment>,
@@ -66,69 +110,6 @@ pub async fn insert_repos_status_unknown(repos: Vec<Repo>) {
}
}
pub async fn insert_stacks_status_unknown(stacks: Vec<Stack>) {
let status_cache = stack_status_cache();
for stack in stacks {
let prev =
status_cache.get(&stack.id).await.map(|s| s.curr.state);
status_cache
.insert(
stack.id.clone(),
History {
curr: CachedStackStatus {
id: stack.id,
state: StackState::Unknown,
services: Vec::new(),
},
prev,
}
.into(),
)
.await;
}
}
type DockerLists = (
Option<Vec<ContainerListItem>>,
Option<Vec<NetworkListItem>>,
Option<Vec<ImageListItem>>,
Option<Vec<VolumeListItem>>,
Option<Vec<ComposeProject>>,
);
pub async fn insert_server_status(
server: &Server,
state: ServerState,
periphery_info: Option<PeripheryInformation>,
system_info: Option<SystemInformation>,
system_stats: Option<SystemStats>,
(containers, networks, images, volumes, projects): DockerLists,
err: impl Into<Option<Serror>>,
) {
let health =
system_stats.as_ref().map(|s| get_server_health(server, s));
server_status_cache()
.insert(
server.id.clone(),
CachedServerStatus {
id: server.id.clone(),
state,
periphery_info,
system_info,
system_stats,
health,
containers,
networks,
images,
volumes,
projects,
err: err.into(),
}
.into(),
)
.await;
}
const ALERT_PERCENTAGE_THRESHOLD: f32 = 5.0;
fn get_server_health(

View File

@@ -7,19 +7,15 @@ use futures_util::future::join_all;
use helpers::insert_stacks_status_unknown;
use komodo_client::entities::{
build::Build,
deployment::{Deployment, DeploymentState},
docker::{
container::ContainerListItem, image::ImageListItem,
network::NetworkListItem, volume::VolumeListItem,
},
deployment::Deployment,
komodo_timestamp, optional_string,
repo::Repo,
server::{PeripheryInformation, Server, ServerHealth, ServerState},
stack::{ComposeProject, Stack, StackService, StackState},
stats::{SystemInformation, SystemStats},
server::{Server, ServerState},
stack::Stack,
stats::SystemStats,
};
use periphery_client::api::{
self, PollStatusResponse, git::GetLatestCommit,
self, git::GetLatestCommit, poll::PollStatusResponse,
};
use serror::Serror;
use tokio::sync::Mutex;
@@ -29,8 +25,8 @@ use crate::{
helpers::periphery_client,
monitor::{alert::check_alerts, record::record_server_stats},
state::{
db_client, deployment_status_cache, periphery_connections,
repo_status_cache,
CachedRepoStatus, db_client, deployment_status_cache,
periphery_connections, repo_status_cache,
},
};
@@ -43,62 +39,24 @@ mod alert;
mod helpers;
mod record;
mod resources;
mod swarm;
#[derive(Default, Debug)]
pub struct History<Curr: Default, Prev> {
pub curr: Curr,
pub prev: Option<Prev>,
}
#[derive(Default, Clone, Debug)]
pub struct CachedServerStatus {
pub id: String,
pub state: ServerState,
pub health: Option<ServerHealth>,
pub periphery_info: Option<PeripheryInformation>,
pub system_info: Option<SystemInformation>,
pub system_stats: Option<SystemStats>,
pub containers: Option<Vec<ContainerListItem>>,
pub networks: Option<Vec<NetworkListItem>>,
pub images: Option<Vec<ImageListItem>>,
pub volumes: Option<Vec<VolumeListItem>>,
pub projects: Option<Vec<ComposeProject>>,
/// Store the error in reaching periphery
pub err: Option<serror::Serror>,
}
#[derive(Default, Clone, Debug)]
pub struct CachedDeploymentStatus {
/// The deployment id
pub id: String,
pub state: DeploymentState,
pub container: Option<ContainerListItem>,
pub update_available: bool,
}
#[derive(Default, Clone, Debug)]
pub struct CachedRepoStatus {
pub latest_hash: Option<String>,
pub latest_message: Option<String>,
}
#[derive(Default, Clone, Debug)]
pub struct CachedStackStatus {
/// The stack id
pub id: String,
/// The stack state
pub state: StackState,
/// The services connected to the stack
pub services: Vec<StackService>,
}
pub use swarm::update_cache_for_swarm;
const ADDITIONAL_MS: u128 = 500;
pub fn spawn_monitor_loop() {
let interval: async_timing_util::Timelength = core_config()
pub fn spawn_monitoring_loops() {
let interval = core_config()
.monitoring_interval
.try_into()
.expect("Invalid monitoring interval");
spawn_server_monitoring_loop(interval);
swarm::spawn_swarm_monitoring_loop(interval);
}
fn spawn_server_monitoring_loop(
interval: async_timing_util::Timelength,
) {
tokio::spawn(async move {
refresh_server_cache(komodo_timestamp()).await;
loop {
@@ -115,7 +73,7 @@ async fn refresh_server_cache(ts: i64) {
Ok(servers) => servers,
Err(e) => {
error!(
"failed to get server list (manage status cache) | {e:#}"
"Failed to get server list (refresh server cache) | {e:#}"
);
return;
}
@@ -172,7 +130,7 @@ pub async fn update_cache_for_server(server: &Server, force: bool) {
None,
None,
None,
(None, None, None, None, None),
None,
None,
)
.await;
@@ -190,7 +148,7 @@ pub async fn update_cache_for_server(server: &Server, force: bool) {
None,
None,
None,
(None, None, None, None, None),
None,
Serror::from(&e),
)
.await;
@@ -202,14 +160,11 @@ pub async fn update_cache_for_server(server: &Server, force: bool) {
periphery_info,
system_info,
system_stats,
mut containers,
networks,
images,
volumes,
projects,
mut docker,
} = match periphery
.request(api::PollStatus {
.request(api::poll::PollStatus {
include_stats: server.config.stats_monitoring,
include_docker: true,
})
.await
{
@@ -222,7 +177,7 @@ pub async fn update_cache_for_server(server: &Server, force: bool) {
None,
None,
None,
(None, None, None, None, None),
None,
Serror::from(&e),
)
.await;
@@ -230,37 +185,44 @@ pub async fn update_cache_for_server(server: &Server, force: bool) {
}
};
containers.iter_mut().for_each(|container| {
container.server_id = Some(server.id.clone())
});
if let Some(docker) = &mut docker {
docker.containers.iter_mut().for_each(|container| {
container.server_id = Some(server.id.clone())
});
}
let containers = docker
.as_ref()
.map(|docker| docker.containers.as_slice())
.unwrap_or(&[]);
let images = docker
.as_ref()
.map(|docker| docker.images.as_slice())
.unwrap_or(&[]);
tokio::join!(
resources::update_deployment_cache(
server.name.clone(),
resources.deployments,
&containers,
&images,
containers,
images,
&resources.builds,
),
resources::update_stack_cache(
server.name.clone(),
resources.stacks,
&containers,
&images
containers,
images
),
);
insert_server_status(
server,
ServerState::Ok,
Some(periphery_info),
Some(system_info),
system_stats.map(|stats| filter_volumes(server, stats)),
(
Some(containers.clone()),
Some(networks),
Some(images),
Some(volumes),
Some(projects),
),
docker,
None,
)
.await;

View File

@@ -30,200 +30,12 @@ use crate::{
services::extract_services_from_stack,
},
state::{
CachedDeploymentStatus, CachedStackStatus, History,
action_states, db_client, deployment_status_cache,
stack_status_cache,
},
};
use super::{CachedDeploymentStatus, CachedStackStatus, History};
fn deployment_alert_sent_cache() -> &'static Mutex<HashSet<String>> {
static CACHE: OnceLock<Mutex<HashSet<String>>> = OnceLock::new();
CACHE.get_or_init(Default::default)
}
pub async fn update_deployment_cache(
server_name: String,
deployments: Vec<Deployment>,
containers: &[ContainerListItem],
images: &[ImageListItem],
builds: &[Build],
) {
let deployment_status_cache = deployment_status_cache();
for deployment in deployments {
let container = containers
.iter()
.find(|container| container.name == deployment.name)
.cloned();
let prev = deployment_status_cache
.get(&deployment.id)
.await
.map(|s| s.curr.state);
let state = container
.as_ref()
.map(|c| c.state.into())
.unwrap_or(DeploymentState::NotDeployed);
let image = match deployment.config.image {
DeploymentImage::Build { build_id, version } => {
let (build_name, build_version) = builds
.iter()
.find(|build| build.id == build_id)
.map(|b| (b.name.as_ref(), b.config.version))
.unwrap_or(("Unknown", Default::default()));
let version = if version.is_none() {
build_version.to_string()
} else {
version.to_string()
};
format!("{build_name}:{version}")
}
DeploymentImage::Image { image } => {
// If image already has tag, leave it,
// otherwise default the tag to latest
if image.contains(':') {
image.to_string()
} else {
format!("{image}:latest")
}
}
};
let update_available = if let Some(ContainerListItem {
image_id: Some(curr_image_id),
..
}) = &container
{
// Docker will automatically strip `docker.io` from incoming image names re #468.
// Need to strip it in order to match by image name and find available updates.
let image = image.strip_prefix("docker.io/").unwrap_or(&image);
images
.iter()
.find(|i| i.name == image)
.map(|i| &i.id != curr_image_id)
.unwrap_or_default()
} else {
false
};
if update_available {
if deployment.config.auto_update {
if state == DeploymentState::Running
&& !action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await
.busy()
.unwrap_or(true)
{
let id = deployment.id.clone();
let server_name = server_name.clone();
tokio::spawn(async move {
match execute::inner_handler(
ExecuteRequest::Deploy(Deploy {
deployment: deployment.name.clone(),
stop_time: None,
stop_signal: None,
}),
auto_redeploy_user().to_owned(),
)
.await
{
Ok(_) => {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Deployment(id.clone()),
data: AlertData::DeploymentAutoUpdated {
id,
name: deployment.name,
server_name,
server_id: deployment.config.server_id,
image,
},
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!(
"Failed to record DeploymentAutoUpdated to db | {e:#}"
);
}
send_alerts(&[alert]).await;
}
Err(e) => {
warn!(
"Failed to auto update Deployment {} | {e:#}",
deployment.name
)
}
}
});
}
} else if state == DeploymentState::Running
&& deployment.config.send_alerts
&& !deployment_alert_sent_cache()
.lock()
.unwrap()
.contains(&deployment.id)
{
// Add that it is already sent to the cache, so another alert won't be sent.
deployment_alert_sent_cache()
.lock()
.unwrap()
.insert(deployment.id.clone());
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Deployment(deployment.id.clone()),
data: AlertData::DeploymentImageUpdateAvailable {
id: deployment.id.clone(),
name: deployment.name,
server_name: server_name.clone(),
server_id: deployment.config.server_id,
image,
},
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!(
"Failed to record DeploymentImageUpdateAvailable to db | {e:#}"
);
}
send_alerts(&[alert]).await;
}
} else {
// If it sees there is no longer update available, remove
// from the sent cache, so on next `update_available = true`
// the cache is empty and a fresh alert will be sent.
deployment_alert_sent_cache()
.lock()
.unwrap()
.remove(&deployment.id);
}
deployment_status_cache
.insert(
deployment.id.clone(),
History {
curr: CachedDeploymentStatus {
id: deployment.id,
state,
container,
update_available,
},
prev,
}
.into(),
)
.await;
}
}
/// (StackId, Service)
fn stack_alert_sent_cache()
-> &'static Mutex<HashSet<(String, String)>> {
@@ -425,3 +237,190 @@ pub async fn update_stack_cache(
.await;
}
}
fn deployment_alert_sent_cache() -> &'static Mutex<HashSet<String>> {
static CACHE: OnceLock<Mutex<HashSet<String>>> = OnceLock::new();
CACHE.get_or_init(Default::default)
}
pub async fn update_deployment_cache(
server_name: String,
deployments: Vec<Deployment>,
containers: &[ContainerListItem],
images: &[ImageListItem],
builds: &[Build],
) {
let deployment_status_cache = deployment_status_cache();
for deployment in deployments {
let container = containers
.iter()
.find(|container| container.name == deployment.name)
.cloned();
let prev = deployment_status_cache
.get(&deployment.id)
.await
.map(|s| s.curr.state);
let state = container
.as_ref()
.map(|c| c.state.into())
.unwrap_or(DeploymentState::NotDeployed);
let image = match deployment.config.image {
DeploymentImage::Build { build_id, version } => {
let (build_name, build_version) = builds
.iter()
.find(|build| build.id == build_id)
.map(|b| (b.name.as_ref(), b.config.version))
.unwrap_or(("Unknown", Default::default()));
let version = if version.is_none() {
build_version.to_string()
} else {
version.to_string()
};
format!("{build_name}:{version}")
}
DeploymentImage::Image { image } => {
// If image already has tag, leave it,
// otherwise default the tag to latest
if image.contains(':') {
image.to_string()
} else {
format!("{image}:latest")
}
}
};
let update_available = if let Some(ContainerListItem {
image_id: Some(curr_image_id),
..
}) = &container
{
// Docker will automatically strip `docker.io` from incoming image names re #468.
// Need to strip it in order to match by image name and find available updates.
let image = image.strip_prefix("docker.io/").unwrap_or(&image);
images
.iter()
.find(|i| i.name == image)
.map(|i| &i.id != curr_image_id)
.unwrap_or_default()
} else {
false
};
if update_available {
if deployment.config.auto_update {
if state == DeploymentState::Running
&& !action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await
.busy()
.unwrap_or(true)
{
let id = deployment.id.clone();
let server_name = server_name.clone();
tokio::spawn(async move {
match execute::inner_handler(
ExecuteRequest::Deploy(Deploy {
deployment: deployment.name.clone(),
stop_time: None,
stop_signal: None,
}),
auto_redeploy_user().to_owned(),
)
.await
{
Ok(_) => {
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Deployment(id.clone()),
data: AlertData::DeploymentAutoUpdated {
id,
name: deployment.name,
server_name,
server_id: deployment.config.server_id,
image,
},
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!(
"Failed to record DeploymentAutoUpdated to db | {e:#}"
);
}
send_alerts(&[alert]).await;
}
Err(e) => {
warn!(
"Failed to auto update Deployment {} | {e:#}",
deployment.name
)
}
}
});
}
} else if state == DeploymentState::Running
&& deployment.config.send_alerts
&& !deployment_alert_sent_cache()
.lock()
.unwrap()
.contains(&deployment.id)
{
// Add that it is already sent to the cache, so another alert won't be sent.
deployment_alert_sent_cache()
.lock()
.unwrap()
.insert(deployment.id.clone());
let ts = komodo_timestamp();
let alert = Alert {
id: Default::default(),
ts,
resolved: true,
resolved_ts: ts.into(),
level: SeverityLevel::Ok,
target: ResourceTarget::Deployment(deployment.id.clone()),
data: AlertData::DeploymentImageUpdateAvailable {
id: deployment.id.clone(),
name: deployment.name,
server_name: server_name.clone(),
server_id: deployment.config.server_id,
image,
},
};
let res = db_client().alerts.insert_one(&alert).await;
if let Err(e) = res {
error!(
"Failed to record DeploymentImageUpdateAvailable to db | {e:#}"
);
}
send_alerts(&[alert]).await;
}
} else {
// If it sees there is no longer update available, remove
// from the sent cache, so on next `update_available = true`
// the cache is empty and a fresh alert will be sent.
deployment_alert_sent_cache()
.lock()
.unwrap()
.remove(&deployment.id);
}
deployment_status_cache
.insert(
deployment.id.clone(),
History {
curr: CachedDeploymentStatus {
id: deployment.id,
state,
container,
update_available,
},
prev,
}
.into(),
)
.await;
}
}

View File

@@ -0,0 +1,186 @@
use std::{
sync::{Arc, OnceLock},
time::Duration,
};
use anyhow::anyhow;
use async_timing_util::wait_until_timelength;
use cache::CloneCache;
use database::mungos::find::find_collect;
use futures_util::future::join_all;
use komodo_client::entities::{
docker::node::NodeState,
komodo_timestamp,
server::Server,
swarm::{Swarm, SwarmState},
};
use periphery_client::api::swarm::{
PollSwarmStatus, PollSwarmStatusResponse,
};
use tokio::sync::Mutex;
use crate::{
helpers::periphery_client,
resource,
state::{CachedSwarmStatus, db_client, swarm_status_cache},
};
const ADDITIONAL_MS: u128 = 1000;
pub fn spawn_swarm_monitoring_loop(
interval: async_timing_util::Timelength,
) {
tokio::spawn(async move {
refresh_swarm_cache(komodo_timestamp()).await;
loop {
let ts = (wait_until_timelength(interval, ADDITIONAL_MS).await
- ADDITIONAL_MS) as i64;
refresh_swarm_cache(ts).await;
}
});
}
async fn refresh_swarm_cache(_ts: i64) {
let swarms =
match find_collect(&db_client().swarms, None, None).await {
Ok(swarms) => swarms,
Err(e) => {
error!(
"Failed to get swarm list (refresh swarm cache) | {e:#}"
);
return;
}
};
let futures = swarms.into_iter().map(|swarm| async move {
update_cache_for_swarm(&swarm, false).await;
});
join_all(futures).await;
// tokio::join!(check_alerts(ts), record_swarm_stats(ts));
}
/// Makes sure cache for swarm doesn't update too frequently / simultaneously.
/// If forced, will still block against simultaneous update.
fn update_cache_for_swarm_controller()
-> &'static CloneCache<String, Arc<Mutex<i64>>> {
static CACHE: OnceLock<CloneCache<String, Arc<Mutex<i64>>>> =
OnceLock::new();
CACHE.get_or_init(Default::default)
}
/// The background loop will call this with force: false,
/// which exits early if the lock is busy or it was completed too recently.
/// If force is true, it will wait on simultaneous calls, and will
/// ignore the restriction on being completed too recently.
pub async fn update_cache_for_swarm(swarm: &Swarm, force: bool) {
// Concurrency controller to ensure it isn't done too often
// when it happens in other contexts.
let controller = update_cache_for_swarm_controller()
.get_or_insert_default(&swarm.id)
.await;
let mut lock = match controller.try_lock() {
Ok(lock) => lock,
Err(_) if force => controller.lock().await,
Err(_) => return,
};
let now = komodo_timestamp();
// early return if called again sooner than 1s.
if !force && *lock > now - 1_000 {
return;
}
*lock = now;
if swarm.config.server_ids.is_empty() {
swarm_status_cache()
.insert(
swarm.id.clone(),
CachedSwarmStatus {
state: SwarmState::Unknown,
inspect: None,
lists: None,
err: Some(
anyhow!("No Servers configured as manager nodes").into(),
),
}
.into(),
)
.await;
return;
}
let PollSwarmStatusResponse { inspect, lists } =
match poll_swarm_inspect_info(&swarm.config.server_ids).await {
Ok(info) => info,
Err(e) => {
swarm_status_cache()
.insert(
swarm.id.clone(),
CachedSwarmStatus {
state: SwarmState::Unknown,
inspect: None,
lists: None,
err: Some(e.into()),
}
.into(),
)
.await;
return;
}
};
let mut state = SwarmState::Healthy;
for node in &lists.nodes {
let node_state = node
.status
.as_ref()
.and_then(|status| status.state)
.unwrap_or_default();
if !matches!(node_state, NodeState::READY) {
state = SwarmState::Unhealthy;
}
}
swarm_status_cache()
.insert(
swarm.id.clone(),
CachedSwarmStatus {
state,
inspect,
lists: Some(lists),
err: None,
}
.into(),
)
.await;
}
async fn poll_swarm_inspect_info(
servers: &[String],
) -> anyhow::Result<PollSwarmStatusResponse> {
let mut err = Option::<anyhow::Error>::None;
for server in servers {
match poll_swarm_inspect_info_from_server(server).await {
Ok(res) => return Ok(res),
Err(e) => err = Some(e),
}
}
Err(err.unwrap_or_else(|| {
anyhow!("Failed to poll swarm inspect info with unknown error")
}))
}
async fn poll_swarm_inspect_info_from_server(
server: &str,
) -> anyhow::Result<PollSwarmStatusResponse> {
let server = resource::get::<Server>(server).await?;
let periphery = periphery_client(&server).await?;
periphery
.request_custom_timeout(
PollSwarmStatus {},
Duration::from_secs(1),
)
.await
}

View File

@@ -12,7 +12,6 @@ use uuid::Uuid;
use crate::{
connection::{
PeripheryConnection, PeripheryConnectionArgs, ResponseChannels,
TerminalChannels,
},
state::periphery_connections,
};
@@ -24,7 +23,6 @@ pub struct PeripheryClient {
/// Usually the server id
pub id: String,
pub responses: Arc<ResponseChannels>,
pub terminals: Arc<TerminalChannels>,
}
impl PeripheryClient {
@@ -51,7 +49,6 @@ impl PeripheryClient {
return Ok(PeripheryClient {
id,
responses: connection.responses.clone(),
terminals: connection.terminals.clone(),
});
}
@@ -68,7 +65,6 @@ impl PeripheryClient {
Ok(PeripheryClient {
id,
responses: connection.responses.clone(),
terminals: connection.terminals.clone(),
})
} else {
// Core -> Periphery connection
@@ -89,6 +85,20 @@ impl PeripheryClient {
&self,
request: T,
) -> anyhow::Result<T::Response>
where
T: std::fmt::Debug + Serialize + HasResponse,
T::Response: DeserializeOwned,
{
self
.request_custom_timeout(request, Duration::from_secs(10))
.await
}
pub async fn request_custom_timeout<T>(
&self,
request: T,
timeout: Duration,
) -> anyhow::Result<T::Response>
where
T: std::fmt::Debug + Serialize + HasResponse,
T::Response: DeserializeOwned,
@@ -117,24 +127,28 @@ impl PeripheryClient {
.await
.context("Failed to send request over channel")
{
// cleanup
self.terminals.remove(&channel_id).await;
self.responses.remove(&channel_id).await;
return Err(e);
}
// Poll for the associated response
loop {
let message = response_receiever
.recv()
.with_timeout(Duration::from_secs(10))
.await?;
let res = async {
// Poll for the associated response
loop {
let message =
response_receiever.recv().with_timeout(timeout).await?;
// Still in progress, sent to avoid timeout.
let Some(message) = message.decode()? else {
continue;
};
// Still in progress, sent to avoid timeout.
let Some(message) = message.decode()? else {
continue;
};
return message.decode();
return message.decode();
}
}
.await;
self.responses.remove(&channel_id).await;
res
}
}

View File

@@ -147,8 +147,9 @@ impl super::KomodoResource for Stack {
.await
.as_ref()
{
if let Some(projects) = &status.projects {
if let Some(project) = projects
if let Some(docker) = &status.docker {
if let Some(project) = docker
.projects
.iter()
.find(|project| project.name == project_name)
{

View File

@@ -8,13 +8,16 @@ use komodo_client::entities::{
swarm::{
PartialSwarmConfig, Swarm, SwarmConfig, SwarmConfigDiff,
SwarmInfo, SwarmListItem, SwarmListItemInfo, SwarmQuerySpecifics,
SwarmState,
},
update::Update,
user::User,
};
use crate::{config::core_config, state::db_client};
use crate::{
config::core_config,
monitor::update_cache_for_swarm,
state::{db_client, swarm_status_cache},
};
use super::get_check_permissions;
@@ -42,6 +45,11 @@ impl super::KomodoResource for Swarm {
async fn to_list_item(
swarm: Resource<Self::Config, Self::Info>,
) -> Self::ListItem {
let state = swarm_status_cache()
.get(&swarm.id)
.await
.map(|status| status.state)
.unwrap_or_default();
SwarmListItem {
name: swarm.name,
id: swarm.id,
@@ -50,18 +58,12 @@ impl super::KomodoResource for Swarm {
resource_type: ResourceTargetVariant::Swarm,
info: SwarmListItemInfo {
server_ids: swarm.config.server_ids,
state: SwarmState::Unknown,
state,
},
}
}
async fn busy(id: &String) -> anyhow::Result<bool> {
// action_states()
// .swarm
// .get(id)
// .await
// .unwrap_or_default()
// .busy()
async fn busy(_id: &String) -> anyhow::Result<bool> {
Ok(false)
}
@@ -83,10 +85,10 @@ impl super::KomodoResource for Swarm {
}
async fn post_create(
_created: &Resource<Self::Config, Self::Info>,
created: &Self,
_update: &mut Update,
) -> anyhow::Result<()> {
// refresh_swarm_state_cache().await;
update_cache_for_swarm(created, true).await;
Ok(())
}
@@ -105,10 +107,10 @@ impl super::KomodoResource for Swarm {
}
async fn post_update(
_updated: &Self,
updated: &Self,
_update: &mut Update,
) -> anyhow::Result<()> {
// refresh_swarm_state_cache().await;
update_cache_for_swarm(updated, true).await;
Ok(())
}
@@ -125,18 +127,17 @@ impl super::KomodoResource for Swarm {
}
async fn pre_delete(
_swarm: &Resource<Self::Config, Self::Info>,
_swarm: &Self,
_update: &mut Update,
) -> anyhow::Result<()> {
Ok(())
}
async fn post_delete(
_swarm: &Resource<Self::Config, Self::Info>,
swarm: &Self,
_update: &mut Update,
) -> anyhow::Result<()> {
// swarm_state_cache().remove(&swarm.id).await;
swarm_status_cache().remove(&swarm.id).await;
Ok(())
}
}

View File

@@ -4,9 +4,19 @@ use anyhow::Context;
use arc_swap::ArcSwap;
use cache::CloneCache;
use komodo_client::entities::{
action::ActionState, build::BuildState,
deployment::DeploymentState, procedure::ProcedureState,
repo::RepoState, stack::StackState,
action::ActionState,
build::BuildState,
deployment::DeploymentState,
docker::{
DockerLists, SwarmLists, container::ContainerListItem,
swarm::SwarmInspectInfo,
},
procedure::ProcedureState,
repo::RepoState,
server::{PeripheryInformation, ServerHealth, ServerState},
stack::{StackService, StackState},
stats::{SystemInformation, SystemStats},
swarm::SwarmState,
};
use crate::{
@@ -16,10 +26,6 @@ use crate::{
helpers::{
action_state::ActionStates, all_resources::AllResourcesById,
},
monitor::{
CachedDeploymentStatus, CachedRepoStatus, CachedServerStatus,
CachedStackStatus, History,
},
};
static DB_CLIENT: OnceLock<database::Client> = OnceLock::new();
@@ -64,6 +70,43 @@ pub fn action_states() -> &'static ActionStates {
ACTION_STATES.get_or_init(ActionStates::default)
}
#[derive(Default, Debug)]
pub struct History<Curr: Default, Prev> {
pub curr: Curr,
pub prev: Option<Prev>,
}
#[derive(Default, Clone, Debug)]
pub struct CachedSwarmStatus {
pub state: SwarmState,
pub inspect: Option<SwarmInspectInfo>,
pub lists: Option<SwarmLists>,
/// Store the error in communicating with Swarm
pub err: Option<serror::Serror>,
}
pub type SwarmStatusCache =
CloneCache<String, Arc<CachedSwarmStatus>>;
pub fn swarm_status_cache() -> &'static SwarmStatusCache {
static SWARM_STATUS_CACHE: OnceLock<SwarmStatusCache> =
OnceLock::new();
SWARM_STATUS_CACHE.get_or_init(Default::default)
}
#[derive(Default, Clone, Debug)]
pub struct CachedServerStatus {
pub id: String,
pub state: ServerState,
pub health: Option<ServerHealth>,
pub periphery_info: Option<PeripheryInformation>,
pub system_info: Option<SystemInformation>,
pub system_stats: Option<SystemStats>,
pub docker: Option<DockerLists>,
/// Store the error in reaching periphery
pub err: Option<serror::Serror>,
}
pub type ServerStatusCache =
CloneCache<String, Arc<CachedServerStatus>>;
@@ -73,6 +116,16 @@ pub fn server_status_cache() -> &'static ServerStatusCache {
SERVER_STATUS_CACHE.get_or_init(Default::default)
}
#[derive(Default, Clone, Debug)]
pub struct CachedStackStatus {
/// The stack id
pub id: String,
/// The stack state
pub state: StackState,
/// The services connected to the stack
pub services: Vec<StackService>,
}
pub type StackStatusCache =
CloneCache<String, Arc<History<CachedStackStatus, StackState>>>;
@@ -82,6 +135,15 @@ pub fn stack_status_cache() -> &'static StackStatusCache {
STACK_STATUS_CACHE.get_or_init(Default::default)
}
#[derive(Default, Clone, Debug)]
pub struct CachedDeploymentStatus {
/// The deployment id
pub id: String,
pub state: DeploymentState,
pub container: Option<ContainerListItem>,
pub update_available: bool,
}
/// Cache of ids to status
pub type DeploymentStatusCache = CloneCache<
String,
@@ -103,6 +165,12 @@ pub fn build_state_cache() -> &'static BuildStateCache {
BUILD_STATE_CACHE.get_or_init(Default::default)
}
#[derive(Default, Clone, Debug)]
pub struct CachedRepoStatus {
pub latest_hash: Option<String>,
pub latest_message: Option<String>,
}
pub type RepoStatusCache = CloneCache<String, Arc<CachedRepoStatus>>;
pub fn repo_status_cache() -> &'static RepoStatusCache {

View File

@@ -1,28 +1,20 @@
use command::run_komodo_standard_command;
use derive_variants::EnumVariants;
use encoding::{EncodedJsonMessage, EncodedResponse};
use futures_util::FutureExt;
use komodo_client::entities::{
config::{DockerRegistry, GitProvider},
server::PeripheryInformation,
stats::SystemProcess,
update::Log,
};
use periphery_client::api::{
build::*, compose::*, container::*, docker::*, git::*, keys::*,
stats::*, swarm::*, terminal::*, *,
poll::*, stats::*, swarm::*, terminal::*, *,
};
use resolver_api::Resolve;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
api::compose::list_compose_projects,
config::periphery_config,
state::{
docker_client, host_public_ip, periphery_keys, stats_client,
},
};
use crate::{config::periphery_config, state::stats_client};
pub mod terminal;
@@ -33,6 +25,7 @@ mod deploy;
mod docker;
mod git;
mod keys;
mod poll;
mod swarm;
#[derive(Debug)]
@@ -144,6 +137,7 @@ pub enum PeripheryRequest {
PruneSystem(PruneSystem),
// Swarm
GetSwarmLists(PollSwarmStatus),
InspectSwarmNode(InspectSwarmNode),
InspectSwarmService(InspectSwarmService),
InspectSwarmTask(InspectSwarmTask),
@@ -191,78 +185,6 @@ impl Resolve<Args> for GetVersion {
//
impl Resolve<Args> for PollStatus {
async fn resolve(
self,
_: &Args,
) -> anyhow::Result<PollStatusResponse> {
// Docker lists
let docker_lists = async {
let client = docker_client().load();
let Some(client) = client.iter().next() else {
return Default::default();
};
let containers =
client.list_containers().await.unwrap_or_default();
// Todo: handle errors better
(
tokio::join!(
client
.list_networks(&containers)
.map(Result::unwrap_or_default),
client
.list_images(&containers)
.map(Result::unwrap_or_default),
client
.list_volumes(&containers)
.map(Result::unwrap_or_default)
),
containers,
)
};
let (
((networks, images, volumes), containers),
projects,
stats_client,
) = tokio::join!(
docker_lists,
list_compose_projects().map(Result::unwrap_or_default),
stats_client().read(),
);
let system_stats = if self.include_stats {
Some(stats_client.stats.clone())
} else {
None
};
let config = periphery_config();
Ok(PollStatusResponse {
periphery_info: PeripheryInformation {
version: env!("CARGO_PKG_VERSION").to_string(),
public_key: periphery_keys().load().public.to_string(),
terminals_disabled: config.disable_terminals,
container_terminals_disabled: config
.disable_container_terminals,
stats_polling_rate: config.stats_polling_rate,
docker_connected: docker_client().load().is_some(),
public_ip: host_public_ip().await.cloned(),
},
system_info: stats_client.info.clone(),
system_stats,
containers,
networks,
images,
volumes,
projects,
})
}
}
//
impl Resolve<Args> for GetSystemProcesses {
async fn resolve(
self,

View File

@@ -0,0 +1,84 @@
use futures_util::FutureExt;
use komodo_client::entities::{
docker::DockerLists, server::PeripheryInformation,
};
use periphery_client::api::poll::{PollStatus, PollStatusResponse};
use resolver_api::Resolve;
use crate::{
api::compose::list_compose_projects,
config::periphery_config,
docker::DockerClient,
state::{
docker_client, host_public_ip, periphery_keys, stats_client,
},
};
impl Resolve<super::Args> for PollStatus {
async fn resolve(
self,
_: &super::Args,
) -> anyhow::Result<PollStatusResponse> {
let stats_client = stats_client().read().await;
let system_stats = if self.include_stats {
Some(stats_client.stats.clone())
} else {
None
};
let docker = if self.include_docker {
let client = docker_client().load();
if let Some(client) = client.iter().next() {
Some(docker_lists(client).await)
} else {
None
}
} else {
None
};
Ok(PollStatusResponse {
periphery_info: periphery_information().await,
system_info: stats_client.info.clone(),
system_stats,
docker,
})
}
}
async fn periphery_information() -> PeripheryInformation {
let config = periphery_config();
PeripheryInformation {
version: env!("CARGO_PKG_VERSION").to_string(),
public_key: periphery_keys().load().public.to_string(),
terminals_disabled: config.disable_terminals,
container_terminals_disabled: config.disable_container_terminals,
stats_polling_rate: config.stats_polling_rate,
docker_connected: docker_client().load().is_some(),
public_ip: host_public_ip().await.cloned(),
}
}
async fn docker_lists(client: &DockerClient) -> DockerLists {
let containers = client.list_containers().await.unwrap_or_default();
let (networks, images, volumes, projects) = tokio::join!(
client
.list_networks(&containers)
.map(Result::unwrap_or_default),
client
.list_images(&containers)
.map(Result::unwrap_or_default),
client
.list_volumes(&containers)
.map(Result::unwrap_or_default),
list_compose_projects().map(Result::unwrap_or_default),
);
DockerLists {
containers,
networks,
images,
volumes,
projects,
}
}

View File

@@ -1,16 +1,45 @@
use anyhow::Context as _;
use komodo_client::entities::docker::{
node::SwarmNode, secret::SwarmSecret, service::SwarmService,
task::SwarmTask,
SwarmLists, node::SwarmNode, secret::SwarmSecret,
service::SwarmService, task::SwarmTask,
};
use periphery_client::api::swarm::{
InspectSwarmNode, InspectSwarmSecret, InspectSwarmService,
InspectSwarmTask,
InspectSwarmTask, PollSwarmStatus, PollSwarmStatusResponse,
};
use resolver_api::Resolve;
use crate::state::docker_client;
impl Resolve<super::Args> for PollSwarmStatus {
async fn resolve(
self,
_: &super::Args,
) -> anyhow::Result<PollSwarmStatusResponse> {
let client = docker_client().load();
let client = client
.iter()
.next()
.context("Could not connect to docker client")?;
let (inspect, nodes, services, tasks, secrets) = tokio::join!(
client.inspect_swarm(),
client.list_swarm_nodes(),
client.list_swarm_services(),
client.list_swarm_tasks(),
client.list_swarm_secrets(),
);
Ok(PollSwarmStatusResponse {
inspect: inspect.ok(),
lists: SwarmLists {
nodes: nodes.unwrap_or_default(),
services: services.unwrap_or_default(),
tasks: tasks.unwrap_or_default(),
secrets: secrets.unwrap_or_default(),
},
})
}
}
// ======
// Node
// ======

View File

@@ -15,6 +15,7 @@ mod network;
mod node;
mod secret;
mod service;
mod swarm;
mod task;
mod volume;
@@ -463,3 +464,11 @@ fn convert_task_spec_container_spec(
ulimits: spec.ulimits.map(|ulimits| ulimits.into_iter().map(convert_resources_ulimits).collect()),
}
}
fn convert_tls_info(tls_info: bollard::models::TlsInfo) -> TlsInfo {
TlsInfo {
trust_root: tls_info.trust_root,
cert_issuer_subject: tls_info.cert_issuer_subject,
cert_issuer_public_key: tls_info.cert_issuer_public_key,
}
}

View File

@@ -8,7 +8,9 @@ use super::{
impl DockerClient {
/// Lists swarm nodes
pub async fn list_swarm_nodes(&self) -> anyhow::Result<Vec<SwarmNode>> {
pub async fn list_swarm_nodes(
&self,
) -> anyhow::Result<Vec<SwarmNode>> {
let nodes = self
.docker
.list_nodes(Option::<ListNodesOptions>::None)
@@ -93,11 +95,7 @@ fn convert_node(node: bollard::models::Node) -> SwarmNode {
.collect()
}),
}),
tls_info: description.tls_info.map(|tls_info| TlsInfo {
trust_root: tls_info.trust_root,
cert_issuer_subject: tls_info.cert_issuer_subject,
cert_issuer_public_key: tls_info.cert_issuer_public_key,
}),
tls_info: description.tls_info.map(super::convert_tls_info),
}
}),
status: node.status.map(|status| NodeStatus {

View File

@@ -0,0 +1,96 @@
use anyhow::Context;
use komodo_client::entities::docker::swarm::{
JoinTokens, SwarmInspectInfo, SwarmSpec, SwarmSpecCaConfig,
SwarmSpecCaConfigExternalCas,
SwarmSpecCaConfigExternalCasProtocolEnum, SwarmSpecDispatcher,
SwarmSpecEncryptionConfig, SwarmSpecOrchestration, SwarmSpecRaft,
SwarmSpecTaskDefaults, SwarmSpecTaskDefaultsLogDriver,
};
use super::DockerClient;
impl DockerClient {
/// Inspect swarm info
pub async fn inspect_swarm(
&self,
) -> anyhow::Result<SwarmInspectInfo> {
self
.docker
.inspect_swarm()
.await
.map(convert_swarm_info)
.context("Failed to query for swarm info")
}
}
fn convert_swarm_info(
swarm: bollard::models::Swarm,
) -> SwarmInspectInfo {
SwarmInspectInfo {
id: swarm.id,
version: swarm.version.map(super::convert_object_version),
created_at: swarm.created_at,
updated_at: swarm.updated_at,
spec: swarm.spec.map(|spec| SwarmSpec {
name: spec.name,
labels: spec.labels,
orchestration: spec.orchestration.map(|orchestration| {
SwarmSpecOrchestration {
task_history_retention_limit: orchestration
.task_history_retention_limit,
}
}),
raft: spec.raft.map(|raft| SwarmSpecRaft {
snapshot_interval: raft.snapshot_interval,
keep_old_snapshots: raft.keep_old_snapshots,
log_entries_for_slow_followers: raft
.log_entries_for_slow_followers,
election_tick: raft.election_tick,
heartbeat_tick: raft.heartbeat_tick,
}),
dispatcher: spec.dispatcher.map(|dispatcher| {
SwarmSpecDispatcher {
heartbeat_period: dispatcher.heartbeat_period,
}
}),
ca_config: spec.ca_config.map(|config| SwarmSpecCaConfig {
node_cert_expiry: config.node_cert_expiry,
external_cas: config.external_cas.map(|cas| {
cas
.into_iter()
.map(|cas| SwarmSpecCaConfigExternalCas {
protocol: cas.protocol.map(|protocol| match protocol {
bollard::secret::SwarmSpecCaConfigExternalCasProtocolEnum::EMPTY => SwarmSpecCaConfigExternalCasProtocolEnum::EMPTY,
bollard::secret::SwarmSpecCaConfigExternalCasProtocolEnum::CFSSL => SwarmSpecCaConfigExternalCasProtocolEnum::CFSSL,
}),
url: cas.url,
options: cas.options,
ca_cert: cas.ca_cert,
})
.collect()
}),
signing_ca_cert: config.signing_ca_cert,
signing_ca_key: config.signing_ca_key,
force_rotate: config.force_rotate,
}),
encryption_config: spec.encryption_config.map(|config| SwarmSpecEncryptionConfig {
auto_lock_managers: config.auto_lock_managers,
}),
task_defaults: spec.task_defaults.map(|defaults| SwarmSpecTaskDefaults {
log_driver: defaults.log_driver.map(|driver| SwarmSpecTaskDefaultsLogDriver {
name: driver.name,
options: driver.options,
}),
}),
}),
tls_info: swarm.tls_info.map(super::convert_tls_info),
root_rotation_in_progress: swarm.root_rotation_in_progress,
data_path_port: swarm.data_path_port,
default_addr_pool: swarm.default_addr_pool,
subnet_size: swarm.subnet_size,
join_tokens: swarm.join_tokens.map(|tokens| JoinTokens {
worker: tokens.worker,
manager: tokens.manager,
}),
}
}

View File

@@ -108,6 +108,6 @@ pub struct GetSwarmsSummaryResponse {
pub healthy: u32,
/// The number of Swarms with Unhealthy state
pub unhealthy: u32,
/// The number of Swarms with Offline state
pub offline: u32,
/// The number of Swarms with Unknown state
pub unknown: u32,
}

View File

@@ -2,13 +2,20 @@ use crate::entities::{
action::ActionActionState, build::BuildActionState,
deployment::DeploymentActionState, procedure::ProcedureActionState,
repo::RepoActionState, server::ServerActionState,
stack::StackActionState, sync::ResourceSyncActionState,
stack::StackActionState, swarm::SwarmActionState,
sync::ResourceSyncActionState,
};
pub trait Busy {
fn busy(&self) -> bool;
}
impl Busy for SwarmActionState {
fn busy(&self) -> bool {
false
}
}
impl Busy for ServerActionState {
fn busy(&self) -> bool {
self.pruning_containers

View File

@@ -3,6 +3,15 @@ use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{
docker::{
container::ContainerListItem, image::ImageListItem,
network::NetworkListItem, node::SwarmNode, secret::SwarmSecret,
service::SwarmService, task::SwarmTask, volume::VolumeListItem,
},
stack::ComposeProject,
};
use super::{I64, U64};
pub mod container;
@@ -12,9 +21,32 @@ pub mod node;
pub mod secret;
pub mod service;
pub mod stats;
pub mod swarm;
pub mod task;
pub mod volume;
/// Swarm lists available from a manager node.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SwarmLists {
pub nodes: Vec<SwarmNode>,
pub services: Vec<SwarmService>,
pub tasks: Vec<SwarmTask>,
pub secrets: Vec<SwarmSecret>,
}
/// Standard docker lists available from a Server.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct DockerLists {
pub containers: Vec<ContainerListItem>,
pub networks: Vec<NetworkListItem>,
pub images: Vec<ImageListItem>,
pub volumes: Vec<VolumeListItem>,
pub projects: Vec<ComposeProject>,
}
/// PortBinding represents a binding between a host IP address and a host port.
#[typeshare]
#[derive(
@@ -565,3 +597,22 @@ pub enum EndpointPortConfigPublishModeEnum {
#[serde(rename = "host")]
HOST,
}
/// Information about the issuer of leaf TLS certificates and the trusted root CA certificate.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct TlsInfo {
/// The root CA certificate(s) that are used to validate leaf TLS certificates.
#[serde(rename = "TrustRoot")]
pub trust_root: Option<String>,
/// The base64-url-safe-encoded raw subject bytes of the issuer.
#[serde(rename = "CertIssuerSubject")]
pub cert_issuer_subject: Option<String>,
/// The base64-url-safe-encoded raw public key bytes of the issuer.
#[serde(rename = "CertIssuerPublicKey")]
pub cert_issuer_public_key: Option<String>,
}

View File

@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use typeshare::typeshare;
#[typeshare]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NetworkListItem {
pub name: Option<String>,
pub id: Option<String>,

View File

@@ -5,7 +5,7 @@ use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use super::{ObjectVersion, Platform, ResourceObject};
use super::*;
/// Swarm node details.
#[typeshare]
@@ -159,25 +159,6 @@ pub struct EngineDescriptionPlugins {
pub name: Option<String>,
}
/// Information about the issuer of leaf TLS certificates and the trusted root CA certificate.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct TlsInfo {
/// The root CA certificate(s) that are used to validate leaf TLS certificates.
#[serde(rename = "TrustRoot")]
pub trust_root: Option<String>,
/// The base64-url-safe-encoded raw subject bytes of the issuer.
#[serde(rename = "CertIssuerSubject")]
pub cert_issuer_subject: Option<String>,
/// The base64-url-safe-encoded raw public key bytes of the issuer.
#[serde(rename = "CertIssuerPublicKey")]
pub cert_issuer_public_key: Option<String>,
}
/// NodeStatus represents the status of a node. It provides the current status of the node, as seen by the manager.
#[typeshare]
#[derive(

View File

@@ -3,7 +3,7 @@ use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use super::{Driver, ObjectVersion};
use super::*;
/// Swarm secret details.
#[typeshare]

View File

@@ -0,0 +1,257 @@
use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use super::*;
/// Docker-level information about the Swarm.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmInspectInfo {
/// The (Docker) ID of the swarm.
#[serde(rename = "ID")]
pub id: Option<String>,
#[serde(rename = "Version")]
pub version: Option<ObjectVersion>,
/// Date and time at which the swarm was initialised in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
#[serde(rename = "CreatedAt")]
pub created_at: Option<String>,
/// Date and time at which the swarm was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds.
#[serde(rename = "UpdatedAt")]
pub updated_at: Option<String>,
#[serde(rename = "Spec")]
pub spec: Option<SwarmSpec>,
#[serde(rename = "TLSInfo")]
pub tls_info: Option<TlsInfo>,
/// Whether there is currently a root CA rotation in progress for the swarm
#[serde(rename = "RootRotationInProgress")]
pub root_rotation_in_progress: Option<bool>,
/// DataPathPort specifies the data path port number for data traffic. Acceptable port range is 1024 to 49151. If no port is set or is set to 0, the default port (4789) is used.
#[serde(rename = "DataPathPort")]
pub data_path_port: Option<u32>,
/// Default Address Pool specifies default subnet pools for global scope networks.
#[serde(rename = "DefaultAddrPool")]
pub default_addr_pool: Option<Vec<String>>,
/// SubnetSize specifies the subnet size of the networks created from the default subnet pool.
#[serde(rename = "SubnetSize")]
pub subnet_size: Option<u32>,
#[serde(rename = "JoinTokens")]
pub join_tokens: Option<JoinTokens>,
}
/// User modifiable swarm configuration.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpec {
/// Name of the swarm.
#[serde(rename = "Name")]
pub name: Option<String>,
/// User-defined key/value metadata.
#[serde(rename = "Labels")]
pub labels: Option<HashMap<String, String>>,
#[serde(rename = "Orchestration")]
pub orchestration: Option<SwarmSpecOrchestration>,
#[serde(rename = "Raft")]
pub raft: Option<SwarmSpecRaft>,
#[serde(rename = "Dispatcher")]
pub dispatcher: Option<SwarmSpecDispatcher>,
#[serde(rename = "CAConfig")]
pub ca_config: Option<SwarmSpecCaConfig>,
#[serde(rename = "EncryptionConfig")]
pub encryption_config: Option<SwarmSpecEncryptionConfig>,
#[serde(rename = "TaskDefaults")]
pub task_defaults: Option<SwarmSpecTaskDefaults>,
}
/// Orchestration configuration.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecOrchestration {
/// The number of historic tasks to keep per instance or node.
/// If negative, never remove completed or failed tasks.
#[serde(rename = "TaskHistoryRetentionLimit")]
pub task_history_retention_limit: Option<I64>,
}
/// Raft configuration.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecRaft {
/// The number of log entries between snapshots.
#[serde(rename = "SnapshotInterval")]
pub snapshot_interval: Option<U64>,
/// The number of snapshots to keep beyond the current snapshot.
#[serde(rename = "KeepOldSnapshots")]
pub keep_old_snapshots: Option<U64>,
/// The number of log entries to keep around to sync up slow followers after a snapshot is created.
#[serde(rename = "LogEntriesForSlowFollowers")]
pub log_entries_for_slow_followers: Option<U64>,
/// The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed.
#[serde(rename = "ElectionTick")]
pub election_tick: Option<I64>,
/// The number of ticks between heartbeats.
/// Every HeartbeatTick ticks, the leader will send a heartbeat to the followers.
/// A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed.
#[serde(rename = "HeartbeatTick")]
pub heartbeat_tick: Option<I64>,
}
/// Dispatcher configuration.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecDispatcher {
/// The delay for an agent to send a heartbeat to the dispatcher.
#[serde(rename = "HeartbeatPeriod")]
pub heartbeat_period: Option<I64>,
}
/// CA configuration.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecCaConfig {
/// The duration node certificates are issued for.
#[serde(rename = "NodeCertExpiry")]
pub node_cert_expiry: Option<I64>,
/// Configuration for forwarding signing requests to an external certificate authority.
#[serde(rename = "ExternalCAs")]
pub external_cas: Option<Vec<SwarmSpecCaConfigExternalCas>>,
/// The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format.
#[serde(rename = "SigningCACert")]
pub signing_ca_cert: Option<String>,
/// The desired signing CA key for all swarm node TLS leaf certificates, in PEM format.
#[serde(rename = "SigningCAKey")]
pub signing_ca_key: Option<String>,
/// An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey`
#[serde(rename = "ForceRotate")]
pub force_rotate: Option<U64>,
}
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecCaConfigExternalCas {
/// Protocol for communication with the external CA (currently only `cfssl` is supported).
#[serde(rename = "Protocol")]
pub protocol: Option<SwarmSpecCaConfigExternalCasProtocolEnum>,
/// URL where certificate signing requests should be sent.
#[serde(rename = "URL")]
pub url: Option<String>,
/// An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver.
#[serde(rename = "Options")]
pub options: Option<HashMap<String, String>>,
/// The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided).
#[serde(rename = "CACert")]
pub ca_cert: Option<String>,
}
#[typeshare]
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
)]
pub enum SwarmSpecCaConfigExternalCasProtocolEnum {
#[default]
#[serde(rename = "")]
EMPTY,
#[serde(rename = "cfssl")]
CFSSL,
}
/// Parameters related to encryption-at-rest.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecEncryptionConfig {
/// If set, generate a key and use it to lock data stored on the managers.
#[serde(rename = "AutoLockManagers")]
pub auto_lock_managers: Option<bool>,
}
/// Defaults for creating tasks in this cluster.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecTaskDefaults {
#[serde(rename = "LogDriver")]
pub log_driver: Option<SwarmSpecTaskDefaultsLogDriver>,
}
/// The log driver to use for tasks created in the orchestrator if unspecified by a service. Updating this value only affects new tasks. Existing tasks continue to use their previously configured log driver until recreated.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct SwarmSpecTaskDefaultsLogDriver {
/// The log driver to use as a default for new tasks.
#[serde(rename = "Name")]
pub name: Option<String>,
/// Driver-specific options for the selected log driver, specified as key/value pairs.
#[serde(rename = "Options")]
pub options: Option<HashMap<String, String>>,
}
/// JoinTokens contains the tokens workers and managers need to join the swarm.
#[typeshare]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct JoinTokens {
/// The token workers can use to join the swarm.
#[serde(rename = "Worker")]
pub worker: Option<String>,
/// The token managers can use to join the swarm.
#[serde(rename = "Manager")]
pub manager: Option<String>,
}

View File

@@ -655,7 +655,9 @@ impl Default for StackConfig {
}
#[typeshare]
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[derive(
Debug, Clone, Default, PartialEq, Serialize, Deserialize,
)]
pub struct ComposeProject {
/// The compose project name.
pub name: String,

View File

@@ -6,6 +6,10 @@ use serde::{Deserialize, Serialize};
use strum::Display;
use typeshare::typeshare;
use crate::deserializers::{
option_string_list_deserializer, string_list_deserializer,
};
use super::resource::{Resource, ResourceListItem, ResourceQuery};
#[typeshare]
@@ -32,8 +36,6 @@ pub enum SwarmState {
Healthy,
/// The Swarm is unhealthy
Unhealthy,
/// Servers are reachable, but Swarm is not running on any of them.
Offline,
}
#[typeshare]
@@ -58,12 +60,22 @@ pub struct SwarmConfig {
#[partial_attr(serde(alias = "servers"))]
#[builder(default)]
pub server_ids: Vec<String>,
/// Configure quick links that are displayed in the resource header
#[serde(default, deserialize_with = "string_list_deserializer")]
#[partial_attr(serde(
default,
deserialize_with = "option_string_list_deserializer"
))]
#[builder(default)]
pub links: Vec<String>,
}
impl Default for SwarmConfig {
fn default() -> Self {
Self {
server_ids: Default::default(),
links: Default::default(),
}
}
}

View File

@@ -22,36 +22,12 @@ export type ReadResponses = {
ListGitProvidersFromConfig: Types.ListGitProvidersFromConfigResponse;
ListDockerRegistriesFromConfig: Types.ListDockerRegistriesFromConfigResponse;
// ==== USER ====
GetUsername: Types.GetUsernameResponse;
GetPermission: Types.GetPermissionResponse;
FindUser: Types.FindUserResponse;
ListUsers: Types.ListUsersResponse;
ListApiKeys: Types.ListApiKeysResponse;
ListApiKeysForServiceUser: Types.ListApiKeysForServiceUserResponse;
ListPermissions: Types.ListPermissionsResponse;
ListUserTargetPermissions: Types.ListUserTargetPermissionsResponse;
// ==== USER GROUP ====
GetUserGroup: Types.GetUserGroupResponse;
ListUserGroups: Types.ListUserGroupsResponse;
// ==== PROCEDURE ====
GetProceduresSummary: Types.GetProceduresSummaryResponse;
GetProcedure: Types.GetProcedureResponse;
GetProcedureActionState: Types.GetProcedureActionStateResponse;
ListProcedures: Types.ListProceduresResponse;
ListFullProcedures: Types.ListFullProceduresResponse;
// ==== ACTION ====
GetActionsSummary: Types.GetActionsSummaryResponse;
GetAction: Types.GetActionResponse;
GetActionActionState: Types.GetActionActionStateResponse;
ListActions: Types.ListActionsResponse;
ListFullActions: Types.ListFullActionsResponse;
// ==== SCHEDULE ====
ListSchedules: Types.ListSchedulesResponse;
// ==== SWARM ====
GetSwarmsSummary: Types.GetSwarmsSummaryResponse;
GetSwarm: Types.GetSwarmResponse;
GetSwarmActionState: Types.GetSwarmActionStateResponse;
ListSwarms: Types.ListSwarmsResponse;
ListFullSwarms: Types.ListFullSwarmsResponse;
// ==== SERVER ====
GetServersSummary: Types.GetServersSummaryResponse;
@@ -59,9 +35,10 @@ export type ReadResponses = {
GetServerState: Types.GetServerStateResponse;
GetPeripheryInformation: Types.GetPeripheryInformationResponse;
GetServerActionState: Types.GetServerActionStateResponse;
GetHistoricalServerStats: Types.GetHistoricalServerStatsResponse;
ListServers: Types.ListServersResponse;
ListFullServers: Types.ListFullServersResponse;
// ==== TERMINAL ====
ListTerminals: Types.ListTerminalsResponse;
// ==== DOCKER ====
@@ -81,6 +58,12 @@ export type ReadResponses = {
ListDockerVolumes: Types.ListDockerVolumesResponse;
InspectDockerVolume: Types.InspectDockerVolumeResponse;
// ==== SERVER STATS ====
GetSystemInformation: Types.GetSystemInformationResponse;
GetSystemStats: Types.GetSystemStatsResponse;
GetHistoricalServerStats: Types.GetHistoricalServerStatsResponse;
ListSystemProcesses: Types.ListSystemProcessesResponse;
// ==== STACK ====
GetStacksSummary: Types.GetStacksSummaryResponse;
GetStack: Types.GetStackResponse;
@@ -124,6 +107,23 @@ export type ReadResponses = {
ListRepos: Types.ListReposResponse;
ListFullRepos: Types.ListFullReposResponse;
// ==== PROCEDURE ====
GetProceduresSummary: Types.GetProceduresSummaryResponse;
GetProcedure: Types.GetProcedureResponse;
GetProcedureActionState: Types.GetProcedureActionStateResponse;
ListProcedures: Types.ListProceduresResponse;
ListFullProcedures: Types.ListFullProceduresResponse;
// ==== ACTION ====
GetActionsSummary: Types.GetActionsSummaryResponse;
GetAction: Types.GetActionResponse;
GetActionActionState: Types.GetActionActionStateResponse;
ListActions: Types.ListActionsResponse;
ListFullActions: Types.ListFullActionsResponse;
// ==== SCHEDULE ====
ListSchedules: Types.ListSchedulesResponse;
// ==== SYNC ====
GetResourceSyncsSummary: Types.GetResourceSyncsSummaryResponse;
GetResourceSync: Types.GetResourceSyncResponse;
@@ -151,6 +151,20 @@ export type ReadResponses = {
GetTag: Types.GetTagResponse;
ListTags: Types.ListTagsResponse;
// ==== USER ====
GetUsername: Types.GetUsernameResponse;
GetPermission: Types.GetPermissionResponse;
FindUser: Types.FindUserResponse;
ListUsers: Types.ListUsersResponse;
ListApiKeys: Types.ListApiKeysResponse;
ListApiKeysForServiceUser: Types.ListApiKeysForServiceUserResponse;
ListPermissions: Types.ListPermissionsResponse;
ListUserTargetPermissions: Types.ListUserTargetPermissionsResponse;
// ==== USER GROUP ====
GetUserGroup: Types.GetUserGroupResponse;
ListUserGroups: Types.ListUserGroupsResponse;
// ==== UPDATE ====
GetUpdate: Types.GetUpdateResponse;
ListUpdates: Types.ListUpdatesResponse;
@@ -159,11 +173,6 @@ export type ReadResponses = {
ListAlerts: Types.ListAlertsResponse;
GetAlert: Types.GetAlertResponse;
// ==== SERVER STATS ====
GetSystemInformation: Types.GetSystemInformationResponse;
GetSystemStats: Types.GetSystemStatsResponse;
ListSystemProcesses: Types.ListSystemProcessesResponse;
// ==== VARIABLE ====
GetVariable: Types.GetVariableResponse;
ListVariables: Types.ListVariablesResponse;
@@ -179,36 +188,16 @@ export type ReadResponses = {
};
export type WriteResponses = {
// ==== USER ====
CreateLocalUser: Types.CreateLocalUserResponse;
UpdateUserUsername: Types.UpdateUserUsernameResponse;
UpdateUserPassword: Types.UpdateUserPasswordResponse;
DeleteUser: Types.DeleteUserResponse;
// ==== SERVICE USER ====
CreateServiceUser: Types.CreateServiceUserResponse;
UpdateServiceUserDescription: Types.UpdateServiceUserDescriptionResponse;
CreateApiKeyForServiceUser: Types.CreateApiKeyForServiceUserResponse;
DeleteApiKeyForServiceUser: Types.DeleteApiKeyForServiceUserResponse;
// ==== USER GROUP ====
CreateUserGroup: Types.UserGroup;
RenameUserGroup: Types.UserGroup;
DeleteUserGroup: Types.UserGroup;
AddUserToUserGroup: Types.UserGroup;
RemoveUserFromUserGroup: Types.UserGroup;
SetUsersInUserGroup: Types.UserGroup;
SetEveryoneUserGroup: Types.UserGroup;
// ==== PERMISSIONS ====
UpdateUserAdmin: Types.UpdateUserAdminResponse;
UpdateUserBasePermissions: Types.UpdateUserBasePermissionsResponse;
UpdatePermissionOnResourceType: Types.UpdatePermissionOnResourceTypeResponse;
UpdatePermissionOnTarget: Types.UpdatePermissionOnTargetResponse;
// ==== RESOURCE ====
UpdateResourceMeta: Types.UpdateResourceMetaResponse;
// ==== SWARM ====
CreateSwarm: Types.Swarm;
CopySwarm: Types.Swarm;
DeleteSwarm: Types.Swarm;
UpdateSwarm: Types.Swarm;
RenameSwarm: Types.Update;
// ==== SERVER ====
CreateServer: Types.Server;
CopyServer: Types.Server;
@@ -219,6 +208,12 @@ export type WriteResponses = {
UpdateServerPublicKey: Types.Update;
RotateServerKeys: Types.Update;
// ==== TERMINAL ====
CreateTerminal: Types.NoData;
DeleteTerminal: Types.NoData;
DeleteAllTerminals: Types.NoData;
BatchDeleteAllTerminals: Types.NoData;
// ==== STACK ====
CreateStack: Types.Stack;
CopyStack: Types.Stack;
@@ -245,13 +240,6 @@ export type WriteResponses = {
WriteBuildFileContents: Types.Update;
RefreshBuildCache: Types.NoData;
// ==== BUILDER ====
CreateBuilder: Types.Builder;
CopyBuilder: Types.Builder;
DeleteBuilder: Types.Builder;
UpdateBuilder: Types.Builder;
RenameBuilder: Types.Update;
// ==== REPO ====
CreateRepo: Types.Repo;
CopyRepo: Types.Repo;
@@ -260,13 +248,6 @@ export type WriteResponses = {
RenameRepo: Types.Update;
RefreshRepoCache: Types.NoData;
// ==== ALERTER ====
CreateAlerter: Types.Alerter;
CopyAlerter: Types.Alerter;
DeleteAlerter: Types.Alerter;
UpdateAlerter: Types.Alerter;
RenameAlerter: Types.Update;
// ==== PROCEDURE ====
CreateProcedure: Types.Procedure;
CopyProcedure: Types.Procedure;
@@ -291,11 +272,51 @@ export type WriteResponses = {
WriteSyncFileContents: Types.Update;
RefreshResourceSyncPending: Types.ResourceSync;
// ==== TERMINAL ====
CreateTerminal: Types.NoData;
DeleteTerminal: Types.NoData;
DeleteAllTerminals: Types.NoData;
BatchDeleteAllTerminals: Types.NoData;
// ==== BUILDER ====
CreateBuilder: Types.Builder;
CopyBuilder: Types.Builder;
DeleteBuilder: Types.Builder;
UpdateBuilder: Types.Builder;
RenameBuilder: Types.Update;
// ==== ALERTER ====
CreateAlerter: Types.Alerter;
CopyAlerter: Types.Alerter;
DeleteAlerter: Types.Alerter;
UpdateAlerter: Types.Alerter;
RenameAlerter: Types.Update;
// ==== ONBOARDING KEY ====
CreateOnboardingKey: Types.CreateOnboardingKeyResponse;
UpdateOnboardingKey: Types.UpdateOnboardingKeyResponse;
DeleteOnboardingKey: Types.DeleteOnboardingKeyResponse;
// ==== USER ====
CreateLocalUser: Types.CreateLocalUserResponse;
UpdateUserUsername: Types.UpdateUserUsernameResponse;
UpdateUserPassword: Types.UpdateUserPasswordResponse;
DeleteUser: Types.DeleteUserResponse;
// ==== SERVICE USER ====
CreateServiceUser: Types.CreateServiceUserResponse;
UpdateServiceUserDescription: Types.UpdateServiceUserDescriptionResponse;
CreateApiKeyForServiceUser: Types.CreateApiKeyForServiceUserResponse;
DeleteApiKeyForServiceUser: Types.DeleteApiKeyForServiceUserResponse;
// ==== USER GROUP ====
CreateUserGroup: Types.UserGroup;
RenameUserGroup: Types.UserGroup;
DeleteUserGroup: Types.UserGroup;
AddUserToUserGroup: Types.UserGroup;
RemoveUserFromUserGroup: Types.UserGroup;
SetUsersInUserGroup: Types.UserGroup;
SetEveryoneUserGroup: Types.UserGroup;
// ==== PERMISSIONS ====
UpdateUserAdmin: Types.UpdateUserAdminResponse;
UpdateUserBasePermissions: Types.UpdateUserBasePermissionsResponse;
UpdatePermissionOnResourceType: Types.UpdatePermissionOnResourceTypeResponse;
UpdatePermissionOnTarget: Types.UpdatePermissionOnTargetResponse;
// ==== TAG ====
CreateTag: Types.Tag;
@@ -310,7 +331,7 @@ export type WriteResponses = {
UpdateVariableIsSecret: Types.UpdateVariableIsSecretResponse;
DeleteVariable: Types.DeleteVariableResponse;
// ==== PROVIDERS ====
// ==== PROVIDER ====
CreateGitProviderAccount: Types.CreateGitProviderAccountResponse;
UpdateGitProviderAccount: Types.UpdateGitProviderAccountResponse;
DeleteGitProviderAccount: Types.DeleteGitProviderAccountResponse;
@@ -318,11 +339,6 @@ export type WriteResponses = {
UpdateDockerRegistryAccount: Types.UpdateDockerRegistryAccountResponse;
DeleteDockerRegistryAccount: Types.DeleteDockerRegistryAccountResponse;
// ==== ONBOARDING KEY ====
CreateOnboardingKey: Types.CreateOnboardingKeyResponse;
UpdateOnboardingKey: Types.UpdateOnboardingKeyResponse;
DeleteOnboardingKey: Types.DeleteOnboardingKeyResponse;
// ==== ALERT ====
CloseAlert: Types.NoData;
};
@@ -365,6 +381,7 @@ export type ExecuteResponses = {
UnpauseStack: Types.Update;
DestroyStack: Types.Update;
BatchDestroyStack: Types.BatchExecutionResponse;
RunStackService: Types.Update;
// ==== DEPLOYMENT ====
Deploy: Types.Update;
@@ -403,16 +420,6 @@ export type ExecuteResponses = {
// ==== SYNC ====
RunSync: Types.Update;
// ==== STACK Service ====
DeployStackService: Types.Update;
StartStackService: Types.Update;
RestartStackService: Types.Update;
StopStackService: Types.Update;
PauseStackService: Types.Update;
UnpauseStackService: Types.Update;
DestroyStackService: Types.Update;
RunStackService: Types.Update;
// ==== ALERTER ====
TestAlerter: Types.Update;
SendAlert: Types.Update;

View File

@@ -2564,6 +2564,8 @@ export interface SwarmConfig {
* tries the next Server.
*/
server_ids?: string[];
/** Configure quick links that are displayed in the resource header */
links?: string[];
}
export interface SwarmInfo {
@@ -4144,8 +4146,6 @@ export enum SwarmState {
Healthy = "Healthy",
/** The Swarm is unhealthy */
Unhealthy = "Unhealthy",
/** Servers are reachable, but Swarm is not running on any of them. */
Offline = "Offline",
}
export interface SwarmListItemInfo {
@@ -5800,6 +5800,15 @@ export interface DiscordAlerterEndpoint {
url: string;
}
/** Standard docker lists available from a Server. */
export interface DockerLists {
containers: ContainerListItem[];
networks: NetworkListItem[];
images: ImageListItem[];
volumes: VolumeListItem[];
projects: ComposeProject[];
}
/** Driver represents a driver (network, logging, secrets). */
export interface Driver {
/** Name of the driver. */
@@ -6637,8 +6646,8 @@ export interface GetSwarmsSummaryResponse {
healthy: number;
/** The number of Swarms with Unhealthy state */
unhealthy: number;
/** The number of Swarms with Offline state */
offline: number;
/** The number of Swarms with Unknown state */
unknown: number;
}
/**
@@ -6798,6 +6807,14 @@ export interface InspectStackContainer {
service: string;
}
/** JoinTokens contains the tokens workers and managers need to join the swarm. */
export interface JoinTokens {
/** The token workers can use to join the swarm. */
Worker?: string;
/** The token managers can use to join the swarm. */
Manager?: string;
}
export interface LatestCommit {
hash: string;
message: string;
@@ -8804,6 +8821,124 @@ export interface StopStack {
services?: string[];
}
/** Orchestration configuration. */
export interface SwarmSpecOrchestration {
/**
* The number of historic tasks to keep per instance or node.
* If negative, never remove completed or failed tasks.
*/
TaskHistoryRetentionLimit?: I64;
}
/** Raft configuration. */
export interface SwarmSpecRaft {
/** The number of log entries between snapshots. */
SnapshotInterval?: U64;
/** The number of snapshots to keep beyond the current snapshot. */
KeepOldSnapshots?: U64;
/** The number of log entries to keep around to sync up slow followers after a snapshot is created. */
LogEntriesForSlowFollowers?: U64;
/** The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. */
ElectionTick?: I64;
/**
* The number of ticks between heartbeats.
* Every HeartbeatTick ticks, the leader will send a heartbeat to the followers.
* A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed.
*/
HeartbeatTick?: I64;
}
/** Dispatcher configuration. */
export interface SwarmSpecDispatcher {
/** The delay for an agent to send a heartbeat to the dispatcher. */
HeartbeatPeriod?: I64;
}
export enum SwarmSpecCaConfigExternalCasProtocolEnum {
EMPTY = "",
CFSSL = "cfssl",
}
export interface SwarmSpecCaConfigExternalCas {
/** Protocol for communication with the external CA (currently only `cfssl` is supported). */
Protocol?: SwarmSpecCaConfigExternalCasProtocolEnum;
/** URL where certificate signing requests should be sent. */
URL?: string;
/** An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver. */
Options?: Record<string, string>;
/** The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided). */
CACert?: string;
}
/** CA configuration. */
export interface SwarmSpecCaConfig {
/** The duration node certificates are issued for. */
NodeCertExpiry?: I64;
/** Configuration for forwarding signing requests to an external certificate authority. */
ExternalCAs?: SwarmSpecCaConfigExternalCas[];
/** The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format. */
SigningCACert?: string;
/** The desired signing CA key for all swarm node TLS leaf certificates, in PEM format. */
SigningCAKey?: string;
/** An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey` */
ForceRotate?: U64;
}
/** Parameters related to encryption-at-rest. */
export interface SwarmSpecEncryptionConfig {
/** If set, generate a key and use it to lock data stored on the managers. */
AutoLockManagers?: boolean;
}
/** The log driver to use for tasks created in the orchestrator if unspecified by a service. Updating this value only affects new tasks. Existing tasks continue to use their previously configured log driver until recreated. */
export interface SwarmSpecTaskDefaultsLogDriver {
/** The log driver to use as a default for new tasks. */
Name?: string;
/** Driver-specific options for the selected log driver, specified as key/value pairs. */
Options?: Record<string, string>;
}
/** Defaults for creating tasks in this cluster. */
export interface SwarmSpecTaskDefaults {
LogDriver?: SwarmSpecTaskDefaultsLogDriver;
}
/** User modifiable swarm configuration. */
export interface SwarmSpec {
/** Name of the swarm. */
Name?: string;
/** User-defined key/value metadata. */
Labels?: Record<string, string>;
Orchestration?: SwarmSpecOrchestration;
Raft?: SwarmSpecRaft;
Dispatcher?: SwarmSpecDispatcher;
CAConfig?: SwarmSpecCaConfig;
EncryptionConfig?: SwarmSpecEncryptionConfig;
TaskDefaults?: SwarmSpecTaskDefaults;
}
/** Docker-level information about the Swarm. */
export interface SwarmInspectInfo {
/** The (Docker) ID of the swarm. */
ID?: string;
Version?: ObjectVersion;
/** Date and time at which the swarm was initialised in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
CreatedAt?: string;
/** Date and time at which the swarm was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
UpdatedAt?: string;
Spec?: SwarmSpec;
TLSInfo?: TlsInfo;
/** Whether there is currently a root CA rotation in progress for the swarm */
RootRotationInProgress?: boolean;
/** DataPathPort specifies the data path port number for data traffic. Acceptable port range is 1024 to 49151. If no port is set or is set to 0, the default port (4789) is used. */
DataPathPort?: number;
/** Default Address Pool specifies default subnet pools for global scope networks. */
DefaultAddrPool?: string[];
/** SubnetSize specifies the subnet size of the networks created from the default subnet pool. */
SubnetSize?: number;
JoinTokens?: JoinTokens;
}
/** Swarm node details. */
export interface SwarmNode {
ID?: string;
@@ -9486,9 +9621,9 @@ export type ExecuteRequest =
| { type: "BatchRunProcedure", params: BatchRunProcedure }
| { type: "RunAction", params: RunAction }
| { type: "BatchRunAction", params: BatchRunAction }
| { type: "RunSync", params: RunSync }
| { type: "TestAlerter", params: TestAlerter }
| { type: "SendAlert", params: SendAlert }
| { type: "RunSync", params: RunSync }
| { type: "ClearRepoCache", params: ClearRepoCache }
| { type: "BackupCoreDatabase", params: BackupCoreDatabase }
| { type: "GlobalAutoUpdate", params: GlobalAutoUpdate }
@@ -9587,33 +9722,16 @@ export type ReadRequest =
| { type: "ListSecrets", params: ListSecrets }
| { type: "ListGitProvidersFromConfig", params: ListGitProvidersFromConfig }
| { type: "ListDockerRegistriesFromConfig", params: ListDockerRegistriesFromConfig }
| { type: "GetUsername", params: GetUsername }
| { type: "GetPermission", params: GetPermission }
| { type: "FindUser", params: FindUser }
| { type: "ListUsers", params: ListUsers }
| { type: "ListApiKeys", params: ListApiKeys }
| { type: "ListApiKeysForServiceUser", params: ListApiKeysForServiceUser }
| { type: "ListPermissions", params: ListPermissions }
| { type: "ListUserTargetPermissions", params: ListUserTargetPermissions }
| { type: "GetUserGroup", params: GetUserGroup }
| { type: "ListUserGroups", params: ListUserGroups }
| { type: "GetProceduresSummary", params: GetProceduresSummary }
| { type: "GetProcedure", params: GetProcedure }
| { type: "GetProcedureActionState", params: GetProcedureActionState }
| { type: "ListProcedures", params: ListProcedures }
| { type: "ListFullProcedures", params: ListFullProcedures }
| { type: "GetActionsSummary", params: GetActionsSummary }
| { type: "GetAction", params: GetAction }
| { type: "GetActionActionState", params: GetActionActionState }
| { type: "ListActions", params: ListActions }
| { type: "ListFullActions", params: ListFullActions }
| { type: "ListSchedules", params: ListSchedules }
| { type: "GetSwarmsSummary", params: GetSwarmsSummary }
| { type: "GetSwarm", params: GetSwarm }
| { type: "GetSwarmActionState", params: GetSwarmActionState }
| { type: "ListSwarms", params: ListSwarms }
| { type: "ListFullSwarms", params: ListFullSwarms }
| { type: "GetServersSummary", params: GetServersSummary }
| { type: "GetServer", params: GetServer }
| { type: "GetServerState", params: GetServerState }
| { type: "GetPeripheryInformation", params: GetPeripheryInformation }
| { type: "GetServerActionState", params: GetServerActionState }
| { type: "GetHistoricalServerStats", params: GetHistoricalServerStats }
| { type: "ListServers", params: ListServers }
| { type: "ListFullServers", params: ListFullServers }
| { type: "ListTerminals", params: ListTerminals }
@@ -9634,6 +9752,7 @@ export type ReadRequest =
| { type: "InspectDockerVolume", params: InspectDockerVolume }
| { type: "GetSystemInformation", params: GetSystemInformation }
| { type: "GetSystemStats", params: GetSystemStats }
| { type: "GetHistoricalServerStats", params: GetHistoricalServerStats }
| { type: "ListSystemProcesses", params: ListSystemProcesses }
| { type: "GetStacksSummary", params: GetStacksSummary }
| { type: "GetStack", params: GetStack }
@@ -9670,6 +9789,17 @@ export type ReadRequest =
| { type: "GetRepoActionState", params: GetRepoActionState }
| { type: "ListRepos", params: ListRepos }
| { type: "ListFullRepos", params: ListFullRepos }
| { type: "GetProceduresSummary", params: GetProceduresSummary }
| { type: "GetProcedure", params: GetProcedure }
| { type: "GetProcedureActionState", params: GetProcedureActionState }
| { type: "ListProcedures", params: ListProcedures }
| { type: "ListFullProcedures", params: ListFullProcedures }
| { type: "GetActionsSummary", params: GetActionsSummary }
| { type: "GetAction", params: GetAction }
| { type: "GetActionActionState", params: GetActionActionState }
| { type: "ListActions", params: ListActions }
| { type: "ListFullActions", params: ListFullActions }
| { type: "ListSchedules", params: ListSchedules }
| { type: "GetResourceSyncsSummary", params: GetResourceSyncsSummary }
| { type: "GetResourceSync", params: GetResourceSync }
| { type: "GetResourceSyncActionState", params: GetResourceSyncActionState }
@@ -9687,6 +9817,16 @@ export type ReadRequest =
| { type: "ExportResourcesToToml", params: ExportResourcesToToml }
| { type: "GetTag", params: GetTag }
| { type: "ListTags", params: ListTags }
| { type: "GetUsername", params: GetUsername }
| { type: "GetPermission", params: GetPermission }
| { type: "FindUser", params: FindUser }
| { type: "ListUsers", params: ListUsers }
| { type: "ListApiKeys", params: ListApiKeys }
| { type: "ListApiKeysForServiceUser", params: ListApiKeysForServiceUser }
| { type: "ListPermissions", params: ListPermissions }
| { type: "ListUserTargetPermissions", params: ListUserTargetPermissions }
| { type: "GetUserGroup", params: GetUserGroup }
| { type: "ListUserGroups", params: ListUserGroups }
| { type: "GetUpdate", params: GetUpdate }
| { type: "ListUpdates", params: ListUpdates }
| { type: "ListAlerts", params: ListAlerts }
@@ -9761,26 +9901,12 @@ export type UserRequest =
| { type: "DeleteApiKey", params: DeleteApiKey };
export type WriteRequest =
| { type: "CreateLocalUser", params: CreateLocalUser }
| { type: "UpdateUserUsername", params: UpdateUserUsername }
| { type: "UpdateUserPassword", params: UpdateUserPassword }
| { type: "DeleteUser", params: DeleteUser }
| { type: "CreateServiceUser", params: CreateServiceUser }
| { type: "UpdateServiceUserDescription", params: UpdateServiceUserDescription }
| { type: "CreateApiKeyForServiceUser", params: CreateApiKeyForServiceUser }
| { type: "DeleteApiKeyForServiceUser", params: DeleteApiKeyForServiceUser }
| { type: "CreateUserGroup", params: CreateUserGroup }
| { type: "RenameUserGroup", params: RenameUserGroup }
| { type: "DeleteUserGroup", params: DeleteUserGroup }
| { type: "AddUserToUserGroup", params: AddUserToUserGroup }
| { type: "RemoveUserFromUserGroup", params: RemoveUserFromUserGroup }
| { type: "SetUsersInUserGroup", params: SetUsersInUserGroup }
| { type: "SetEveryoneUserGroup", params: SetEveryoneUserGroup }
| { type: "UpdateUserAdmin", params: UpdateUserAdmin }
| { type: "UpdateUserBasePermissions", params: UpdateUserBasePermissions }
| { type: "UpdatePermissionOnResourceType", params: UpdatePermissionOnResourceType }
| { type: "UpdatePermissionOnTarget", params: UpdatePermissionOnTarget }
| { type: "UpdateResourceMeta", params: UpdateResourceMeta }
| { type: "CreateSwarm", params: CreateSwarm }
| { type: "CopySwarm", params: CopySwarm }
| { type: "DeleteSwarm", params: DeleteSwarm }
| { type: "UpdateSwarm", params: UpdateSwarm }
| { type: "RenameSwarm", params: RenameSwarm }
| { type: "CreateServer", params: CreateServer }
| { type: "CopyServer", params: CopyServer }
| { type: "DeleteServer", params: DeleteServer }
@@ -9789,6 +9915,10 @@ export type WriteRequest =
| { type: "CreateNetwork", params: CreateNetwork }
| { type: "UpdateServerPublicKey", params: UpdateServerPublicKey }
| { type: "RotateServerKeys", params: RotateServerKeys }
| { type: "CreateTerminal", params: CreateTerminal }
| { type: "DeleteTerminal", params: DeleteTerminal }
| { type: "DeleteAllTerminals", params: DeleteAllTerminals }
| { type: "BatchDeleteAllTerminals", params: BatchDeleteAllTerminals }
| { type: "CreateStack", params: CreateStack }
| { type: "CopyStack", params: CopyStack }
| { type: "DeleteStack", params: DeleteStack }
@@ -9809,22 +9939,12 @@ export type WriteRequest =
| { type: "RenameBuild", params: RenameBuild }
| { type: "WriteBuildFileContents", params: WriteBuildFileContents }
| { type: "RefreshBuildCache", params: RefreshBuildCache }
| { type: "CreateBuilder", params: CreateBuilder }
| { type: "CopyBuilder", params: CopyBuilder }
| { type: "DeleteBuilder", params: DeleteBuilder }
| { type: "UpdateBuilder", params: UpdateBuilder }
| { type: "RenameBuilder", params: RenameBuilder }
| { type: "CreateRepo", params: CreateRepo }
| { type: "CopyRepo", params: CopyRepo }
| { type: "DeleteRepo", params: DeleteRepo }
| { type: "UpdateRepo", params: UpdateRepo }
| { type: "RenameRepo", params: RenameRepo }
| { type: "RefreshRepoCache", params: RefreshRepoCache }
| { type: "CreateAlerter", params: CreateAlerter }
| { type: "CopyAlerter", params: CopyAlerter }
| { type: "DeleteAlerter", params: DeleteAlerter }
| { type: "UpdateAlerter", params: UpdateAlerter }
| { type: "RenameAlerter", params: RenameAlerter }
| { type: "CreateProcedure", params: CreateProcedure }
| { type: "CopyProcedure", params: CopyProcedure }
| { type: "DeleteProcedure", params: DeleteProcedure }
@@ -9843,10 +9963,38 @@ export type WriteRequest =
| { type: "WriteSyncFileContents", params: WriteSyncFileContents }
| { type: "CommitSync", params: CommitSync }
| { type: "RefreshResourceSyncPending", params: RefreshResourceSyncPending }
| { type: "CreateTerminal", params: CreateTerminal }
| { type: "DeleteTerminal", params: DeleteTerminal }
| { type: "DeleteAllTerminals", params: DeleteAllTerminals }
| { type: "BatchDeleteAllTerminals", params: BatchDeleteAllTerminals }
| { type: "CreateBuilder", params: CreateBuilder }
| { type: "CopyBuilder", params: CopyBuilder }
| { type: "DeleteBuilder", params: DeleteBuilder }
| { type: "UpdateBuilder", params: UpdateBuilder }
| { type: "RenameBuilder", params: RenameBuilder }
| { type: "CreateAlerter", params: CreateAlerter }
| { type: "CopyAlerter", params: CopyAlerter }
| { type: "DeleteAlerter", params: DeleteAlerter }
| { type: "UpdateAlerter", params: UpdateAlerter }
| { type: "RenameAlerter", params: RenameAlerter }
| { type: "CreateOnboardingKey", params: CreateOnboardingKey }
| { type: "UpdateOnboardingKey", params: UpdateOnboardingKey }
| { type: "DeleteOnboardingKey", params: DeleteOnboardingKey }
| { type: "CreateLocalUser", params: CreateLocalUser }
| { type: "UpdateUserUsername", params: UpdateUserUsername }
| { type: "UpdateUserPassword", params: UpdateUserPassword }
| { type: "DeleteUser", params: DeleteUser }
| { type: "CreateServiceUser", params: CreateServiceUser }
| { type: "UpdateServiceUserDescription", params: UpdateServiceUserDescription }
| { type: "CreateApiKeyForServiceUser", params: CreateApiKeyForServiceUser }
| { type: "DeleteApiKeyForServiceUser", params: DeleteApiKeyForServiceUser }
| { type: "CreateUserGroup", params: CreateUserGroup }
| { type: "RenameUserGroup", params: RenameUserGroup }
| { type: "DeleteUserGroup", params: DeleteUserGroup }
| { type: "AddUserToUserGroup", params: AddUserToUserGroup }
| { type: "RemoveUserFromUserGroup", params: RemoveUserFromUserGroup }
| { type: "SetUsersInUserGroup", params: SetUsersInUserGroup }
| { type: "SetEveryoneUserGroup", params: SetEveryoneUserGroup }
| { type: "UpdateUserAdmin", params: UpdateUserAdmin }
| { type: "UpdateUserBasePermissions", params: UpdateUserBasePermissions }
| { type: "UpdatePermissionOnResourceType", params: UpdatePermissionOnResourceType }
| { type: "UpdatePermissionOnTarget", params: UpdatePermissionOnTarget }
| { type: "CreateTag", params: CreateTag }
| { type: "DeleteTag", params: DeleteTag }
| { type: "RenameTag", params: RenameTag }
@@ -9862,9 +10010,6 @@ export type WriteRequest =
| { type: "CreateDockerRegistryAccount", params: CreateDockerRegistryAccount }
| { type: "UpdateDockerRegistryAccount", params: UpdateDockerRegistryAccount }
| { type: "DeleteDockerRegistryAccount", params: DeleteDockerRegistryAccount }
| { type: "CreateOnboardingKey", params: CreateOnboardingKey }
| { type: "UpdateOnboardingKey", params: UpdateOnboardingKey }
| { type: "DeleteOnboardingKey", params: DeleteOnboardingKey }
| { type: "CloseAlert", params: CloseAlert };
export type WsLoginMessage =

View File

@@ -1,12 +1,5 @@
use komodo_client::entities::{
config::{DockerRegistry, GitProvider},
docker::{
container::ContainerListItem, image::ImageListItem,
network::NetworkListItem, volume::VolumeListItem,
},
server::PeripheryInformation,
stack::ComposeProject,
stats::{SystemInformation, SystemStats},
update::Log,
};
use resolver_api::Resolve;
@@ -18,6 +11,7 @@ pub mod container;
pub mod docker;
pub mod git;
pub mod keys;
pub mod poll;
pub mod stats;
pub mod swarm;
pub mod terminal;
@@ -60,33 +54,6 @@ pub struct GetVersionResponse {
//
/// This is the data Core uses to update all Server-related status caches.
#[derive(Serialize, Deserialize, Debug, Clone, Resolve)]
#[response(PollStatusResponse)]
#[error(anyhow::Error)]
pub struct PollStatus {
/// Some servers have stats monitoring disabled.
pub include_stats: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PollStatusResponse {
pub periphery_info: PeripheryInformation,
/// Basic system information
pub system_info: SystemInformation,
/// Current System Stats (Cpu, Mem, Disk)
pub system_stats: Option<SystemStats>,
// Docker lists
pub containers: Vec<ContainerListItem>,
pub networks: Vec<NetworkListItem>,
pub images: Vec<ImageListItem>,
pub volumes: Vec<VolumeListItem>,
pub projects: Vec<ComposeProject>,
}
//
#[derive(Serialize, Deserialize, Debug, Clone, Resolve)]
#[response(ListGitProvidersResponse)]
#[error(anyhow::Error)]

View File

@@ -0,0 +1,29 @@
use komodo_client::entities::{
docker::DockerLists,
server::PeripheryInformation,
stats::{SystemInformation, SystemStats},
};
use resolver_api::Resolve;
use serde::{Deserialize, Serialize};
/// This is the data Core uses to update all Server-related status caches.
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(PollStatusResponse)]
#[error(anyhow::Error)]
pub struct PollStatus {
/// Include system stats
pub include_stats: bool,
/// Include docker info
pub include_docker: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PollStatusResponse {
pub periphery_info: PeripheryInformation,
/// Basic system information
pub system_info: SystemInformation,
/// Current System Stats (Cpu, Mem, Disk)
pub system_stats: Option<SystemStats>,
/// Docker lists
pub docker: Option<DockerLists>,
}

View File

@@ -1,10 +1,22 @@
use komodo_client::entities::docker::{
node::SwarmNode, secret::SwarmSecret, service::SwarmService,
task::SwarmTask,
SwarmLists, node::SwarmNode, secret::SwarmSecret,
service::SwarmService, swarm::SwarmInspectInfo, task::SwarmTask,
};
use resolver_api::Resolve;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(PollSwarmStatusResponse)]
#[error(anyhow::Error)]
pub struct PollSwarmStatus {}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PollSwarmStatusResponse {
/// Inspect swarm response
pub inspect: Option<SwarmInspectInfo>,
pub lists: SwarmLists,
}
// ======
// Node
// ======

View File

@@ -18,33 +18,16 @@ export type ReadResponses = {
ListSecrets: Types.ListSecretsResponse;
ListGitProvidersFromConfig: Types.ListGitProvidersFromConfigResponse;
ListDockerRegistriesFromConfig: Types.ListDockerRegistriesFromConfigResponse;
GetUsername: Types.GetUsernameResponse;
GetPermission: Types.GetPermissionResponse;
FindUser: Types.FindUserResponse;
ListUsers: Types.ListUsersResponse;
ListApiKeys: Types.ListApiKeysResponse;
ListApiKeysForServiceUser: Types.ListApiKeysForServiceUserResponse;
ListPermissions: Types.ListPermissionsResponse;
ListUserTargetPermissions: Types.ListUserTargetPermissionsResponse;
GetUserGroup: Types.GetUserGroupResponse;
ListUserGroups: Types.ListUserGroupsResponse;
GetProceduresSummary: Types.GetProceduresSummaryResponse;
GetProcedure: Types.GetProcedureResponse;
GetProcedureActionState: Types.GetProcedureActionStateResponse;
ListProcedures: Types.ListProceduresResponse;
ListFullProcedures: Types.ListFullProceduresResponse;
GetActionsSummary: Types.GetActionsSummaryResponse;
GetAction: Types.GetActionResponse;
GetActionActionState: Types.GetActionActionStateResponse;
ListActions: Types.ListActionsResponse;
ListFullActions: Types.ListFullActionsResponse;
ListSchedules: Types.ListSchedulesResponse;
GetSwarmsSummary: Types.GetSwarmsSummaryResponse;
GetSwarm: Types.GetSwarmResponse;
GetSwarmActionState: Types.GetSwarmActionStateResponse;
ListSwarms: Types.ListSwarmsResponse;
ListFullSwarms: Types.ListFullSwarmsResponse;
GetServersSummary: Types.GetServersSummaryResponse;
GetServer: Types.GetServerResponse;
GetServerState: Types.GetServerStateResponse;
GetPeripheryInformation: Types.GetPeripheryInformationResponse;
GetServerActionState: Types.GetServerActionStateResponse;
GetHistoricalServerStats: Types.GetHistoricalServerStatsResponse;
ListServers: Types.ListServersResponse;
ListFullServers: Types.ListFullServersResponse;
ListTerminals: Types.ListTerminalsResponse;
@@ -63,6 +46,10 @@ export type ReadResponses = {
ListDockerImageHistory: Types.ListDockerImageHistoryResponse;
ListDockerVolumes: Types.ListDockerVolumesResponse;
InspectDockerVolume: Types.InspectDockerVolumeResponse;
GetSystemInformation: Types.GetSystemInformationResponse;
GetSystemStats: Types.GetSystemStatsResponse;
GetHistoricalServerStats: Types.GetHistoricalServerStatsResponse;
ListSystemProcesses: Types.ListSystemProcessesResponse;
GetStacksSummary: Types.GetStacksSummaryResponse;
GetStack: Types.GetStackResponse;
GetStackActionState: Types.GetStackActionStateResponse;
@@ -98,6 +85,17 @@ export type ReadResponses = {
GetRepoActionState: Types.GetRepoActionStateResponse;
ListRepos: Types.ListReposResponse;
ListFullRepos: Types.ListFullReposResponse;
GetProceduresSummary: Types.GetProceduresSummaryResponse;
GetProcedure: Types.GetProcedureResponse;
GetProcedureActionState: Types.GetProcedureActionStateResponse;
ListProcedures: Types.ListProceduresResponse;
ListFullProcedures: Types.ListFullProceduresResponse;
GetActionsSummary: Types.GetActionsSummaryResponse;
GetAction: Types.GetActionResponse;
GetActionActionState: Types.GetActionActionStateResponse;
ListActions: Types.ListActionsResponse;
ListFullActions: Types.ListFullActionsResponse;
ListSchedules: Types.ListSchedulesResponse;
GetResourceSyncsSummary: Types.GetResourceSyncsSummaryResponse;
GetResourceSync: Types.GetResourceSyncResponse;
GetResourceSyncActionState: Types.GetResourceSyncActionStateResponse;
@@ -115,13 +113,20 @@ export type ReadResponses = {
ExportResourcesToToml: Types.ExportResourcesToTomlResponse;
GetTag: Types.GetTagResponse;
ListTags: Types.ListTagsResponse;
GetUsername: Types.GetUsernameResponse;
GetPermission: Types.GetPermissionResponse;
FindUser: Types.FindUserResponse;
ListUsers: Types.ListUsersResponse;
ListApiKeys: Types.ListApiKeysResponse;
ListApiKeysForServiceUser: Types.ListApiKeysForServiceUserResponse;
ListPermissions: Types.ListPermissionsResponse;
ListUserTargetPermissions: Types.ListUserTargetPermissionsResponse;
GetUserGroup: Types.GetUserGroupResponse;
ListUserGroups: Types.ListUserGroupsResponse;
GetUpdate: Types.GetUpdateResponse;
ListUpdates: Types.ListUpdatesResponse;
ListAlerts: Types.ListAlertsResponse;
GetAlert: Types.GetAlertResponse;
GetSystemInformation: Types.GetSystemInformationResponse;
GetSystemStats: Types.GetSystemStatsResponse;
ListSystemProcesses: Types.ListSystemProcessesResponse;
GetVariable: Types.GetVariableResponse;
ListVariables: Types.ListVariablesResponse;
GetGitProviderAccount: Types.GetGitProviderAccountResponse;
@@ -131,26 +136,12 @@ export type ReadResponses = {
ListOnboardingKeys: Types.ListOnboardingKeysResponse;
};
export type WriteResponses = {
CreateLocalUser: Types.CreateLocalUserResponse;
UpdateUserUsername: Types.UpdateUserUsernameResponse;
UpdateUserPassword: Types.UpdateUserPasswordResponse;
DeleteUser: Types.DeleteUserResponse;
CreateServiceUser: Types.CreateServiceUserResponse;
UpdateServiceUserDescription: Types.UpdateServiceUserDescriptionResponse;
CreateApiKeyForServiceUser: Types.CreateApiKeyForServiceUserResponse;
DeleteApiKeyForServiceUser: Types.DeleteApiKeyForServiceUserResponse;
CreateUserGroup: Types.UserGroup;
RenameUserGroup: Types.UserGroup;
DeleteUserGroup: Types.UserGroup;
AddUserToUserGroup: Types.UserGroup;
RemoveUserFromUserGroup: Types.UserGroup;
SetUsersInUserGroup: Types.UserGroup;
SetEveryoneUserGroup: Types.UserGroup;
UpdateUserAdmin: Types.UpdateUserAdminResponse;
UpdateUserBasePermissions: Types.UpdateUserBasePermissionsResponse;
UpdatePermissionOnResourceType: Types.UpdatePermissionOnResourceTypeResponse;
UpdatePermissionOnTarget: Types.UpdatePermissionOnTargetResponse;
UpdateResourceMeta: Types.UpdateResourceMetaResponse;
CreateSwarm: Types.Swarm;
CopySwarm: Types.Swarm;
DeleteSwarm: Types.Swarm;
UpdateSwarm: Types.Swarm;
RenameSwarm: Types.Update;
CreateServer: Types.Server;
CopyServer: Types.Server;
DeleteServer: Types.Server;
@@ -159,6 +150,10 @@ export type WriteResponses = {
CreateNetwork: Types.Update;
UpdateServerPublicKey: Types.Update;
RotateServerKeys: Types.Update;
CreateTerminal: Types.NoData;
DeleteTerminal: Types.NoData;
DeleteAllTerminals: Types.NoData;
BatchDeleteAllTerminals: Types.NoData;
CreateStack: Types.Stack;
CopyStack: Types.Stack;
DeleteStack: Types.Stack;
@@ -179,22 +174,12 @@ export type WriteResponses = {
RenameBuild: Types.Update;
WriteBuildFileContents: Types.Update;
RefreshBuildCache: Types.NoData;
CreateBuilder: Types.Builder;
CopyBuilder: Types.Builder;
DeleteBuilder: Types.Builder;
UpdateBuilder: Types.Builder;
RenameBuilder: Types.Update;
CreateRepo: Types.Repo;
CopyRepo: Types.Repo;
DeleteRepo: Types.Repo;
UpdateRepo: Types.Repo;
RenameRepo: Types.Update;
RefreshRepoCache: Types.NoData;
CreateAlerter: Types.Alerter;
CopyAlerter: Types.Alerter;
DeleteAlerter: Types.Alerter;
UpdateAlerter: Types.Alerter;
RenameAlerter: Types.Update;
CreateProcedure: Types.Procedure;
CopyProcedure: Types.Procedure;
DeleteProcedure: Types.Procedure;
@@ -213,10 +198,38 @@ export type WriteResponses = {
CommitSync: Types.Update;
WriteSyncFileContents: Types.Update;
RefreshResourceSyncPending: Types.ResourceSync;
CreateTerminal: Types.NoData;
DeleteTerminal: Types.NoData;
DeleteAllTerminals: Types.NoData;
BatchDeleteAllTerminals: Types.NoData;
CreateBuilder: Types.Builder;
CopyBuilder: Types.Builder;
DeleteBuilder: Types.Builder;
UpdateBuilder: Types.Builder;
RenameBuilder: Types.Update;
CreateAlerter: Types.Alerter;
CopyAlerter: Types.Alerter;
DeleteAlerter: Types.Alerter;
UpdateAlerter: Types.Alerter;
RenameAlerter: Types.Update;
CreateOnboardingKey: Types.CreateOnboardingKeyResponse;
UpdateOnboardingKey: Types.UpdateOnboardingKeyResponse;
DeleteOnboardingKey: Types.DeleteOnboardingKeyResponse;
CreateLocalUser: Types.CreateLocalUserResponse;
UpdateUserUsername: Types.UpdateUserUsernameResponse;
UpdateUserPassword: Types.UpdateUserPasswordResponse;
DeleteUser: Types.DeleteUserResponse;
CreateServiceUser: Types.CreateServiceUserResponse;
UpdateServiceUserDescription: Types.UpdateServiceUserDescriptionResponse;
CreateApiKeyForServiceUser: Types.CreateApiKeyForServiceUserResponse;
DeleteApiKeyForServiceUser: Types.DeleteApiKeyForServiceUserResponse;
CreateUserGroup: Types.UserGroup;
RenameUserGroup: Types.UserGroup;
DeleteUserGroup: Types.UserGroup;
AddUserToUserGroup: Types.UserGroup;
RemoveUserFromUserGroup: Types.UserGroup;
SetUsersInUserGroup: Types.UserGroup;
SetEveryoneUserGroup: Types.UserGroup;
UpdateUserAdmin: Types.UpdateUserAdminResponse;
UpdateUserBasePermissions: Types.UpdateUserBasePermissionsResponse;
UpdatePermissionOnResourceType: Types.UpdatePermissionOnResourceTypeResponse;
UpdatePermissionOnTarget: Types.UpdatePermissionOnTargetResponse;
CreateTag: Types.Tag;
DeleteTag: Types.Tag;
RenameTag: Types.Tag;
@@ -232,9 +245,6 @@ export type WriteResponses = {
CreateDockerRegistryAccount: Types.CreateDockerRegistryAccountResponse;
UpdateDockerRegistryAccount: Types.UpdateDockerRegistryAccountResponse;
DeleteDockerRegistryAccount: Types.DeleteDockerRegistryAccountResponse;
CreateOnboardingKey: Types.CreateOnboardingKeyResponse;
UpdateOnboardingKey: Types.UpdateOnboardingKeyResponse;
DeleteOnboardingKey: Types.DeleteOnboardingKeyResponse;
CloseAlert: Types.NoData;
};
export type ExecuteResponses = {
@@ -272,6 +282,7 @@ export type ExecuteResponses = {
UnpauseStack: Types.Update;
DestroyStack: Types.Update;
BatchDestroyStack: Types.BatchExecutionResponse;
RunStackService: Types.Update;
Deploy: Types.Update;
BatchDeploy: Types.BatchExecutionResponse;
PullDeployment: Types.Update;
@@ -297,14 +308,6 @@ export type ExecuteResponses = {
RunAction: Types.Update;
BatchRunAction: Types.BatchExecutionResponse;
RunSync: Types.Update;
DeployStackService: Types.Update;
StartStackService: Types.Update;
RestartStackService: Types.Update;
StopStackService: Types.Update;
PauseStackService: Types.Update;
UnpauseStackService: Types.Update;
DestroyStackService: Types.Update;
RunStackService: Types.Update;
TestAlerter: Types.Update;
SendAlert: Types.Update;
ClearRepoCache: Types.Update;

View File

@@ -2684,6 +2684,8 @@ export interface SwarmConfig {
* tries the next Server.
*/
server_ids?: string[];
/** Configure quick links that are displayed in the resource header */
links?: string[];
}
export interface SwarmInfo {
}
@@ -4108,9 +4110,7 @@ export declare enum SwarmState {
/** The Swarm is healthy, all nodes OK */
Healthy = "Healthy",
/** The Swarm is unhealthy */
Unhealthy = "Unhealthy",
/** Servers are reachable, but Swarm is not running on any of them. */
Offline = "Offline"
Unhealthy = "Unhealthy"
}
export interface SwarmListItemInfo {
/** Servers part of the swarm */
@@ -5601,6 +5601,14 @@ export interface DiscordAlerterEndpoint {
/** The Discord webhook url */
url: string;
}
/** Standard docker lists available from a Server. */
export interface DockerLists {
containers: ContainerListItem[];
networks: NetworkListItem[];
images: ImageListItem[];
volumes: VolumeListItem[];
projects: ComposeProject[];
}
/** Driver represents a driver (network, logging, secrets). */
export interface Driver {
/** Name of the driver. */
@@ -6356,8 +6364,8 @@ export interface GetSwarmsSummaryResponse {
healthy: number;
/** The number of Swarms with Unhealthy state */
unhealthy: number;
/** The number of Swarms with Offline state */
offline: number;
/** The number of Swarms with Unknown state */
unknown: number;
}
/**
* Get the system information of the target server.
@@ -6498,6 +6506,13 @@ export interface InspectStackContainer {
/** The service name to inspect */
service: string;
}
/** JoinTokens contains the tokens workers and managers need to join the swarm. */
export interface JoinTokens {
/** The token workers can use to join the swarm. */
Worker?: string;
/** The token managers can use to join the swarm. */
Manager?: string;
}
export interface LatestCommit {
hash: string;
message: string;
@@ -8312,6 +8327,113 @@ export interface StopStack {
*/
services?: string[];
}
/** Orchestration configuration. */
export interface SwarmSpecOrchestration {
/**
* The number of historic tasks to keep per instance or node.
* If negative, never remove completed or failed tasks.
*/
TaskHistoryRetentionLimit?: I64;
}
/** Raft configuration. */
export interface SwarmSpecRaft {
/** The number of log entries between snapshots. */
SnapshotInterval?: U64;
/** The number of snapshots to keep beyond the current snapshot. */
KeepOldSnapshots?: U64;
/** The number of log entries to keep around to sync up slow followers after a snapshot is created. */
LogEntriesForSlowFollowers?: U64;
/** The number of ticks that a follower will wait for a message from the leader before becoming a candidate and starting an election. `ElectionTick` must be greater than `HeartbeatTick`. A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed. */
ElectionTick?: I64;
/**
* The number of ticks between heartbeats.
* Every HeartbeatTick ticks, the leader will send a heartbeat to the followers.
* A tick currently defaults to one second, so these translate directly to seconds currently, but this is NOT guaranteed.
*/
HeartbeatTick?: I64;
}
/** Dispatcher configuration. */
export interface SwarmSpecDispatcher {
/** The delay for an agent to send a heartbeat to the dispatcher. */
HeartbeatPeriod?: I64;
}
export declare enum SwarmSpecCaConfigExternalCasProtocolEnum {
EMPTY = "",
CFSSL = "cfssl"
}
export interface SwarmSpecCaConfigExternalCas {
/** Protocol for communication with the external CA (currently only `cfssl` is supported). */
Protocol?: SwarmSpecCaConfigExternalCasProtocolEnum;
/** URL where certificate signing requests should be sent. */
URL?: string;
/** An object with key/value pairs that are interpreted as protocol-specific options for the external CA driver. */
Options?: Record<string, string>;
/** The root CA certificate (in PEM format) this external CA uses to issue TLS certificates (assumed to be to the current swarm root CA certificate if not provided). */
CACert?: string;
}
/** CA configuration. */
export interface SwarmSpecCaConfig {
/** The duration node certificates are issued for. */
NodeCertExpiry?: I64;
/** Configuration for forwarding signing requests to an external certificate authority. */
ExternalCAs?: SwarmSpecCaConfigExternalCas[];
/** The desired signing CA certificate for all swarm node TLS leaf certificates, in PEM format. */
SigningCACert?: string;
/** The desired signing CA key for all swarm node TLS leaf certificates, in PEM format. */
SigningCAKey?: string;
/** An integer whose purpose is to force swarm to generate a new signing CA certificate and key, if none have been specified in `SigningCACert` and `SigningCAKey` */
ForceRotate?: U64;
}
/** Parameters related to encryption-at-rest. */
export interface SwarmSpecEncryptionConfig {
/** If set, generate a key and use it to lock data stored on the managers. */
AutoLockManagers?: boolean;
}
/** The log driver to use for tasks created in the orchestrator if unspecified by a service. Updating this value only affects new tasks. Existing tasks continue to use their previously configured log driver until recreated. */
export interface SwarmSpecTaskDefaultsLogDriver {
/** The log driver to use as a default for new tasks. */
Name?: string;
/** Driver-specific options for the selected log driver, specified as key/value pairs. */
Options?: Record<string, string>;
}
/** Defaults for creating tasks in this cluster. */
export interface SwarmSpecTaskDefaults {
LogDriver?: SwarmSpecTaskDefaultsLogDriver;
}
/** User modifiable swarm configuration. */
export interface SwarmSpec {
/** Name of the swarm. */
Name?: string;
/** User-defined key/value metadata. */
Labels?: Record<string, string>;
Orchestration?: SwarmSpecOrchestration;
Raft?: SwarmSpecRaft;
Dispatcher?: SwarmSpecDispatcher;
CAConfig?: SwarmSpecCaConfig;
EncryptionConfig?: SwarmSpecEncryptionConfig;
TaskDefaults?: SwarmSpecTaskDefaults;
}
/** Docker-level information about the Swarm. */
export interface SwarmInspectInfo {
/** The (Docker) ID of the swarm. */
ID?: string;
Version?: ObjectVersion;
/** Date and time at which the swarm was initialised in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
CreatedAt?: string;
/** Date and time at which the swarm was last updated in [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format with nano-seconds. */
UpdatedAt?: string;
Spec?: SwarmSpec;
TLSInfo?: TlsInfo;
/** Whether there is currently a root CA rotation in progress for the swarm */
RootRotationInProgress?: boolean;
/** DataPathPort specifies the data path port number for data traffic. Acceptable port range is 1024 to 49151. If no port is set or is set to 0, the default port (4789) is used. */
DataPathPort?: number;
/** Default Address Pool specifies default subnet pools for global scope networks. */
DefaultAddrPool?: string[];
/** SubnetSize specifies the subnet size of the networks created from the default subnet pool. */
SubnetSize?: number;
JoinTokens?: JoinTokens;
}
/** Swarm node details. */
export interface SwarmNode {
ID?: string;
@@ -9074,15 +9196,15 @@ export type ExecuteRequest = {
} | {
type: "BatchRunAction";
params: BatchRunAction;
} | {
type: "RunSync";
params: RunSync;
} | {
type: "TestAlerter";
params: TestAlerter;
} | {
type: "SendAlert";
params: SendAlert;
} | {
type: "RunSync";
params: RunSync;
} | {
type: "ClearRepoCache";
params: ClearRepoCache;
@@ -9200,68 +9322,20 @@ export type ReadRequest = {
type: "ListDockerRegistriesFromConfig";
params: ListDockerRegistriesFromConfig;
} | {
type: "GetUsername";
params: GetUsername;
type: "GetSwarmsSummary";
params: GetSwarmsSummary;
} | {
type: "GetPermission";
params: GetPermission;
type: "GetSwarm";
params: GetSwarm;
} | {
type: "FindUser";
params: FindUser;
type: "GetSwarmActionState";
params: GetSwarmActionState;
} | {
type: "ListUsers";
params: ListUsers;
type: "ListSwarms";
params: ListSwarms;
} | {
type: "ListApiKeys";
params: ListApiKeys;
} | {
type: "ListApiKeysForServiceUser";
params: ListApiKeysForServiceUser;
} | {
type: "ListPermissions";
params: ListPermissions;
} | {
type: "ListUserTargetPermissions";
params: ListUserTargetPermissions;
} | {
type: "GetUserGroup";
params: GetUserGroup;
} | {
type: "ListUserGroups";
params: ListUserGroups;
} | {
type: "GetProceduresSummary";
params: GetProceduresSummary;
} | {
type: "GetProcedure";
params: GetProcedure;
} | {
type: "GetProcedureActionState";
params: GetProcedureActionState;
} | {
type: "ListProcedures";
params: ListProcedures;
} | {
type: "ListFullProcedures";
params: ListFullProcedures;
} | {
type: "GetActionsSummary";
params: GetActionsSummary;
} | {
type: "GetAction";
params: GetAction;
} | {
type: "GetActionActionState";
params: GetActionActionState;
} | {
type: "ListActions";
params: ListActions;
} | {
type: "ListFullActions";
params: ListFullActions;
} | {
type: "ListSchedules";
params: ListSchedules;
type: "ListFullSwarms";
params: ListFullSwarms;
} | {
type: "GetServersSummary";
params: GetServersSummary;
@@ -9277,9 +9351,6 @@ export type ReadRequest = {
} | {
type: "GetServerActionState";
params: GetServerActionState;
} | {
type: "GetHistoricalServerStats";
params: GetHistoricalServerStats;
} | {
type: "ListServers";
params: ListServers;
@@ -9340,6 +9411,9 @@ export type ReadRequest = {
} | {
type: "GetSystemStats";
params: GetSystemStats;
} | {
type: "GetHistoricalServerStats";
params: GetHistoricalServerStats;
} | {
type: "ListSystemProcesses";
params: ListSystemProcesses;
@@ -9448,6 +9522,39 @@ export type ReadRequest = {
} | {
type: "ListFullRepos";
params: ListFullRepos;
} | {
type: "GetProceduresSummary";
params: GetProceduresSummary;
} | {
type: "GetProcedure";
params: GetProcedure;
} | {
type: "GetProcedureActionState";
params: GetProcedureActionState;
} | {
type: "ListProcedures";
params: ListProcedures;
} | {
type: "ListFullProcedures";
params: ListFullProcedures;
} | {
type: "GetActionsSummary";
params: GetActionsSummary;
} | {
type: "GetAction";
params: GetAction;
} | {
type: "GetActionActionState";
params: GetActionActionState;
} | {
type: "ListActions";
params: ListActions;
} | {
type: "ListFullActions";
params: ListFullActions;
} | {
type: "ListSchedules";
params: ListSchedules;
} | {
type: "GetResourceSyncsSummary";
params: GetResourceSyncsSummary;
@@ -9499,6 +9606,36 @@ export type ReadRequest = {
} | {
type: "ListTags";
params: ListTags;
} | {
type: "GetUsername";
params: GetUsername;
} | {
type: "GetPermission";
params: GetPermission;
} | {
type: "FindUser";
params: FindUser;
} | {
type: "ListUsers";
params: ListUsers;
} | {
type: "ListApiKeys";
params: ListApiKeys;
} | {
type: "ListApiKeysForServiceUser";
params: ListApiKeysForServiceUser;
} | {
type: "ListPermissions";
params: ListPermissions;
} | {
type: "ListUserTargetPermissions";
params: ListUserTargetPermissions;
} | {
type: "GetUserGroup";
params: GetUserGroup;
} | {
type: "ListUserGroups";
params: ListUserGroups;
} | {
type: "GetUpdate";
params: GetUpdate;
@@ -9598,65 +9735,23 @@ export type UserRequest = {
params: DeleteApiKey;
};
export type WriteRequest = {
type: "CreateLocalUser";
params: CreateLocalUser;
} | {
type: "UpdateUserUsername";
params: UpdateUserUsername;
} | {
type: "UpdateUserPassword";
params: UpdateUserPassword;
} | {
type: "DeleteUser";
params: DeleteUser;
} | {
type: "CreateServiceUser";
params: CreateServiceUser;
} | {
type: "UpdateServiceUserDescription";
params: UpdateServiceUserDescription;
} | {
type: "CreateApiKeyForServiceUser";
params: CreateApiKeyForServiceUser;
} | {
type: "DeleteApiKeyForServiceUser";
params: DeleteApiKeyForServiceUser;
} | {
type: "CreateUserGroup";
params: CreateUserGroup;
} | {
type: "RenameUserGroup";
params: RenameUserGroup;
} | {
type: "DeleteUserGroup";
params: DeleteUserGroup;
} | {
type: "AddUserToUserGroup";
params: AddUserToUserGroup;
} | {
type: "RemoveUserFromUserGroup";
params: RemoveUserFromUserGroup;
} | {
type: "SetUsersInUserGroup";
params: SetUsersInUserGroup;
} | {
type: "SetEveryoneUserGroup";
params: SetEveryoneUserGroup;
} | {
type: "UpdateUserAdmin";
params: UpdateUserAdmin;
} | {
type: "UpdateUserBasePermissions";
params: UpdateUserBasePermissions;
} | {
type: "UpdatePermissionOnResourceType";
params: UpdatePermissionOnResourceType;
} | {
type: "UpdatePermissionOnTarget";
params: UpdatePermissionOnTarget;
} | {
type: "UpdateResourceMeta";
params: UpdateResourceMeta;
} | {
type: "CreateSwarm";
params: CreateSwarm;
} | {
type: "CopySwarm";
params: CopySwarm;
} | {
type: "DeleteSwarm";
params: DeleteSwarm;
} | {
type: "UpdateSwarm";
params: UpdateSwarm;
} | {
type: "RenameSwarm";
params: RenameSwarm;
} | {
type: "CreateServer";
params: CreateServer;
@@ -9681,6 +9776,18 @@ export type WriteRequest = {
} | {
type: "RotateServerKeys";
params: RotateServerKeys;
} | {
type: "CreateTerminal";
params: CreateTerminal;
} | {
type: "DeleteTerminal";
params: DeleteTerminal;
} | {
type: "DeleteAllTerminals";
params: DeleteAllTerminals;
} | {
type: "BatchDeleteAllTerminals";
params: BatchDeleteAllTerminals;
} | {
type: "CreateStack";
params: CreateStack;
@@ -9741,21 +9848,6 @@ export type WriteRequest = {
} | {
type: "RefreshBuildCache";
params: RefreshBuildCache;
} | {
type: "CreateBuilder";
params: CreateBuilder;
} | {
type: "CopyBuilder";
params: CopyBuilder;
} | {
type: "DeleteBuilder";
params: DeleteBuilder;
} | {
type: "UpdateBuilder";
params: UpdateBuilder;
} | {
type: "RenameBuilder";
params: RenameBuilder;
} | {
type: "CreateRepo";
params: CreateRepo;
@@ -9774,21 +9866,6 @@ export type WriteRequest = {
} | {
type: "RefreshRepoCache";
params: RefreshRepoCache;
} | {
type: "CreateAlerter";
params: CreateAlerter;
} | {
type: "CopyAlerter";
params: CopyAlerter;
} | {
type: "DeleteAlerter";
params: DeleteAlerter;
} | {
type: "UpdateAlerter";
params: UpdateAlerter;
} | {
type: "RenameAlerter";
params: RenameAlerter;
} | {
type: "CreateProcedure";
params: CreateProcedure;
@@ -9844,17 +9921,101 @@ export type WriteRequest = {
type: "RefreshResourceSyncPending";
params: RefreshResourceSyncPending;
} | {
type: "CreateTerminal";
params: CreateTerminal;
type: "CreateBuilder";
params: CreateBuilder;
} | {
type: "DeleteTerminal";
params: DeleteTerminal;
type: "CopyBuilder";
params: CopyBuilder;
} | {
type: "DeleteAllTerminals";
params: DeleteAllTerminals;
type: "DeleteBuilder";
params: DeleteBuilder;
} | {
type: "BatchDeleteAllTerminals";
params: BatchDeleteAllTerminals;
type: "UpdateBuilder";
params: UpdateBuilder;
} | {
type: "RenameBuilder";
params: RenameBuilder;
} | {
type: "CreateAlerter";
params: CreateAlerter;
} | {
type: "CopyAlerter";
params: CopyAlerter;
} | {
type: "DeleteAlerter";
params: DeleteAlerter;
} | {
type: "UpdateAlerter";
params: UpdateAlerter;
} | {
type: "RenameAlerter";
params: RenameAlerter;
} | {
type: "CreateOnboardingKey";
params: CreateOnboardingKey;
} | {
type: "UpdateOnboardingKey";
params: UpdateOnboardingKey;
} | {
type: "DeleteOnboardingKey";
params: DeleteOnboardingKey;
} | {
type: "CreateLocalUser";
params: CreateLocalUser;
} | {
type: "UpdateUserUsername";
params: UpdateUserUsername;
} | {
type: "UpdateUserPassword";
params: UpdateUserPassword;
} | {
type: "DeleteUser";
params: DeleteUser;
} | {
type: "CreateServiceUser";
params: CreateServiceUser;
} | {
type: "UpdateServiceUserDescription";
params: UpdateServiceUserDescription;
} | {
type: "CreateApiKeyForServiceUser";
params: CreateApiKeyForServiceUser;
} | {
type: "DeleteApiKeyForServiceUser";
params: DeleteApiKeyForServiceUser;
} | {
type: "CreateUserGroup";
params: CreateUserGroup;
} | {
type: "RenameUserGroup";
params: RenameUserGroup;
} | {
type: "DeleteUserGroup";
params: DeleteUserGroup;
} | {
type: "AddUserToUserGroup";
params: AddUserToUserGroup;
} | {
type: "RemoveUserFromUserGroup";
params: RemoveUserFromUserGroup;
} | {
type: "SetUsersInUserGroup";
params: SetUsersInUserGroup;
} | {
type: "SetEveryoneUserGroup";
params: SetEveryoneUserGroup;
} | {
type: "UpdateUserAdmin";
params: UpdateUserAdmin;
} | {
type: "UpdateUserBasePermissions";
params: UpdateUserBasePermissions;
} | {
type: "UpdatePermissionOnResourceType";
params: UpdatePermissionOnResourceType;
} | {
type: "UpdatePermissionOnTarget";
params: UpdatePermissionOnTarget;
} | {
type: "CreateTag";
params: CreateTag;
@@ -9900,15 +10061,6 @@ export type WriteRequest = {
} | {
type: "DeleteDockerRegistryAccount";
params: DeleteDockerRegistryAccount;
} | {
type: "CreateOnboardingKey";
params: CreateOnboardingKey;
} | {
type: "UpdateOnboardingKey";
params: UpdateOnboardingKey;
} | {
type: "DeleteOnboardingKey";
params: DeleteOnboardingKey;
} | {
type: "CloseAlert";
params: CloseAlert;

View File

@@ -580,8 +580,6 @@ export var SwarmState;
SwarmState["Healthy"] = "Healthy";
/** The Swarm is unhealthy */
SwarmState["Unhealthy"] = "Unhealthy";
/** Servers are reachable, but Swarm is not running on any of them. */
SwarmState["Offline"] = "Offline";
})(SwarmState || (SwarmState = {}));
/**
* Configures the behavior of [CreateTerminal] if the
@@ -734,6 +732,11 @@ export var ServiceUpdateStatusStateEnum;
ServiceUpdateStatusStateEnum["ROLLBACK_PAUSED"] = "rollback_paused";
ServiceUpdateStatusStateEnum["ROLLBACK_COMPLETED"] = "rollback_completed";
})(ServiceUpdateStatusStateEnum || (ServiceUpdateStatusStateEnum = {}));
export var SwarmSpecCaConfigExternalCasProtocolEnum;
(function (SwarmSpecCaConfigExternalCasProtocolEnum) {
SwarmSpecCaConfigExternalCasProtocolEnum["EMPTY"] = "";
SwarmSpecCaConfigExternalCasProtocolEnum["CFSSL"] = "cfssl";
})(SwarmSpecCaConfigExternalCasProtocolEnum || (SwarmSpecCaConfigExternalCasProtocolEnum = {}));
export var TaskState;
(function (TaskState) {
TaskState["NEW"] = "new";

View File

@@ -46,7 +46,7 @@ export const BuildComponents: RequiredResourceComponents = {
list_item: (id) => useBuild(id),
resource_links: (resource) => (resource.config as Types.BuildConfig).links,
Description: () => <>Build docker images.</>,
Description: () => <>Build container images.</>,
Dashboard: () => {
const summary = useRead("GetBuildsSummary", {}).data;

View File

@@ -299,6 +299,7 @@ export const ResourceSelector = ({
placeholder,
targetClassName,
state,
exclude_ids,
}: {
type: UsableResource;
selected: string | undefined;
@@ -309,6 +310,7 @@ export const ResourceSelector = ({
placeholder?: string;
targetClassName?: string;
state?: unknown;
exclude_ids?: string[];
}) => {
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");
@@ -321,7 +323,11 @@ export const ResourceSelector = ({
: () => true;
const resources = useRead(`List${type}s`, {})
.data?.filter(templateFilterFn)
.filter((r) => !state || (r.info as any).state === state);
.filter(
(r) =>
(!state || (r.info as any).state === state) &&
(!exclude_ids || r.id === selected || !exclude_ids?.includes(r.id))
);
const name = resources?.find((r) => r.id === selected)?.name;
if (!resources) return null;

View File

@@ -1,18 +1,20 @@
import { RequiredResourceComponents, UsableResource } from "@types";
import { AlerterComponents } from "./alerter";
import { BuildComponents } from "./build";
import { BuilderComponents } from "./builder";
import { DeploymentComponents } from "./deployment";
import { RepoComponents } from "./repo";
import { SwarmComponents } from "./swarm";
import { ServerComponents } from "./server";
import { ProcedureComponents } from "./procedure/index";
import { ResourceSyncComponents } from "./sync";
import { StackComponents } from "./stack";
import { DeploymentComponents } from "./deployment";
import { BuildComponents } from "./build";
import { RepoComponents } from "./repo";
import { ProcedureComponents } from "./procedure/index";
import { ActionComponents } from "./action";
import { BuilderComponents } from "./builder";
import { AlerterComponents } from "./alerter";
import { ResourceSyncComponents } from "./sync";
export const ResourceComponents: {
[key in UsableResource]: RequiredResourceComponents;
} = {
Swarm: SwarmComponents,
Server: ServerComponents,
Stack: StackComponents,
Deployment: DeploymentComponents,

View File

@@ -0,0 +1,107 @@
import { Config } from "@components/config";
import { ConfigItem } from "@components/config/util";
import { useLocalStorage, usePermissions, useRead, useWrite } from "@lib/hooks";
import { Types } from "komodo_client";
import { ResourceSelector } from "../common";
import { Button } from "@ui/button";
import { MinusCircle, PlusCircle } from "lucide-react";
export const SwarmConfig = ({ id }: { id: string }) => {
const { canWrite } = usePermissions({ type: "Swarm", id });
const swarm = useRead("GetSwarm", { swarm: id }).data;
const config = swarm?.config;
const global_disabled =
useRead("GetCoreInfo", {}).data?.ui_write_disabled ?? false;
const [update, set] = useLocalStorage<Partial<Types.SwarmConfig>>(
`swarm-${id}-update-v1`,
{}
);
const { mutateAsync } = useWrite("UpdateSwarm");
if (!config) return null;
const disabled = global_disabled || !canWrite;
return (
<Config
disabled={disabled}
original={config}
update={update}
set={set}
onSave={async () => {
await mutateAsync({ id, config: update });
}}
components={{
"": [
{
label: "Managers",
labelHidden: true,
components: {
server_ids: (server_ids, set) => {
return (
<ConfigItem
label="Manager Nodes"
description="Select the Servers which have joined the Swarm as Manager Nodes."
>
<div className="flex flex-col gap-4 w-full">
{server_ids?.map((server_id, index) => (
<div key={index} className="w-full flex gap-4">
<ResourceSelector
type="Server"
exclude_ids={server_ids}
selected={server_id}
onSelect={(server_id) =>
set({
server_ids: [
...server_ids.map((id, i) =>
i === index ? server_id : id
),
],
})
}
disabled={disabled}
align="start"
/>
{!disabled && (
<Button
variant="secondary"
onClick={() =>
set({
server_ids: [
...server_ids?.filter(
(_, i) => i !== index
),
],
})
}
>
<MinusCircle className="w-4 h-4" />
</Button>
)}
</div>
))}
{!disabled && (
<Button
variant="secondary"
onClick={() =>
set({
server_ids: [...(server_ids ?? []), ""],
})
}
className="flex items-center gap-2 w-[200px]"
>
<PlusCircle className="w-4 h-4" />
Add Server
</Button>
)}
</div>
</ConfigItem>
);
},
},
},
],
}}
/>
);
};

View File

@@ -0,0 +1,101 @@
import { useRead } from "@lib/hooks";
import { RequiredResourceComponents } from "@types";
import { Boxes } from "lucide-react";
import { SwarmConfig } from "./config";
import { DeleteResource, NewResource, ResourcePageHeader } from "../common";
import { SwarmTable } from "./table";
import {
swarm_state_intention,
stroke_color_class_by_intention,
} from "@lib/color";
import { cn } from "@lib/utils";
import { Types } from "komodo_client";
import { DashboardPieChart } from "@components/util";
import { StatusBadge } from "@components/util";
import { GroupActions } from "@components/group-actions";
export const useSwarm = (id?: string) =>
useRead("ListSwarms", {}, { refetchInterval: 10_000 }).data?.find(
(d) => d.id === id
);
export const useFullSwarm = (id: string) =>
useRead("GetSwarm", { swarm: id }, { refetchInterval: 10_000 }).data;
const SwarmIcon = ({ id, size }: { id?: string; size: number }) => {
const state = useSwarm(id)?.info.state;
const color = stroke_color_class_by_intention(swarm_state_intention(state));
return <Boxes className={cn(`w-${size} h-${size}`, state && color)} />;
};
export const SwarmComponents: RequiredResourceComponents = {
list_item: (id) => useSwarm(id),
resource_links: (resource) => (resource.config as Types.SwarmConfig).links,
Description: () => <>Control and monitor docker swarms.</>,
Dashboard: () => {
const summary = useRead("GetSwarmsSummary", {}).data;
return (
<DashboardPieChart
data={[
{ intention: "Good", value: summary?.healthy ?? 0, title: "Healthy" },
{
intention: "Critical",
value: summary?.unhealthy ?? 0,
title: "Unhealthy",
},
{
intention: "Unknown",
value: summary?.unknown ?? 0,
title: "Unknown",
},
]}
/>
);
},
New: () => <NewResource type="Swarm" />,
GroupActions: () => <GroupActions type="Swarm" actions={[]} />,
Table: ({ resources }) => (
<SwarmTable swarms={resources as Types.SwarmListItem[]} />
),
Icon: ({ id }) => <SwarmIcon id={id} size={4} />,
BigIcon: ({ id }) => <SwarmIcon id={id} size={8} />,
State: ({ id }) => {
const state = useSwarm(id)?.info.state;
return <StatusBadge text={state} intent={swarm_state_intention(state)} />;
},
Info: {},
Status: {},
Actions: {},
Page: {},
Config: SwarmConfig,
DangerZone: ({ id }) => <DeleteResource type="Swarm" id={id} />,
ResourcePageHeader: ({ id }) => {
const swarm = useSwarm(id);
return (
<ResourcePageHeader
intent={swarm_state_intention(swarm?.info.state)}
icon={<SwarmIcon id={id} size={8} />}
type="Swarm"
id={id}
resource={swarm}
state={swarm?.info.state}
status=""
/>
);
},
};

View File

@@ -0,0 +1,43 @@
import { DataTable, SortableHeader } from "@ui/data-table";
import { ResourceLink } from "../common";
import { TableTags } from "@components/tags";
import { SwarmComponents } from ".";
import { Types } from "komodo_client";
import { useSelectedResources } from "@lib/hooks";
export const SwarmTable = ({ swarms }: { swarms: Types.SwarmListItem[] }) => {
const [_, setSelectedResources] = useSelectedResources("Swarm");
return (
<DataTable
tableKey="swarms"
data={swarms}
selectOptions={{
selectKey: ({ name }) => name,
onSelect: setSelectedResources,
}}
columns={[
{
header: ({ column }) => (
<SortableHeader column={column} title="Name" />
),
accessorKey: "name",
cell: ({ row }) => <ResourceLink type="Swarm" id={row.original.id} />,
size: 200,
},
{
header: ({ column }) => (
<SortableHeader column={column} title="State" />
),
accessorKey: "info.state",
cell: ({ row }) => <SwarmComponents.State id={row.original.id} />,
size: 120,
},
{
header: "Tags",
cell: ({ row }) => <TableTags tag_ids={row.original.tags} />,
},
]}
/>
);
};

View File

@@ -129,6 +129,23 @@ export const soft_text_color_class_by_intention = (
}
};
export const swarm_state_intention: (
state?: Types.SwarmState,
hasVersionMismatch?: boolean
) => ColorIntention = (state, hasVersionMismatch) => {
switch (state) {
case Types.SwarmState.Healthy:
// If there's a version mismatch and the server is "Ok", show warning instead
return hasVersionMismatch ? "Warning" : "Good";
case Types.SwarmState.Unhealthy:
return "Critical";
case Types.SwarmState.Unknown:
return "Neutral";
case undefined:
return "None";
}
};
export const server_state_intention: (
state?: Types.ServerState,
hasVersionMismatch?: boolean
@@ -146,6 +163,29 @@ export const server_state_intention: (
}
};
export const stack_state_intention = (state?: Types.StackState) => {
switch (state) {
case undefined:
return "None";
case Types.StackState.Deploying:
return "Warning";
case Types.StackState.Running:
return "Good";
case Types.StackState.Paused:
return "Warning";
case Types.StackState.Stopped:
return "Critical";
case Types.StackState.Restarting:
return "Critical";
case Types.StackState.Down:
return "Neutral";
case Types.StackState.Unknown:
return "Unknown";
default:
return "Critical";
}
};
export const deployment_state_intention: (
state?: Types.DeploymentState
) => ColorIntention = (state) => {
@@ -222,29 +262,6 @@ export const repo_state_intention = (state?: Types.RepoState) => {
}
};
export const stack_state_intention = (state?: Types.StackState) => {
switch (state) {
case undefined:
return "None";
case Types.StackState.Deploying:
return "Warning";
case Types.StackState.Running:
return "Good";
case Types.StackState.Paused:
return "Warning";
case Types.StackState.Stopped:
return "Critical";
case Types.StackState.Restarting:
return "Critical";
case Types.StackState.Down:
return "Neutral";
case Types.StackState.Unknown:
return "Unknown";
default:
return "Critical";
}
};
export const procedure_state_intention = (status?: Types.ProcedureState) => {
switch (status) {
case undefined:

View File

@@ -364,6 +364,7 @@ type ResourceMap = {
export const useAllResources = (): ResourceMap => {
return {
Swarm: useRead("ListSwarms", {}).data,
Server: useRead("ListServers", {}).data,
Stack: useRead("ListStacks", {}).data,
Deployment: useRead("ListDeployments", {}).data,

View File

@@ -126,12 +126,14 @@ const on_update = (
// Invalidate these every time
invalidate(["ListUpdates"]);
invalidate(["GetUpdate", { id: update.id }]);
if (update.target.type === "Deployment") {
invalidate(["GetDeploymentActionState", { deployment: update.target.id }]);
} else if (update.target.type === "Stack") {
invalidate(["GetStackActionState", { stack: update.target.id }]);
if (update.target.type === "Swarm") {
invalidate(["GetSwarmActionState", { swarm: update.target.id }]);
} else if (update.target.type === "Server") {
invalidate(["GetServerActionState", { server: update.target.id }]);
} else if (update.target.type === "Stack") {
invalidate(["GetStackActionState", { stack: update.target.id }]);
} else if (update.target.type === "Deployment") {
invalidate(["GetDeploymentActionState", { deployment: update.target.id }]);
} else if (update.target.type === "Build") {
invalidate(["GetBuildActionState", { build: update.target.id }]);
} else if (update.target.type === "Repo") {
@@ -144,7 +146,7 @@ const on_update = (
invalidate(["GetResourceSyncActionState", { sync: update.target.id }]);
}
// Invalidate lists for execution updates - update status
// Invalidate lists for execution updates - this updates status
if (update.operation === Types.Operation.RunBuild) {
invalidate(["ListBuilds"]);
} else if (
@@ -180,18 +182,23 @@ const on_update = (
);
}
if (update.target.type === "Deployment") {
if (update.target.type === "Swarm") {
invalidate(
["ListDeployments"],
["GetDeploymentsSummary"],
["ListDockerContainers"],
["ListDockerNetworks"],
["ListDockerImages"],
["GetDeployment"],
["GetDeploymentLog", { deployment: update.target.id }],
["SearchDeploymentLog", { deployment: update.target.id }],
["GetDeploymentContainer"],
["GetResourceMatchingContainer"]
["ListSwarms"],
["ListFullSwarms"],
["GetSwarmsSummary"],
["GetSwarm"]
);
}
if (update.target.type === "Server") {
invalidate(
["ListServers"],
["ListFullServers"],
["GetServersSummary"],
["GetServer"],
["GetServerState"],
["GetHistoricalServerStats"]
);
}
@@ -213,14 +220,18 @@ const on_update = (
);
}
if (update.target.type === "Server") {
if (update.target.type === "Deployment") {
invalidate(
["ListServers"],
["ListFullServers"],
["GetServersSummary"],
["GetServer"],
["GetServerState"],
["GetHistoricalServerStats"]
["ListDeployments"],
["GetDeploymentsSummary"],
["ListDockerContainers"],
["ListDockerNetworks"],
["ListDockerImages"],
["GetDeployment"],
["GetDeploymentLog", { deployment: update.target.id }],
["SearchDeploymentLog", { deployment: update.target.id }],
["GetDeploymentContainer"],
["GetResourceMatchingContainer"]
);
}
@@ -264,6 +275,15 @@ const on_update = (
);
}
if (update.target.type === "ResourceSync") {
invalidate(
["ListResourceSyncs"],
["ListFullResourceSyncs"],
["GetResourceSyncsSummary"],
["GetResourceSync"]
);
}
if (update.target.type === "Builder") {
invalidate(
["ListBuilders"],
@@ -282,15 +302,6 @@ const on_update = (
);
}
if (update.target.type === "ResourceSync") {
invalidate(
["ListResourceSyncs"],
["ListFullResourceSyncs"],
["GetResourceSyncsSummary"],
["GetResourceSync"]
);
}
if (
update.target.type === "System" &&
update.operation.includes("Variable")

View File

@@ -14,6 +14,7 @@ export const object_keys = <T extends object>(o: T): (keyof T)[] =>
Object.keys(o) as (keyof T)[];
export const RESOURCE_TARGETS: UsableResource[] = [
"Swarm",
"Server",
"Stack",
"Deployment",