swarm service logs api

This commit is contained in:
mbecker20
2025-11-23 00:43:59 -08:00
parent b1fec7c663
commit 9c86b2d239
11 changed files with 478 additions and 53 deletions

View File

@@ -85,16 +85,18 @@ enum ReadRequest {
ListFullSwarms(ListFullSwarms),
ListSwarmNodes(ListSwarmNodes),
InspectSwarmNode(InspectSwarmNode),
ListSwarmServices(ListSwarmServices),
InspectSwarmService(InspectSwarmService),
ListSwarmTasks(ListSwarmTasks),
InspectSwarmTask(InspectSwarmTask),
ListSwarmSecrets(ListSwarmSecrets),
InspectSwarmSecret(InspectSwarmSecret),
ListSwarmConfigs(ListSwarmConfigs),
InspectSwarmConfig(InspectSwarmConfig),
ListSwarmSecrets(ListSwarmSecrets),
InspectSwarmSecret(InspectSwarmSecret),
ListSwarmStacks(ListSwarmStacks),
InspectSwarmStack(InspectSwarmStack),
ListSwarmTasks(ListSwarmTasks),
InspectSwarmTask(InspectSwarmTask),
ListSwarmServices(ListSwarmServices),
InspectSwarmService(InspectSwarmService),
GetSwarmServiceLog(GetSwarmServiceLog),
SearchSwarmServiceLog(SearchSwarmServiceLog),
// ==== SERVER ====
GetServersSummary(GetServersSummary),

View File

@@ -249,6 +249,62 @@ impl Resolve<ReadArgs> for InspectSwarmService {
}
}
impl Resolve<ReadArgs> for GetSwarmServiceLog {
async fn resolve(
self,
ReadArgs { user }: &ReadArgs,
) -> serror::Result<GetSwarmServiceLogResponse> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
user,
PermissionLevel::Read.logs(),
)
.await?;
swarm_request(
&swarm.config.server_ids,
periphery_client::api::swarm::GetSwarmServiceLog {
service: self.service,
tail: self.tail,
timestamps: self.timestamps,
no_task_ids: self.no_task_ids,
no_resolve: self.no_resolve,
details: self.details,
},
)
.await
.map_err(Into::into)
}
}
impl Resolve<ReadArgs> for SearchSwarmServiceLog {
async fn resolve(
self,
ReadArgs { user }: &ReadArgs,
) -> serror::Result<SearchSwarmServiceLogResponse> {
let swarm = get_check_permissions::<Swarm>(
&self.swarm,
user,
PermissionLevel::Read.logs(),
)
.await?;
swarm_request(
&swarm.config.server_ids,
periphery_client::api::swarm::GetSwarmServiceLogSearch {
service: self.service,
terms: self.terms,
combinator: self.combinator,
invert: self.invert,
timestamps: self.timestamps,
no_task_ids: self.no_task_ids,
no_resolve: self.no_resolve,
details: self.details,
},
)
.await
.map_err(Into::into)
}
}
impl Resolve<ReadArgs> for ListSwarmTasks {
async fn resolve(
self,

View File

@@ -55,7 +55,7 @@ impl Resolve<super::Args> for GetComposeLog {
services.join(" ")
);
Ok(
run_komodo_standard_command("get stack log", None, command)
run_komodo_standard_command("Get Stack Log", None, command)
.await,
)
}
@@ -83,7 +83,7 @@ impl Resolve<super::Args> for GetComposeLogSearch {
services.join(" ")
);
Ok(
run_komodo_shell_command("Get stack log grep", None, command)
run_komodo_shell_command("Search Stack Log", None, command)
.await,
)
}

View File

