Private/summary-functions.ps1
function Check-KubernetesVersion { $versionInfo = kubectl version -o json | ConvertFrom-Json $k8sVersion = $versionInfo.serverVersion.gitVersion $latestVersion = (Invoke-WebRequest -Uri "https://dl.k8s.io/release/stable.txt").Content.Trim() if ($k8sVersion -lt $latestVersion) { return "⚠️ Cluster is running an outdated version: $k8sVersion (Latest: $latestVersion)" } else { return "✅ Cluster is up to date ($k8sVersion)" } } function Show-ApiServerHealth { param([switch]$Html) # 1. Fetch metrics try { $m = (& kubectl get --raw '/metrics') -join "`n" Write-Debug "Raw metrics fetched: $($m.Length) characters" } catch { Write-Warning "Failed to fetch metrics: $_" $m = '' } # 2. Fetch liveness & readiness try { $livez = (& kubectl get --raw '/livez?verbose') -join "`n" $readyz = (& kubectl get --raw '/readyz?verbose') -join "`n" } catch { Write-Warning "Failed to fetch health endpoints: $_" $livez = '' $readyz = '' } $lastLivez = ($livez -split "`n")[-1] $lastReadyz = ($readyz -split "`n")[-1] # 3. Compute p99 GET latency $p99Ms = $null if ($m) { try { Write-Debug "Parsing GET buckets from metrics" # parse buckets, skip +Inf $buckets = $m -split "`n" | Where-Object { $_ -match 'apiserver_request_duration_seconds_bucket.*verb="GET"' } | ForEach-Object { if ($_ -match 'le="([^"]+)"\}.*\s+(\d+)$') { $leRaw = $matches[1] $count = [int64]$matches[2] if ($leRaw -ne '+Inf') { [PSCustomObject]@{ Le = [double]$leRaw; Count = $count } } } } | Where-Object { $_ } # total GET count $totalLine = $m -split "`n" | Where-Object { $_ -match 'apiserver_request_duration_seconds_count.*verb="GET"' } | Select-Object -First 1 $total = ($totalLine -split '\s+')[1] -as [double] if ($buckets.Count -and $total -gt 0) { $target = $total * 0.99 $p99 = $buckets | Sort-Object Le | Where-Object { $_.Count -ge $target } | Select-Object -First 1 if ($p99) { $p99Ms = [math]::Round($p99.Le * 1000, 2) Write-Debug "Calculated p99Ms: $p99Ms" } else { Write-Warning "Couldn't locate p 99 bucket for GET" } } else { Write-Warning "No GET buckets or zero total count found" } } catch { Write-Warning "Error computing p99 latency: $_" } } else { Write-Warning "No valid metrics data to parse" } # 4. Output if ($Html) { if ($p99Ms) { $latLine = "<p><strong>latency (p99):</strong> " + "<span style='color:#0071FF'>$p99Ms ms</span></p>" } else { $latLine = "<p style='color:#999'>Metrics endpoint unavailable</p>" } return @" <div class="health-checks"> $latLine <details style="width: 100%;"> <summary> <span class="label">Liveness:</span> <span class="status">$lastLivez</span> <span class="material-icons">expand_more</span> </summary> <pre class="health-output">$livez</pre> </details> <details style="width: 100%;"> <summary> <span class="label">Readiness:</span> <span class="status">$lastReadyz</span> <span class="material-icons">expand_more</span> </summary> <pre class="health-output">$readyz</pre> </details> </div> "@ } else { Write-Host "API Server Health:" if ($p99Ms) { Write-Host " p 99 GET latency: $p99Ms ms" } else { Write-Host " Metrics endpoint unavailable" } Write-Host " Liveness:`n$livez" Write-Host "`n Readiness:`n$readyz" } } function Show-ClusterSummary { param( [switch]$Html, [switch]$Json, [switch]$Text, [object]$KubeData ) if (-not $Text -and -not $Html -and -not $Json) { Clear-Host } Write-Host "`n[🌐 Cluster Summary]" -ForegroundColor Cyan Write-Host -NoNewline "`n🤖 Fetching Cluster Information..." -ForegroundColor Yellow $versionInfo = kubectl version -o json | ConvertFrom-Json $k8sVersion = $versionInfo.serverVersion.gitVersion $clusterName = kubectl config current-context Write-Host "`r🤖 Cluster Information fetched. " -ForegroundColor Green Write-Host -NoNewline "`n🤖 Checking Kubernetes Version Compatibility..." -ForegroundColor Yellow $versionCheckResult = Check-KubernetesVersion Write-Host "`r🤖 Kubernetes Version Compatibility checked. " -ForegroundColor Green Write-Host -NoNewline "`n🤖 Fetching Cluster Metrics..." -ForegroundColor Yellow $summaryText = Show-HeroMetrics -KubeData:$KubeData -Json:$Json Write-Host "`r🤖 Cluster Metrics fetched. " -ForegroundColor Green Write-Host -NoNewline "`n🤖 Counting Kubernetes Events..." -ForegroundColor Yellow $events = if ($KubeData?.events) { $KubeData.events } else { (kubectl get events -A --sort-by=.metadata.creationTimestamp -o json | ConvertFrom-Json).items } $warningCount = ($events | Where-Object { $_.type -eq "Warning" }).Count $errorCount = ($events | Where-Object { $_.reason -match "Failed|Error" }).Count Write-Host "`r🤖 Kubernetes Events counted. " -ForegroundColor Green # get our HTML snippet (or text) for API health $apiHealth = Show-ApiServerHealth -Html:$Html if ($Json) { return @{ ClusterName = $clusterName KubernetesVersion = $k8sVersion VersionStatus = $versionCheckResult ApiHealthHtml = $apiHealth ErrorEvents = $errorCount WarningEvents = $warningCount MetricsSummary = $summaryText } } if (-not $Text) { Write-Host "`nCluster Name " -NoNewline -ForegroundColor Green Write-Host "is " -NoNewline Write-Host "$clusterName" -ForegroundColor Yellow Write-Host "Kubernetes Version " -NoNewline -ForegroundColor Green Write-Host "is " -NoNewline Write-Host "$k8sVersion" -ForegroundColor Yellow if (-not $KubeData) { kubectl cluster-info } Write-Host "`n$($versionCheckResult)" Write-Host "`nAPI Server Health:" -ForegroundColor Yellow Write-Host "`n$apiHealth" -ForegroundColor Green Write-Host "`n$summaryText" Write-Host "`n❌ Errors: $errorCount ⚠️ Warnings: $warningCount" -ForegroundColor Yellow } if ($Text) { Write-ToReport "Cluster Name: $clusterName" Write-ToReport "Kubernetes Version: $k8sVersion" if (-not $KubeData) { $info = kubectl cluster-info | Out-String Write-ToReport $info } Write-ToReport "Compatibility Check: $($versionCheckResult)" Write-ToReport "`nAPI Server Health: $apiHealth" Write-ToReport "`nMetrics: $summaryText" Write-ToReport "`n❌ Errors: $errorCount ⚠️ Warnings: $warningCount" } if (-not $Text -and -not $Html) { Read-Host "`nPress Enter to return to the main menu" } } function Show-HeroMetrics { param ( [object]$KubeData = $null, [switch]$Json ) $thresholds = if (-not $Json) { Get-KubeBuddyThresholds } else { Get-KubeBuddyThresholds -Silent } # Always fetch unfiltered data for cluster-wide metrics $nodeData = if ($KubeData?.nodes) { $KubeData.nodes } else { (kubectl get nodes -o json | ConvertFrom-Json) } $podData = if ($KubeData?.pods) { $KubeData.pods } else { (kubectl get pods --all-namespaces -o json | ConvertFrom-Json) } $jobData = if ($KubeData?.jobs) { $KubeData.jobs } else { (kubectl get jobs --all-namespaces -o json | ConvertFrom-Json) } $topNodes = if ($KubeData?.topNodes) { $KubeData.topNodes } else { kubectl top nodes --no-headers } $totalNodes = $nodeData.items.Count $healthyNodes = ($nodeData.items | Where-Object { $_.status.conditions | Where-Object { $_.type -eq 'Ready' -and $_.status -eq 'True' } }).Count $issueNodes = $totalNodes - $healthyNodes $totalPods = $podData.items.Count $runningPods = ($podData.items | Where-Object { $_.status.phase -eq "Running" }).Count $failedPods = ($podData.items | Where-Object { $_.status.phase -eq "Failed" }).Count $restartCounts = $podData.items | ForEach-Object { ($_.status.containerStatuses | Where-Object { $_.restartCount -gt 0 }).restartCount } | Measure-Object -Sum $totalRestarts = [int]($restartCounts.Sum) $warningRestarts = ($podData.items | Where-Object { ($_.status.containerStatuses | Where-Object { $_.restartCount -ge $thresholds.restarts_warning }).Count -gt 0 }).Count $criticalRestarts = ($podData.items | Where-Object { ($_.status.containerStatuses | Where-Object { $_.restartCount -ge $thresholds.restarts_critical }).Count -gt 0 }).Count $pendingPods = ($podData.items | Where-Object { $_.status.phase -eq "Pending" }).Count $stuckPods = ($podData.items | Where-Object { ($_.status.containerStatuses.state.waiting.reason -match "CrashLoopBackOff") -or ($_.status.phase -eq "Pending" -and $_.status.startTime -and ((New-TimeSpan -Start $_.status.startTime -End (Get-Date)).TotalMinutes -gt 15)) -or ($_.status.conditions.reason -match "ContainersNotReady|PodInitializing|ImagePullBackOff") }).Count $failedJobs = ($jobData.items | Where-Object { $_.status.failed -gt 0 }).Count $nodePodCounts = $podData.items | Group-Object { $_.spec.nodeName } $podCounts = $nodePodCounts | ForEach-Object { $_.Count } $avgPods = [math]::Round(($podCounts | Measure-Object -Average).Average, 1) $maxPods = ($podCounts | Measure-Object -Maximum).Maximum $minPods = ($podCounts | Measure-Object -Minimum).Minimum $usedCPU = 0; $usedMem = 0; $totalCPU = 0; $totalMem = 0 $topNodes | ForEach-Object { $fields = $_ -split "\s+" if ($fields.Count -ge 5) { $cpu = $fields[1] -replace "m", "" $mem = $fields[3] -replace "Mi", "" if ($cpu -match "^\d+$") { $usedCPU += [int]$cpu } if ($mem -match "^\d+$") { $usedMem += [int]$mem } $totalCPU += 1000 $totalMem += 65536 } } $cpuUsagePercent = if ($totalCPU -gt 0) { [math]::Round(($usedCPU / $totalCPU) * 100, 2) } else { 0 } $memUsagePercent = if ($totalMem -gt 0) { [math]::Round(($usedMem / $totalMem) * 100, 2) } else { 0 } $cpuStatus = if ($cpuUsagePercent -ge 80) { "🔴 Critical" } elseif ($cpuUsagePercent -ge 50) { "🟡 Warning" } else { "🟩 Normal" } $memStatus = if ($memUsagePercent -ge 80) { "🔴 Critical" } elseif ($memUsagePercent -ge 50) { "🟡 Warning" } else { "🟩 Normal" } $col2 = 10; $col3 = 14; $col4 = 16 $out = @() $out += "`n📊 Cluster Metrics Summary" $out += "------------------------------------------------------------------------------------------" $out += "🚀 Nodes: {0,$col2} 🟩 Healthy: {1,$col3} 🟥 Issues: {2,$col4}" -f $totalNodes, $healthyNodes, $issueNodes $out += "📦 Pods: {0,$col2} 🟩 Running: {1,$col3} 🟥 Failed: {2,$col4}" -f $totalPods, $runningPods, $failedPods $out += "🔄 Restarts: {0,$col2} 🟨 Warnings:{1,$col3} 🟥 Critical: {2,$col4}" -f $totalRestarts, $warningRestarts, $criticalRestarts $out += "⏳ Pending Pods: {0,$col2} 🟡 Waiting: {1,$col3} " -f $pendingPods, $pendingPods $out += "⚠️ Stuck Pods: {0,$col2} ❌ Stuck: {1,$col3} " -f $stuckPods, $stuckPods $out += "📉 Job Failures: {0,$col2} 🔴 Failed: {1,$col3} " -f $failedJobs, $failedJobs $out += "------------------------------------------------------------------------------------------" $out += "" $out += "📊 Pod Distribution: Avg: {0} | Max: {1} | Min: {2} | Total Nodes: {3}" -f $avgPods, $maxPods, $minPods, $totalNodes $out += "" $out += "" $out += "💾 Resource Usage" $out += "------------------------------------------------------------------------------------------" $out += "🖥 CPU Usage: {0,$col2}% {1,$col3}" -f $cpuUsagePercent, $cpuStatus $out += "💾 Memory Usage: {0,$col2}% {1,$col3}" -f $memUsagePercent, $memStatus $out += "------------------------------------------------------------------------------------------" return $out -join "`n" } |