Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

NameTypeDescription
idUuidUnique identifier for the diagnostic request.
agent_idUuidThe agent that should handle this request.
deployment_object_idUuidThe deployment object to gather diagnostics for.
statusStringStatus: pending, claimed, completed, failed, expired.
requested_byOption < String >Who requested the diagnostics.
created_atDateTime < Utc >When the request was created.
claimed_atOption < DateTime < Utc > >When the agent claimed the request.
completed_atOption < DateTime < Utc > >When the request was completed.
expires_atDateTime < Utc >When the request expires.

brokkr-agent::diagnostics::SubmitDiagnosticResult

pub

Derives: Debug, Clone, Serialize, Deserialize

Result to submit back to the broker.

Fields

NameTypeDescription
pod_statusesStringJSON-encoded pod statuses.
eventsStringJSON-encoded Kubernetes events.
log_tailsOption < String >JSON-encoded log tails (optional).
collected_atDateTime < Utc >When the diagnostics were collected.

brokkr-agent::diagnostics::PodStatus

pub

Derives: Debug, Clone, Serialize, Deserialize

Pod status information for diagnostics.

Fields

NameTypeDescription
nameStringPod name.
namespaceStringPod namespace.
phaseStringPod phase (Pending, Running, Succeeded, Failed, Unknown).
conditionsVec < PodCondition >Pod conditions.
containersVec < ContainerStatus >Container statuses.

brokkr-agent::diagnostics::PodCondition

pub

Derives: Debug, Clone, Serialize, Deserialize

Pod condition information.

Fields

NameTypeDescription
condition_typeStringCondition type.
statusStringCondition status (True, False, Unknown).
reasonOption < String >Reason for the condition.
messageOption < String >Human-readable message.

brokkr-agent::diagnostics::ContainerStatus

pub

Derives: Debug, Clone, Serialize, Deserialize

Container status information.

Fields

NameTypeDescription
nameStringContainer name.
readyboolWhether the container is ready.
restart_counti32Number of restarts.
stateStringCurrent state of the container.
state_reasonOption < String >Reason for current state.
state_messageOption < String >Message for current state.

brokkr-agent::diagnostics::EventInfo

pub

Derives: Debug, Clone, Serialize, Deserialize

Kubernetes event information.

Fields

NameTypeDescription
event_typeOption < String >Event type (Normal, Warning).
reasonOption < String >Event reason.
messageOption < String >Event message.
involved_objectStringObject involved.
first_timestampOption < DateTime < Utc > >First timestamp.
last_timestampOption < DateTime < Utc > >Last timestamp.
countOption < i32 >Event count.

brokkr-agent::diagnostics::DiagnosticsHandler

pub

Diagnostics handler for collecting Kubernetes diagnostics.

Fields

NameTypeDescription
clientClientKubernetes 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:

NameTypeDescription
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)
    }
}