@@ -139,11 +139,13 @@ pub enum PeripheryRequest {
// Swarm
PollSwarmStatus(PollSwarmStatus),
InspectSwarmNode(InspectSwarmNode),
InspectSwarmService(InspectSwarmService),
InspectSwarmTask(InspectSwarmTask),
InspectSwarmSecret(InspectSwarmSecret),
InspectSwarmConfig(InspectSwarmConfig),
InspectSwarmSecret(InspectSwarmSecret),
InspectSwarmStack(InspectSwarmStack),
InspectSwarmTask(InspectSwarmTask),
InspectSwarmService(InspectSwarmService),
GetSwarmServiceLog(GetSwarmServiceLog),
GetSwarmServiceLogSearch(GetSwarmServiceLogSearch),
// Terminal
ListTerminals(ListTerminals),

View File

@@ -1,8 +1,12 @@
use anyhow::Context as _;
use komodo_client::entities::docker::{
SwarmLists, config::SwarmConfig, node::SwarmNode,
secret::SwarmSecret, service::SwarmService, stack::SwarmStackLists,
task::SwarmTask,
use command::run_komodo_standard_command;
use komodo_client::entities::{
docker::{
SwarmLists, config::SwarmConfig, node::SwarmNode,
secret::SwarmSecret, service::SwarmService,
stack::SwarmStackLists, task::SwarmTask,
},
update::Log,
};
use periphery_client::api::swarm::*;
use resolver_api::Resolve;
@@ -12,6 +16,7 @@ use crate::{
config::{inspect_swarm_config, list_swarm_configs},
stack::{inspect_swarm_stack, list_swarm_stacks},
},
helpers::format_log_grep,
state::docker_client,
};
@@ -84,6 +89,71 @@ impl Resolve<super::Args> for InspectSwarmService {
}
}
impl Resolve<super::Args> for GetSwarmServiceLog {
async fn resolve(self, _: &super::Args) -> anyhow::Result<Log> {
let GetSwarmServiceLog {
service,
tail,
timestamps,
no_task_ids,
no_resolve,
details,
} = self;
let timestamps =
timestamps.then_some(" --timestamps").unwrap_or_default();
let no_task_ids =
no_task_ids.then_some(" --no-task-ids").unwrap_or_default();
let no_resolve =
no_resolve.then_some(" --no-resolve").unwrap_or_default();
let details = details.then_some(" --details").unwrap_or_default();
let command = format!(
"docker service logs --tail {tail}{timestamps}{no_task_ids}{no_resolve}{details} {service}",
);
Ok(
run_komodo_standard_command(
"Get Swarm Service Log",
None,
command,
)
.await,
)
}
}
impl Resolve<super::Args> for GetSwarmServiceLogSearch {
async fn resolve(self, _: &super::Args) -> anyhow::Result<Log> {
let GetSwarmServiceLogSearch {
service,
terms,
combinator,
invert,
timestamps,
no_task_ids,
no_resolve,
details,
} = self;
let timestamps =
timestamps.then_some(" --timestamps").unwrap_or_default();
let no_task_ids =
no_task_ids.then_some(" --no-task-ids").unwrap_or_default();
let no_resolve =
no_resolve.then_some(" --no-resolve").unwrap_or_default();
let details = details.then_some(" --details").unwrap_or_default();
let grep = format_log_grep(&terms, combinator, invert);
let command = format!(
"docker service logs --tail 5000{timestamps}{no_task_ids}{no_resolve}{details} {service} 2>&1 | {grep}",
);
Ok(
run_komodo_standard_command(
"Search Swarm Service Log",
None,
command,
)
.await,
)
}
}
// ======
// Task
// ======

View File

