brokkr-agent::diagnostics Rust
Diagnostics handler for on-demand diagnostic collection.
This module provides functionality to collect detailed diagnostic information about Kubernetes resources, including pod statuses, events, and log tails.
Structs
brokkr-agent::diagnostics::DiagnosticRequest
pub
Derives: Debug, Clone, Serialize, Deserialize
Diagnostic request received from the broker.
Fields
| Name | Type | Description |
|---|---|---|
id | Uuid | Unique identifier for the diagnostic request. |
agent_id | Uuid | The agent that should handle this request. |
deployment_object_id | Uuid | The deployment object to gather diagnostics for. |
status | String | Status: pending, claimed, completed, failed, expired. |
requested_by | Option < String > | Who requested the diagnostics. |
created_at | DateTime < Utc > | When the request was created. |
claimed_at | Option < DateTime < Utc > > | When the agent claimed the request. |
completed_at | Option < DateTime < Utc > > | When the request was completed. |
expires_at | DateTime < Utc > | When the request expires. |
brokkr-agent::diagnostics::SubmitDiagnosticResult
pub
Derives: Debug, Clone, Serialize, Deserialize
Result to submit back to the broker.
Fields
| Name | Type | Description |
|---|---|---|
pod_statuses | String | JSON-encoded pod statuses. |
events | String | JSON-encoded Kubernetes events. |
log_tails | Option < String > | JSON-encoded log tails (optional). |
collected_at | DateTime < Utc > | When the diagnostics were collected. |
brokkr-agent::diagnostics::PodStatus
pub
Derives: Debug, Clone, Serialize, Deserialize
Pod status information for diagnostics.
Fields
| Name | Type | Description |
|---|---|---|
name | String | Pod name. |
namespace | String | Pod namespace. |
phase | String | Pod phase (Pending, Running, Succeeded, Failed, Unknown). |
conditions | Vec < PodCondition > | Pod conditions. |
containers | Vec < ContainerStatus > | Container statuses. |
brokkr-agent::diagnostics::PodCondition
pub
Derives: Debug, Clone, Serialize, Deserialize
Pod condition information.
Fields
| Name | Type | Description |
|---|---|---|
condition_type | String | Condition type. |
status | String | Condition status (True, False, Unknown). |
reason | Option < String > | Reason for the condition. |
message | Option < String > | Human-readable message. |
brokkr-agent::diagnostics::ContainerStatus
pub
Derives: Debug, Clone, Serialize, Deserialize
Container status information.
Fields
| Name | Type | Description |
|---|---|---|
name | String | Container name. |
ready | bool | Whether the container is ready. |
restart_count | i32 | Number of restarts. |
state | String | Current state of the container. |
state_reason | Option < String > | Reason for current state. |
state_message | Option < String > | Message for current state. |
brokkr-agent::diagnostics::EventInfo
pub
Derives: Debug, Clone, Serialize, Deserialize
Kubernetes event information.
Fields
| Name | Type | Description |
|---|---|---|
event_type | Option < String > | Event type (Normal, Warning). |
reason | Option < String > | Event reason. |
message | Option < String > | Event message. |
involved_object | String | Object involved. |
first_timestamp | Option < DateTime < Utc > > | First timestamp. |
last_timestamp | Option < DateTime < Utc > > | Last timestamp. |
count | Option < i32 > | Event count. |
brokkr-agent::diagnostics::DiagnosticsHandler
pub
Diagnostics handler for collecting Kubernetes diagnostics.
Fields
| Name | Type | Description |
|---|---|---|
client | Client | Kubernetes client. |
Methods
new pub
#![allow(unused)]
fn main() {
fn new (client : Client) -> Self
}
Creates a new DiagnosticsHandler.
Source
#![allow(unused)]
fn main() {
pub fn new(client: Client) -> Self {
Self { client }
}
}
collect_diagnostics pub
async
#![allow(unused)]
fn main() {
async fn collect_diagnostics (& self , namespace : & str , label_selector : & str ,) -> Result < SubmitDiagnosticResult , Box < dyn std :: error :: Error + Send + Sync > >
}
Collects diagnostics for resources matching the given labels in the namespace.
Parameters:
| Name | Type | Description |
|---|---|---|
namespace | - | The Kubernetes namespace |
label_selector | - | Label selector to find the resources |
Returns:
A SubmitDiagnosticResult containing collected diagnostics
Source
#![allow(unused)]
fn main() {
pub async fn collect_diagnostics(
&self,
namespace: &str,
label_selector: &str,
) -> Result<SubmitDiagnosticResult, Box<dyn std::error::Error + Send + Sync>> {
info!(
"Collecting diagnostics for namespace={}, labels={}",
namespace, label_selector
);
// Collect pod statuses
let pod_statuses = self.collect_pod_statuses(namespace, label_selector).await?;
// Collect events
let events = self.collect_events(namespace, label_selector).await?;
// Collect log tails
let log_tails = self.collect_log_tails(namespace, label_selector).await.ok();
Ok(SubmitDiagnosticResult {
pod_statuses: serde_json::to_string(&pod_statuses)?,
events: serde_json::to_string(&events)?,
log_tails: log_tails.map(|l| serde_json::to_string(&l)).transpose()?,
collected_at: Utc::now(),
})
}
}
collect_pod_statuses private
async
#![allow(unused)]
fn main() {
async fn collect_pod_statuses (& self , namespace : & str , label_selector : & str ,) -> Result < Vec < PodStatus > , Box < dyn std :: error :: Error + Send + Sync > >
}
Collects pod statuses for matching pods.
Source
#![allow(unused)]
fn main() {
async fn collect_pod_statuses(
&self,
namespace: &str,
label_selector: &str,
) -> Result<Vec<PodStatus>, Box<dyn std::error::Error + Send + Sync>> {
let pods: Api<Pod> = Api::namespaced(self.client.clone(), namespace);
let lp = ListParams::default().labels(label_selector);
let pod_list = pods.list(&lp).await?;
let mut statuses = Vec::new();
for pod in pod_list.items {
let name = pod.metadata.name.clone().unwrap_or_default();
let pod_namespace = pod.metadata.namespace.clone().unwrap_or_default();
let status = if let Some(status) = &pod.status {
let phase = status
.phase
.clone()
.unwrap_or_else(|| "Unknown".to_string());
let conditions: Vec<PodCondition> = status
.conditions
.as_ref()
.map(|conds| {
conds
.iter()
.map(|c| PodCondition {
condition_type: c.type_.clone(),
status: c.status.clone(),
reason: c.reason.clone(),
message: c.message.clone(),
})
.collect()
})
.unwrap_or_default();
let containers: Vec<ContainerStatus> = status
.container_statuses
.as_ref()
.map(|cs| {
cs.iter()
.map(|c| {
let (state, state_reason, state_message) =
if let Some(state) = &c.state {
if let Some(running) = &state.running {
(
"Running".to_string(),
None,
running
.started_at
.as_ref()
.map(|t| format!("Started at {}", t.0)),
)
} else if let Some(waiting) = &state.waiting {
(
"Waiting".to_string(),
waiting.reason.clone(),
waiting.message.clone(),
)
} else if let Some(terminated) = &state.terminated {
(
"Terminated".to_string(),
terminated.reason.clone(),
terminated.message.clone(),
)
} else {
("Unknown".to_string(), None, None)
}
} else {
("Unknown".to_string(), None, None)
};
ContainerStatus {
name: c.name.clone(),
ready: c.ready,
restart_count: c.restart_count,
state,
state_reason,
state_message,
}
})
.collect()
})
.unwrap_or_default();
PodStatus {
name,
namespace: pod_namespace,
phase,
conditions,
containers,
}
} else {
PodStatus {
name,
namespace: pod_namespace,
phase: "Unknown".to_string(),
conditions: vec![],
containers: vec![],
}
};
statuses.push(status);
}
debug!("Collected status for {} pods", statuses.len());
Ok(statuses)
}
}
collect_events private
async
#![allow(unused)]
fn main() {
async fn collect_events (& self , namespace : & str , _label_selector : & str ,) -> Result < Vec < EventInfo > , Box < dyn std :: error :: Error + Send + Sync > >
}
Collects events for matching resources.
Source
#![allow(unused)]
fn main() {
async fn collect_events(
&self,
namespace: &str,
_label_selector: &str,
) -> Result<Vec<EventInfo>, Box<dyn std::error::Error + Send + Sync>> {
let events: Api<Event> = Api::namespaced(self.client.clone(), namespace);
let lp = ListParams::default();
let event_list = events.list(&lp).await?;
let mut event_infos = Vec::new();
for event in event_list.items {
let involved_object = event
.involved_object
.name
.clone()
.unwrap_or_else(|| "unknown".to_string());
event_infos.push(EventInfo {
event_type: event.type_.clone(),
reason: event.reason.clone(),
message: event.message.clone(),
involved_object,
first_timestamp: event.first_timestamp.map(|t| t.0),
last_timestamp: event.last_timestamp.map(|t| t.0),
count: event.count,
});
}
// Sort by last_timestamp descending and take recent events
event_infos.sort_by(|a, b| {
b.last_timestamp
.unwrap_or(DateTime::<Utc>::MIN_UTC)
.cmp(&a.last_timestamp.unwrap_or(DateTime::<Utc>::MIN_UTC))
});
event_infos.truncate(50);
debug!("Collected {} events", event_infos.len());
Ok(event_infos)
}
}
collect_log_tails private
async
#![allow(unused)]
fn main() {
async fn collect_log_tails (& self , namespace : & str , label_selector : & str ,) -> Result < HashMap < String , String > , Box < dyn std :: error :: Error + Send + Sync > >
}
Collects log tails for matching pods.
Source
#![allow(unused)]
fn main() {
async fn collect_log_tails(
&self,
namespace: &str,
label_selector: &str,
) -> Result<HashMap<String, String>, Box<dyn std::error::Error + Send + Sync>> {
let pods: Api<Pod> = Api::namespaced(self.client.clone(), namespace);
let lp = ListParams::default().labels(label_selector);
let pod_list = pods.list(&lp).await?;
let mut log_tails = HashMap::new();
for pod in pod_list.items {
let pod_name = pod.metadata.name.clone().unwrap_or_default();
// Get containers from the spec
if let Some(spec) = &pod.spec {
for container in &spec.containers {
let container_name = &container.name;
let key = format!("{}/{}", pod_name, container_name);
match self
.get_container_logs(namespace, &pod_name, container_name)
.await
{
Ok(logs) => {
log_tails.insert(key, logs);
}
Err(e) => {
debug!(
"Failed to get logs for {}/{}: {}",
pod_name, container_name, e
);
log_tails.insert(key, format!("Error: {}", e));
}
}
}
}
}
debug!("Collected logs for {} containers", log_tails.len());
Ok(log_tails)
}
}
get_container_logs private
async
#![allow(unused)]
fn main() {
async fn get_container_logs (& self , namespace : & str , pod_name : & str , container_name : & str ,) -> Result < String , Box < dyn std :: error :: Error + Send + Sync > >
}
Gets logs for a specific container.
Source
#![allow(unused)]
fn main() {
async fn get_container_logs(
&self,
namespace: &str,
pod_name: &str,
container_name: &str,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let pods: Api<Pod> = Api::namespaced(self.client.clone(), namespace);
let logs = pods
.logs(
pod_name,
&kube::api::LogParams {
container: Some(container_name.to_string()),
tail_lines: Some(MAX_LOG_LINES),
..Default::default()
},
)
.await?;
Ok(logs)
}
}