@@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
use typeshare::typeshare;
use crate::entities::{
SearchCombinator, U64,
docker::{
config::{SwarmConfig, SwarmConfigListItem},
node::{SwarmNode, SwarmNodeListItem},
@@ -14,6 +15,7 @@ use crate::entities::{
task::{SwarmTask, SwarmTaskListItem},
},
swarm::{Swarm, SwarmActionState, SwarmListItem, SwarmQuery},
update::Log,
};
use super::KomodoReadRequest;
@@ -227,6 +229,97 @@ pub type InspectSwarmServiceResponse = SwarmService;
//
/// Get a swarm service's logs. Response: [GetSwarmServiceLogResponse].
///
/// Note. This call will hit the underlying server directly for most up to date log.
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
)]
#[empty_traits(KomodoReadRequest)]
#[response(GetSwarmServiceLogResponse)]
#[error(serror::Error)]
pub struct GetSwarmServiceLog {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub swarm: String,
/// Select the swarm service to get logs for.
pub service: String,
/// The number of lines of the log tail to include.
/// Default: 100.
/// Max: 5000.
#[serde(default = "default_tail")]
pub tail: U64,
/// Enable `--timestamps`
#[serde(default)]
pub timestamps: bool,
/// Enable `--no-task-ids`
#[serde(default)]
pub no_task_ids: bool,
/// Enable `--no-resolve`
#[serde(default)]
pub no_resolve: bool,
/// Enable `--details`
#[serde(default)]
pub details: bool,
}
fn default_tail() -> u64 {
50
}
#[typeshare]
pub type GetSwarmServiceLogResponse = Log;
//
/// Search the swarm service log's tail using `grep`. All lines go to stdout.
/// Response: [SearchSwarmServiceLogResponse].
///
/// Note. This call will hit the underlying server directly for most up to date log.
#[typeshare]
#[derive(
Serialize, Deserialize, Debug, Clone, Resolve, EmptyTraits,
)]
#[empty_traits(KomodoReadRequest)]
#[response(SearchSwarmServiceLogResponse)]
#[error(serror::Error)]
pub struct SearchSwarmServiceLog {
/// Id or name
#[serde(alias = "id", alias = "name")]
pub swarm: String,
/// Select the swarm service to get logs for.
pub service: String,
/// The terms to search for.
pub terms: Vec<String>,
/// When searching for multiple terms, can use `AND` or `OR` combinator.
///
/// - `AND`: Only include lines with **all** terms present in that line.
/// - `OR`: Include lines that have one or more matches in the terms.
#[serde(default)]
pub combinator: SearchCombinator,
/// Invert the results, ie return all lines that DON'T match the terms / combinator.
#[serde(default)]
pub invert: bool,
/// Enable `--timestamps`
#[serde(default)]
pub timestamps: bool,
/// Enable `--no-task-ids`
#[serde(default)]
pub no_task_ids: bool,
/// Enable `--no-resolve`
#[serde(default)]
pub no_resolve: bool,
/// Enable `--details`
#[serde(default)]
pub details: bool,
}
#[typeshare]
pub type SearchSwarmServiceLogResponse = Log;
//
/// List tasks on the target Swarm.
/// Response: [ListSwarmTasksResponse].
#[typeshare]

View File

@@ -31,16 +31,18 @@ export type ReadResponses = {
InspectSwarm: Types.InspectSwarmResponse;
ListSwarmNodes: Types.ListSwarmNodesResponse;
InspectSwarmNode: Types.InspectSwarmNodeResponse;
ListSwarmServices: Types.ListSwarmServicesResponse;
InspectSwarmService: Types.InspectSwarmServiceResponse;
ListSwarmTasks: Types.ListSwarmTasksResponse;
InspectSwarmTask: Types.InspectSwarmTaskResponse;
ListSwarmSecrets: Types.ListSwarmSecretsResponse;
InspectSwarmSecret: Types.InspectSwarmSecretResponse;
ListSwarmConfigs: Types.ListSwarmConfigsResponse;
InspectSwarmConfig: Types.InspectSwarmConfigResponse;
ListSwarmSecrets: Types.ListSwarmSecretsResponse;
InspectSwarmSecret: Types.InspectSwarmSecretResponse;
ListSwarmStacks: Types.ListSwarmStacksResponse;
InspectSwarmStack: Types.InspectSwarmStackResponse;
ListSwarmTasks: Types.ListSwarmTasksResponse;
InspectSwarmTask: Types.InspectSwarmTaskResponse;
ListSwarmServices: Types.ListSwarmServicesResponse;
InspectSwarmService: Types.InspectSwarmServiceResponse;
GetSwarmServiceLog: Types.GetSwarmServiceLogResponse;
SearchSwarmServiceLog: Types.SearchSwarmServiceLogResponse;
// ==== SERVER ====
GetServersSummary: Types.GetServersSummaryResponse;

View File

@@ -2575,6 +2575,8 @@ export type Swarm = Resource<SwarmConfig, SwarmInfo>;
export type GetSwarmResponse = Swarm;
export type GetSwarmServiceLogResponse = Log;
/** System information of a server */
export interface SystemInformation {
/** The system name */
@@ -5282,6 +5284,8 @@ export type SearchDeploymentLogResponse = Log;
export type SearchStackLogResponse = Log;
export type SearchSwarmServiceLogResponse = Log;
export interface ServerQuerySpecifics {
}
@@ -7596,6 +7600,32 @@ export interface GetSwarmActionState {
swarm: string;
}
/**
* Get a swarm service's logs. Response: [GetSwarmServiceLogResponse].
*
* Note. This call will hit the underlying server directly for most up to date log.
*/
export interface GetSwarmServiceLog {
/** Id or name */
swarm: string;
/** Select the swarm service to get logs for. */
service: string;
/**
* The number of lines of the log tail to include.
* Default: 100.
* Max: 5000.
*/
tail: U64;
/** Enable `--timestamps` */
timestamps?: boolean;
/** Enable `--no-task-ids` */
no_task_ids?: boolean;
/** Enable `--no-resolve` */
no_resolve?: boolean;
/** Enable `--details` */
details?: boolean;
}
/**
* Gets a summary of data relating to all swarms.
* Response: [GetSwarmsSummaryResponse].
@@ -9176,6 +9206,38 @@ export interface SearchStackLog {
timestamps?: boolean;
}
/**
* Search the swarm service log's tail using `grep`. All lines go to stdout.
* Response: [SearchSwarmServiceLogResponse].
*
* Note. This call will hit the underlying server directly for most up to date log.
*/
export interface SearchSwarmServiceLog {
/** Id or name */
swarm: string;
/** Select the swarm service to get logs for. */
service: string;
/** The terms to search for. */
terms: string[];
/**
* When searching for multiple terms, can use `AND` or `OR` combinator.
*
* - `AND`: Only include lines with **all** terms present in that line.
* - `OR`: Include lines that have one or more matches in the terms.
*/
combinator?: SearchCombinator;
/** Invert the results, ie return all lines that DON'T match the terms / combinator. */
invert?: boolean;
/** Enable `--timestamps` */
timestamps?: boolean;
/** Enable `--no-task-ids` */
no_task_ids?: boolean;
/** Enable `--no-resolve` */
no_resolve?: boolean;
/** Enable `--details` */
details?: boolean;
}
/**
* Send a custom alert message to configured Alerters. Response: [Update].
* Alias: `alert`
@@ -10085,16 +10147,18 @@ export type ReadRequest =
| { type: "ListFullSwarms", params: ListFullSwarms }
| { type: "ListSwarmNodes", params: ListSwarmNodes }
| { type: "InspectSwarmNode", params: InspectSwarmNode }
| { type: "ListSwarmServices", params: ListSwarmServices }
| { type: "InspectSwarmService", params: InspectSwarmService }
| { type: "ListSwarmTasks", params: ListSwarmTasks }
| { type: "InspectSwarmTask", params: InspectSwarmTask }
| { type: "ListSwarmSecrets", params: ListSwarmSecrets }
| { type: "InspectSwarmSecret", params: InspectSwarmSecret }
| { type: "ListSwarmConfigs", params: ListSwarmConfigs }
| { type: "InspectSwarmConfig", params: InspectSwarmConfig }
| { type: "ListSwarmSecrets", params: ListSwarmSecrets }
| { type: "InspectSwarmSecret", params: InspectSwarmSecret }
| { type: "ListSwarmStacks", params: ListSwarmStacks }
| { type: "InspectSwarmStack", params: InspectSwarmStack }
| { type: "ListSwarmTasks", params: ListSwarmTasks }
| { type: "InspectSwarmTask", params: InspectSwarmTask }
| { type: "ListSwarmServices", params: ListSwarmServices }
| { type: "InspectSwarmService", params: InspectSwarmService }
| { type: "GetSwarmServiceLog", params: GetSwarmServiceLog }
| { type: "SearchSwarmServiceLog", params: SearchSwarmServiceLog }
| { type: "GetServersSummary", params: GetServersSummary }
| { type: "GetServer", params: GetServer }
| { type: "GetServerState", params: GetServerState }

View File

@@ -1,7 +1,11 @@
use komodo_client::entities::docker::{
SwarmLists, config::SwarmConfig, node::SwarmNode,
secret::SwarmSecret, service::SwarmService, stack::SwarmStackLists,
swarm::SwarmInspectInfo, task::SwarmTask,
use komodo_client::entities::{
SearchCombinator,
docker::{
SwarmLists, config::SwarmConfig, node::SwarmNode,
secret::SwarmSecret, service::SwarmService,
stack::SwarmStackLists, swarm::SwarmInspectInfo, task::SwarmTask,
},
update::Log,
};
use resolver_api::Resolve;
use serde::{Deserialize, Serialize};
@@ -40,6 +44,72 @@ pub struct InspectSwarmService {
pub name: String,
}
/// Get a swarm service's logs.
///
/// https://docs.docker.com/reference/cli/docker/service/logs/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct GetSwarmServiceLog {
/// The name or id of service.
/// Also accepts task id.
pub service: String,
/// Pass `--tail` for only recent log contents. Max of 5000
#[serde(default = "default_tail")]
pub tail: u64,
/// Enable `--timestamps`
#[serde(default)]
pub timestamps: bool,
/// Enable `--no-task-ids`
#[serde(default)]
pub no_task_ids: bool,
/// Enable `--no-resolve`
#[serde(default)]
pub no_resolve: bool,
/// Enable `--details`
#[serde(default)]
pub details: bool,
}
fn default_tail() -> u64 {
50
}
//
/// Search a swarm service's logs.
///
/// https://docs.docker.com/reference/cli/docker/service/logs/
#[derive(Debug, Clone, Serialize, Deserialize, Resolve)]
#[response(Log)]
#[error(anyhow::Error)]
pub struct GetSwarmServiceLogSearch {
/// The name or id of service.
/// Also accepts task id.
pub service: String,
/// The search terms.
pub terms: Vec<String>,
/// And: Only lines matching all terms
/// Or: Lines matching any one of the terms
#[serde(default)]
pub combinator: SearchCombinator,
/// Invert the search (search for everything not matching terms)
#[serde(default)]
pub invert: bool,
/// Enable `--timestamps`
#[serde(default)]
pub timestamps: bool,
/// Enable `--no-task-ids`
#[serde(default)]
pub no_task_ids: bool,
/// Enable `--no-resolve`
#[serde(default)]
pub no_resolve: bool,
/// Enable `--details`
#[serde(default)]
pub details: bool,
}
// ======
// Task
// ======

View File

@@ -26,16 +26,18 @@ export type ReadResponses = {
InspectSwarm: Types.InspectSwarmResponse;
ListSwarmNodes: Types.ListSwarmNodesResponse;
InspectSwarmNode: Types.InspectSwarmNodeResponse;
ListSwarmServices: Types.ListSwarmServicesResponse;
InspectSwarmService: Types.InspectSwarmServiceResponse;
ListSwarmTasks: Types.ListSwarmTasksResponse;
InspectSwarmTask: Types.InspectSwarmTaskResponse;
ListSwarmSecrets: Types.ListSwarmSecretsResponse;
InspectSwarmSecret: Types.InspectSwarmSecretResponse;
ListSwarmConfigs: Types.ListSwarmConfigsResponse;
InspectSwarmConfig: Types.InspectSwarmConfigResponse;
ListSwarmSecrets: Types.ListSwarmSecretsResponse;
InspectSwarmSecret: Types.InspectSwarmSecretResponse;
ListSwarmStacks: Types.ListSwarmStacksResponse;
InspectSwarmStack: Types.InspectSwarmStackResponse;
ListSwarmTasks: Types.ListSwarmTasksResponse;
InspectSwarmTask: Types.InspectSwarmTaskResponse;
ListSwarmServices: Types.ListSwarmServicesResponse;
InspectSwarmService: Types.InspectSwarmServiceResponse;
GetSwarmServiceLog: Types.GetSwarmServiceLogResponse;
SearchSwarmServiceLog: Types.SearchSwarmServiceLogResponse;
GetServersSummary: Types.GetServersSummaryResponse;
GetServer: Types.GetServerResponse;
GetServerState: Types.GetServerStateResponse;

View File

@@ -2691,6 +2691,7 @@ export interface SwarmInfo {
}
export type Swarm = Resource<SwarmConfig, SwarmInfo>;
export type GetSwarmResponse = Swarm;
export type GetSwarmServiceLogResponse = Log;
/** System information of a server */
export interface SystemInformation {
/** The system name */
@@ -5125,6 +5126,7 @@ export type ResourceSyncQuery = ResourceQuery<ResourceSyncQuerySpecifics>;
export type SearchContainerLogResponse = Log;
export type SearchDeploymentLogResponse = Log;
export type SearchStackLogResponse = Log;
export type SearchSwarmServiceLogResponse = Log;
export interface ServerQuerySpecifics {
}
/** Server-specific query */
@@ -7219,6 +7221,31 @@ export interface GetSwarmActionState {
/** Id or name */
swarm: string;
}
/**
* Get a swarm service's logs. Response: [GetSwarmServiceLogResponse].
*
* Note. This call will hit the underlying server directly for most up to date log.
*/
export interface GetSwarmServiceLog {
/** Id or name */
swarm: string;
/** Select the swarm service to get logs for. */
service: string;
/**
* The number of lines of the log tail to include.
* Default: 100.
* Max: 5000.
*/
tail: U64;
/** Enable `--timestamps` */
timestamps?: boolean;
/** Enable `--no-task-ids` */
no_task_ids?: boolean;
/** Enable `--no-resolve` */
no_resolve?: boolean;
/** Enable `--details` */
details?: boolean;
}
/**
* Gets a summary of data relating to all swarms.
* Response: [GetSwarmsSummaryResponse].
@@ -8648,6 +8675,37 @@ export interface SearchStackLog {
/** Enable `--timestamps` */
timestamps?: boolean;
}
/**
* Search the swarm service log's tail using `grep`. All lines go to stdout.
* Response: [SearchSwarmServiceLogResponse].
*
* Note. This call will hit the underlying server directly for most up to date log.
*/
export interface SearchSwarmServiceLog {
/** Id or name */
swarm: string;
/** Select the swarm service to get logs for. */
service: string;
/** The terms to search for. */
terms: string[];
/**
* When searching for multiple terms, can use `AND` or `OR` combinator.
*
* - `AND`: Only include lines with **all** terms present in that line.
* - `OR`: Include lines that have one or more matches in the terms.
*/
combinator?: SearchCombinator;
/** Invert the results, ie return all lines that DON'T match the terms / combinator. */
invert?: boolean;
/** Enable `--timestamps` */
timestamps?: boolean;
/** Enable `--no-task-ids` */
no_task_ids?: boolean;
/** Enable `--no-resolve` */
no_resolve?: boolean;
/** Enable `--details` */
details?: boolean;
}
/**
* Send a custom alert message to configured Alerters. Response: [Update].
* Alias: `alert`
@@ -9664,35 +9722,41 @@ export type ReadRequest = {
type: "InspectSwarmNode";
params: InspectSwarmNode;
} | {
type: "ListSwarmServices";
params: ListSwarmServices;
type: "ListSwarmConfigs";
params: ListSwarmConfigs;
} | {
type: "InspectSwarmService";
params: InspectSwarmService;
} | {
type: "ListSwarmTasks";
params: ListSwarmTasks;
} | {
type: "InspectSwarmTask";
params: InspectSwarmTask;
type: "InspectSwarmConfig";
params: InspectSwarmConfig;
} | {
type: "ListSwarmSecrets";
params: ListSwarmSecrets;
} | {
type: "InspectSwarmSecret";
params: InspectSwarmSecret;
} | {
type: "ListSwarmConfigs";
params: ListSwarmConfigs;
} | {
type: "InspectSwarmConfig";
params: InspectSwarmConfig;
} | {
type: "ListSwarmStacks";
params: ListSwarmStacks;
} | {
type: "InspectSwarmStack";
params: InspectSwarmStack;
} | {
type: "ListSwarmTasks";
params: ListSwarmTasks;
} | {
type: "InspectSwarmTask";
params: InspectSwarmTask;
} | {
type: "ListSwarmServices";
params: ListSwarmServices;
} | {
type: "InspectSwarmService";
params: InspectSwarmService;
} | {
type: "GetSwarmServiceLog";
params: GetSwarmServiceLog;
} | {
type: "SearchSwarmServiceLog";
params: SearchSwarmServiceLog;
} | {
type: "GetServersSummary";
params: GetServersSummary;