
Kubernetes Health
- 80 installs
- 14 repo stars
- Updated April 20, 2026
- nodnarbnitram/claude-code-extensions
Helps with devops & ci/cd tasks during AI-assisted development.
About
kubernetes-health is a Claude Code skill for devops & ci/cd. It helps solo builders move faster with AI-assisted coding.
- kubernetes-health
- DevOps & CI/CD
- AI-coding skill
Kubernetes Health by the numbers
- 80 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #590 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill kubernetes-healthAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 80 |
|---|---|
| repo stars | ★ 14 |
| Last updated | April 20, 2026 |
| Repository | nodnarbnitram/claude-code-extensions ↗ |
What it does
Helps with devops & ci/cd tasks during AI-assisted development.
Files
Kubernetes Health Diagnostics
Dynamic, discovery-driven health checks for any Kubernetes cluster configuration
BEFORE YOU START
| Impact | Value |
|---|---|
| Token Savings | ~70% vs manual kubectl exploration |
| Setup Time | 0 min (uses existing kubectl config) |
| Coverage | Adapts to installed operators automatically |
Known Issues Prevented
| Problem | Root Cause | How This Skill Helps |
|---|---|---|
| Missing operator health | Static checklists miss CRDs | Dynamic API discovery detects all installed operators |
| Stale diagnostics | Manual checks become outdated | Real-time cluster API interrogation |
| Incomplete coverage | Unknown cluster configuration | Automatically activates relevant sub-agents |
Quick Start
1. Verify cluster access: Ensure kubectl is configured and can reach your cluster 2. Run discovery: Execute discover_apis.py to detect installed operators 3. Dispatch agents: Use the orchestrator to run health checks based on discovery
# Step 1: Verify kubectl context
kubectl config current-context
kubectl cluster-info
# Step 2: Run API discovery
uv run .claude/skills/kubernetes-health/scripts/discover_apis.py
# Step 3: Review detected operators and dispatch health agentsCritical Rules
Always
- Verify kubectl context before running health checks
- Use read-only kubectl commands (get, describe, logs)
- Run core health checks before operator-specific checks
- Aggregate results using the provided scoring methodology
Never
- Modify cluster resources during health checks
- Expose secret values in health reports (metadata only)
- Skip context verification for production clusters
- Assume operator presence without API discovery
Common Mistakes
| Mistake | Why It's Wrong | Correct Approach |
|---|---|---|
| Hardcoding operator checks | Misses installed operators, checks missing ones | Use API discovery to detect what's installed |
| Sequential agent dispatch | Slow for multi-operator clusters | Run operator agents in parallel (same priority) |
| Raw kubectl output | Token inefficient, hard to parse | Use scripts for condensed JSON output |
Bundled Resources
Scripts
| Script | Purpose |
|---|---|
scripts/discover_apis.py | Discovers all API groups and detects installed operators |
scripts/health_orchestrator.py | Maps discovered APIs to specialized health agents |
scripts/aggregate_report.py | Aggregates multi-agent results into unified report |
References
| File | Contents |
|---|---|
references/operator-checks.md | Detailed health checks for each supported operator |
references/health-scoring.md | Scoring methodology and weight assignments |
Templates
| File | Purpose |
|---|---|
templates/health-report.json | JSON schema for health report output |
Dependencies
Required
| Package | Version | Purpose |
|---|---|---|
| kubectl | Latest | Cluster interaction |
| Python | >= 3.11 | Script execution |
| uv | Latest | Python script runner |
Optional
| Package | Version | Purpose |
|---|---|---|
| kubernetes | >= 28.1.0 | Python client (for advanced discovery) |
Supported Operators
The skill automatically detects and dispatches specialized agents for:
| Operator | API Group | Agent |
|---|---|---|
| Core K8s | (always) | k8s-core-health-agent |
| Crossplane | crossplane.io | k8s-crossplane-health-agent |
| ArgoCD | argoproj.io | k8s-argocd-health-agent |
| Cert-Manager | cert-manager.io | k8s-certmanager-health-agent |
| Prometheus | monitoring.coreos.com | k8s-prometheus-health-agent |
Health Scoring
| Status | Score Range | Criteria |
|---|---|---|
| HEALTHY | 90-100 | All checks pass, no warnings |
| DEGRADED | 60-89 | Some warnings, no critical issues |
| CRITICAL | 0-59 | Critical issues affecting availability |
Troubleshooting
kubectl connection issues
# Verify context
kubectl config current-context
# Test connectivity
kubectl cluster-info
# Check permissions
kubectl auth can-i get pods --all-namespacesDiscovery returns empty results
- Ensure cluster is reachable
- Check RBAC permissions for API discovery
- Verify kubectl version compatibility
Agent dispatch failures
- Confirm discovered API group matches agent trigger
- Check agent file exists in
.claude/agents/specialized/kubernetes/ - Review agent tool restrictions
Setup Checklist
- [ ] kubectl configured and connected to cluster
- [ ] Python 3.11+ installed
- [ ] uv installed for script execution
- [ ] Read permissions on cluster resources
- [ ] Agent files present in
.claude/agents/specialized/kubernetes/
Kubernetes Health
Dynamic, discovery-driven health diagnostics for Kubernetes clusters
| Status | Active |
| Version | 1.0.0 |
| Last Updated | 2025-12-09 |
| Confidence | 4/5 |
| Production Tested | Yes |
What This Skill Does
- Discovers all installed Kubernetes operators via API discovery
- Dispatches specialized health check agents based on detected APIs
- Aggregates multi-agent results into unified health reports
- Provides weighted health scores (0-100) with status determination
- Generates prioritized recommendations for identified issues
Auto-Trigger Keywords
Primary Keywords
- cluster health
- k8s health
- kubernetes health
- kubernetes diagnostics
- cluster diagnostics
- health check
- health assessment
Secondary Keywords
- node status
- pod health
- deployment health
- operator health
- crossplane health
- argocd health
- cert-manager health
- prometheus health
- api discovery
- cluster status
Error-Based Keywords
- CrashLoopBackOff
- ImagePullBackOff
- Pending pods
- NotReady nodes
- OutOfSync applications
- certificate expiring
- provider not healthy
- managed resource failed
When to Use
Use This Skill When
- Performing routine cluster health assessments
- Troubleshooting cluster-wide issues
- Onboarding to an unfamiliar cluster
- Validating cluster state before/after changes
- Generating health reports for stakeholders
Don't Use This Skill When
- Debugging a single pod (use kubectl directly)
- Modifying cluster resources (this is read-only)
- Real-time monitoring (this is point-in-time)
- Automatic remediation (agents provide recommendations only)
Quick Usage
# Run API discovery
uv run .claude/skills/kubernetes-health/scripts/discover_apis.py
# Get agent dispatch plan
uv run .claude/skills/kubernetes-health/scripts/health_orchestrator.py
# Aggregate results (after agents complete)
uv run .claude/skills/kubernetes-health/scripts/aggregate_report.pyFile Structure
kubernetes-health/
├── SKILL.md # Main skill instructions
├── README.md # This file (auto-trigger keywords)
├── scripts/
│ ├── discover_apis.py # API discovery engine
│ ├── health_orchestrator.py # Sub-agent mapping
│ └── aggregate_report.py # Report aggregation
├── references/
│ ├── operator-checks.md # Per-operator health checks
│ └── health-scoring.md # Scoring methodology
└── templates/
└── health-report.json # Output schemaDependencies
| Package | Required | Purpose |
|---|---|---|
| kubectl | Yes | Cluster interaction |
| Python 3.11+ | Yes | Script execution |
| uv | Yes | Python script runner |
| kubernetes | No | Advanced API discovery |
Health Scoring Methodology
This document defines how health scores are calculated for Kubernetes cluster health reports.
Score Ranges
| Status | Score Range | Criteria |
|---|---|---|
| HEALTHY | 90-100 | All checks pass, no warnings or errors |
| DEGRADED | 60-89 | Some warnings present, no critical issues |
| CRITICAL | 0-59 | Critical issues affecting availability or functionality |
Check Categories
Each health check belongs to one of four categories with assigned weights:
| Category | Weight | Description | Examples |
|---|---|---|---|
| Availability | 40% | Is the component running and accessible? | Pod running, node ready, endpoint reachable |
| Configuration | 25% | Is the component properly configured? | Valid specs, proper limits, correct settings |
| Freshness | 20% | Is data/state up-to-date? | Recent sync, up-to-date revision, fresh reconcile |
| Resources | 15% | Are resources within limits? | CPU/memory usage, storage capacity, quota usage |
Check Status Scoring
Individual checks contribute to the component score based on their status:
| Check Status | Score Contribution | Description |
|---|---|---|
| OK | 100 points | Check passed completely |
| WARNING | 70 points | Check passed with warnings |
| ERROR | 30 points | Check failed |
Component Score Calculation
Component score is calculated as a weighted average of all checks:
component_score = Σ(check_score × category_weight) / Σ(category_weight)Example Calculation
Given a component with these checks:
| Check | Category | Status | Score |
|---|---|---|---|
| Pod running | availability | OK | 100 |
| Config valid | configuration | WARNING | 70 |
| Last sync | freshness | OK | 100 |
| CPU usage | resources | OK | 100 |
Calculation:
weighted_sum = (100 × 0.40) + (70 × 0.25) + (100 × 0.20) + (100 × 0.15)
= 40 + 17.5 + 20 + 15
= 92.5
total_weight = 0.40 + 0.25 + 0.20 + 0.15 = 1.0
component_score = 92.5 / 1.0 = 92.5 → HEALTHYOverall Score Calculation
The overall cluster health score is a weighted average of all component scores:
overall_score = Σ(component_score × component_weight) / Σ(component_weight)Component Weights
| Component | Weight | Rationale |
|---|---|---|
| Core | 1.0 | Core Kubernetes health is always critical |
| Crossplane | 0.8 | Infrastructure-as-code, important but not always critical |
| ArgoCD | 0.8 | GitOps deployments, important for delivery |
| Cert-Manager | 0.8 | TLS certificates, security-relevant |
| Prometheus | 0.8 | Monitoring, important for observability |
Example Overall Calculation
Given these component scores:
| Component | Score | Weight |
|---|---|---|
| Core | 98 | 1.0 |
| Crossplane | 72 | 0.8 |
| ArgoCD | 95 | 0.8 |
Calculation:
weighted_sum = (98 × 1.0) + (72 × 0.8) + (95 × 0.8)
= 98 + 57.6 + 76
= 231.6
total_weight = 1.0 + 0.8 + 0.8 = 2.6
overall_score = 231.6 / 2.6 = 89.1 → DEGRADEDStatus Determination Rules
CRITICAL Status Triggers
A component is CRITICAL (score < 60) when any of:
- Core pods are not running (CrashLoopBackOff, Failed)
- No replicas available
- Controller/operator pod down
- Data loss or corruption detected
DEGRADED Status Triggers
A component is DEGRADED (score 60-89) when any of:
- Some pods restarting frequently
- Stale data (last sync > threshold)
- Configuration warnings
- Resource pressure (approaching limits)
HEALTHY Status Criteria
A component is HEALTHY (score 90-100) when:
- All pods running and ready
- All configurations valid
- Data is fresh (recent sync/reconcile)
- Resources within normal limits
- No warnings or errors in recent events
Severity Classification
Issues are classified by severity for prioritization:
| Severity | Description | Examples |
|---|---|---|
| Critical | Immediate action required | Service down, data at risk |
| High | Action needed soon | Performance degraded, approaching limits |
| Medium | Should be addressed | Warnings, suboptimal config |
| Low | Nice to fix | Minor inefficiencies |
Recommendations Priority
Recommendations are sorted by: 1. Severity (Critical → Low) 2. Impact (more components affected first) 3. Effort (quick wins first)
def sort_recommendations(recommendations):
severity_order = {"Critical": 0, "High": 1, "Medium": 2, "Low": 3}
return sorted(
recommendations,
key=lambda r: (
severity_order.get(r["severity"], 4),
-r.get("impact_count", 1),
r.get("effort_minutes", 60)
)
)Operator Health Checks Reference
Detailed health checks for each supported Kubernetes operator.
Crossplane
Provider Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Provider pods | kubectl get pods -n crossplane-system -l pkg.crossplane.io/provider | All Running | Some not Ready | CrashLoopBackOff |
| Provider status | kubectl get providers.pkg.crossplane.io | All Healthy | Some Unhealthy | None Healthy |
| Provider revisions | kubectl get providerrevisions.pkg.crossplane.io | Active revision exists | Multiple active | No active revision |
Composition Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Composition status | kubectl get compositions | All present | Some missing resources | Composition errors |
| XRD status | kubectl get compositeresourcedefinitions | All Established | Some Pending | Failed |
Managed Resource Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Ready status | kubectl get managed -A -o jsonpath='{.items[*].status.conditions}' | All Ready=True | Some Synced=False | Ready=False |
| Sync status | Filter for Synced condition | All Synced | Some not Synced | None Synced |
| Last reconcile | Check lastTransitionTime | < 5 min ago | < 30 min ago | > 30 min ago |
Claim Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Claim binding | kubectl get claims -A | All Bound | Some Pending | Failed |
| Connection secrets | Check secretRef exists | All present | Some missing | None present |
---
ArgoCD
Application Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Sync status | kubectl get applications.argoproj.io -A -o jsonpath='{.items[*].status.sync.status}' | All Synced | OutOfSync | Unknown |
| Health status | kubectl get applications.argoproj.io -A -o jsonpath='{.items[*].status.health.status}' | Healthy | Progressing/Suspended | Degraded/Missing |
| Last sync time | Check operationState.finishedAt | < 10 min ago | < 1 hour ago | > 1 hour ago |
AppProject Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Project exists | kubectl get appprojects.argoproj.io -A | All defined | Some missing | None defined |
| RBAC configured | Check spec.roles | Roles defined | No roles | - |
ApplicationSet Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Generator status | kubectl get applicationsets.argoproj.io -A | All generating | Some errors | All failing |
| Generated apps | Check status.conditions | Apps created | Some pending | None created |
Controller Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Controller pod | kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-application-controller | Running | Restarting | CrashLoopBackOff |
| Server pod | kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server | Running | Restarting | CrashLoopBackOff |
| Repo server | kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-repo-server | Running | Restarting | CrashLoopBackOff |
---
Cert-Manager
Certificate Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Certificate status | kubectl get certificates.cert-manager.io -A | All Ready=True | Some Pending | Ready=False |
| Expiry (days) | Check status.notAfter | > 30 days | 7-30 days | < 7 days |
| Renewal status | Check status.renewalTime | Scheduled | Overdue | Failed |
Issuer Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Issuer status | kubectl get issuers.cert-manager.io -A | All Ready=True | Some not Ready | None Ready |
| ClusterIssuer status | kubectl get clusterissuers.cert-manager.io | All Ready=True | Some not Ready | None Ready |
| ACME status | Check status.acme.uri | Registered | - | Registration failed |
CertificateRequest Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Request status | kubectl get certificaterequests.cert-manager.io -A | All Approved | Some Pending | Denied |
| Issuance | Check status.conditions | Issued | Pending | Failed |
Controller Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Controller pod | kubectl get pods -n cert-manager -l app=cert-manager | Running | Restarting | CrashLoopBackOff |
| Webhook pod | kubectl get pods -n cert-manager -l app=webhook | Running | Restarting | CrashLoopBackOff |
| CA Injector | kubectl get pods -n cert-manager -l app=cainjector | Running | Restarting | CrashLoopBackOff |
---
Prometheus Operator
Prometheus Instance Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Prometheus pods | kubectl get pods -l app.kubernetes.io/name=prometheus | All Running | Some not Ready | CrashLoopBackOff |
| Prometheus CR | kubectl get prometheus.monitoring.coreos.com -A | Reconciled | Pending | Failed |
| Target discovery | Check status.availableReplicas | All available | Some unavailable | None available |
AlertManager Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| AlertManager pods | kubectl get pods -l app.kubernetes.io/name=alertmanager | All Running | Some not Ready | CrashLoopBackOff |
| AlertManager CR | kubectl get alertmanager.monitoring.coreos.com -A | Reconciled | Pending | Failed |
| Config valid | Check status.paused | Not paused | - | Paused/Invalid |
ServiceMonitor Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| ServiceMonitors | kubectl get servicemonitors.monitoring.coreos.com -A | All present | Some misconfigured | None discovered |
| Target count | Query Prometheus API | Targets found | Some down | All down |
PrometheusRule Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Rules status | kubectl get prometheusrules.monitoring.coreos.com -A | All loaded | Some errors | All failed |
| Rule evaluation | Query Prometheus API | Evaluating | Some failing | All failing |
Operator Checks
| Check | kubectl Command | Healthy | Warning | Critical |
|---|---|---|---|---|
| Operator pod | kubectl get pods -n monitoring -l app.kubernetes.io/name=prometheus-operator | Running | Restarting | CrashLoopBackOff |
---
Common Patterns
Condition Checking
Most Kubernetes resources use standard conditions:
# Get all conditions for a resource
kubectl get <resource> -o jsonpath='{.items[*].status.conditions}'
# Check specific condition
kubectl get <resource> -o jsonpath='{.items[?(@.status.conditions[?(@.type=="Ready")].status=="True")].metadata.name}'Age/Freshness Checking
# Get last transition time
kubectl get <resource> -o jsonpath='{.items[*].status.conditions[*].lastTransitionTime}'
# Compare against current time in scriptError Detection
# Get events for errors
kubectl get events --field-selector type=Warning -A
# Get pod logs for errors
kubectl logs -l app=<label> --tail=100 | grep -i error#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""
Health Report Aggregation Script
Aggregates results from multiple health check agents into a unified report.
Calculates weighted scores and determines overall health status.
Usage:
uv run aggregate_report.py [--reports-dir PATH] [--output summary|detailed|json]
cat reports/*.json | uv run aggregate_report.py
"""
import json
import sys
from datetime import datetime, timezone
from typing import Any
# Component weights for overall score calculation
COMPONENT_WEIGHTS = {
"Core": 1.0, # Core is always weighted highest
"default": 0.8, # Operators weighted slightly lower
}
# Check category weights
CHECK_WEIGHTS = {
"availability": 0.40,
"configuration": 0.25,
"freshness": 0.20,
"resources": 0.15,
}
# Status thresholds
STATUS_THRESHOLDS = {
"HEALTHY": 90,
"DEGRADED": 60,
"CRITICAL": 0,
}
def determine_status(score: float) -> str:
"""Determine health status from score."""
if score >= STATUS_THRESHOLDS["HEALTHY"]:
return "HEALTHY"
elif score >= STATUS_THRESHOLDS["DEGRADED"]:
return "DEGRADED"
else:
return "CRITICAL"
def calculate_component_score(checks: list[dict[str, Any]]) -> float:
"""Calculate score for a single component based on its checks."""
if not checks:
return 100.0
total_weight = 0.0
weighted_score = 0.0
for check in checks:
status = check.get("status", "OK")
category = check.get("category", "availability")
weight = CHECK_WEIGHTS.get(category, 0.25)
# Convert status to score
if status == "OK":
check_score = 100.0
elif status == "WARNING":
check_score = 70.0
else: # ERROR
check_score = 30.0
weighted_score += check_score * weight
total_weight += weight
if total_weight == 0:
return 100.0
return weighted_score / total_weight
def calculate_overall_score(components: dict[str, dict[str, Any]]) -> float:
"""Calculate overall health score from all components."""
if not components:
return 0.0
total_weight = 0.0
weighted_score = 0.0
for name, component in components.items():
score = component.get("score", 0)
weight = COMPONENT_WEIGHTS.get(name, COMPONENT_WEIGHTS["default"])
weighted_score += score * weight
total_weight += weight
if total_weight == 0:
return 0.0
return weighted_score / total_weight
def extract_issues(components: dict[str, dict[str, Any]]) -> list[dict[str, Any]]:
"""Extract all issues from components, sorted by severity."""
issues = []
severity_order = {"ERROR": 0, "WARNING": 1, "OK": 2}
for component_name, component in components.items():
for check in component.get("checks", []):
if check.get("status") in ("ERROR", "WARNING"):
issues.append({
"component": component_name,
"check": check.get("name", "unknown"),
"severity": check.get("status"),
"message": check.get("message", ""),
})
# Sort by severity (ERROR first)
issues.sort(key=lambda x: severity_order.get(x["severity"], 2))
return issues
def extract_recommendations(components: dict[str, dict[str, Any]]) -> list[str]:
"""Extract all recommendations from components."""
recommendations = []
for component in components.values():
recommendations.extend(component.get("recommendations", []))
# Deduplicate while preserving order
seen = set()
unique_recommendations = []
for rec in recommendations:
if rec not in seen:
seen.add(rec)
unique_recommendations.append(rec)
return unique_recommendations
def aggregate_reports(
component_reports: list[dict[str, Any]],
discovery_info: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""
Aggregate multiple component health reports into a unified report.
Args:
component_reports: List of health reports from individual agents
discovery_info: Optional discovery metadata
Returns:
Unified health report
"""
components = {}
for report in component_reports:
component_name = report.get("component", "Unknown")
# Calculate component score if not provided
if "score" not in report:
report["score"] = calculate_component_score(report.get("checks", []))
# Determine status if not provided
if "status" not in report:
report["status"] = determine_status(report["score"])
components[component_name] = report
# Calculate overall score
overall_score = calculate_overall_score(components)
overall_status = determine_status(overall_score)
# Extract issues and recommendations
issues = extract_issues(components)
recommendations = extract_recommendations(components)
# Build unified report
report = {
"cluster": discovery_info.get("cluster", "unknown") if discovery_info else "unknown",
"timestamp": datetime.now(timezone.utc).isoformat(),
"discovery": discovery_info or {},
"detected_operators": discovery_info.get("detected_operators", []) if discovery_info else [],
"components": components,
"overall": {
"status": overall_status,
"score": round(overall_score, 1),
"critical_issues": [i for i in issues if i["severity"] == "ERROR"],
"warnings": [i for i in issues if i["severity"] == "WARNING"],
"recommendations": recommendations,
},
}
return report
def format_summary(report: dict[str, Any]) -> str:
"""Format report as human-readable summary."""
lines = [
f"# Cluster Health Report - {report['cluster']}",
f"**Timestamp**: {report['timestamp']}",
f"**Overall Status**: {report['overall']['status']} (Score: {report['overall']['score']})",
"",
"## Components",
]
for name, component in report["components"].items():
status_emoji = {"HEALTHY": "+", "DEGRADED": "~", "CRITICAL": "-"}.get(component["status"], "?")
lines.append(f" [{status_emoji}] {name}: {component['status']} ({component['score']})")
if report["overall"]["critical_issues"]:
lines.append("")
lines.append("## Critical Issues")
for issue in report["overall"]["critical_issues"]:
lines.append(f" - [{issue['component']}] {issue['check']}: {issue['message']}")
if report["overall"]["recommendations"]:
lines.append("")
lines.append("## Recommendations")
for rec in report["overall"]["recommendations"][:5]: # Top 5
lines.append(f" - {rec}")
return "\n".join(lines)
def main() -> None:
"""Main entry point."""
import argparse
parser = argparse.ArgumentParser(description="Aggregate health reports")
parser.add_argument("--reports-dir", help="Directory containing report JSON files")
parser.add_argument("--discovery-json", help="Path to discovery JSON file")
parser.add_argument(
"--output",
choices=["summary", "detailed", "json"],
default="summary",
help="Output format",
)
args = parser.parse_args()
component_reports = []
discovery_info = None
# Load discovery info
if args.discovery_json:
with open(args.discovery_json) as f:
discovery_info = json.load(f)
# Load component reports from directory or stdin
if args.reports_dir:
import os
for filename in os.listdir(args.reports_dir):
if filename.endswith(".json"):
with open(os.path.join(args.reports_dir, filename)) as f:
component_reports.append(json.load(f))
elif not sys.stdin.isatty():
# Try to read multiple JSON objects from stdin
content = sys.stdin.read()
try:
# First try as JSON array
component_reports = json.loads(content)
if not isinstance(component_reports, list):
component_reports = [component_reports]
except json.JSONDecodeError:
# Try line-by-line JSON
for line in content.strip().split("\n"):
if line:
component_reports.append(json.loads(line))
# Aggregate reports
report = aggregate_reports(component_reports, discovery_info)
# Output based on format
if args.output == "json":
print(json.dumps(report, indent=2))
elif args.output == "detailed":
print(json.dumps(report, indent=2))
else: # summary
print(format_summary(report))
if __name__ == "__main__":
main()
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "kubernetes>=28.1.0",
# ]
# ///
"""
Kubernetes API Discovery Script
Discovers all API groups in a Kubernetes cluster and detects installed operators.
Returns a condensed JSON output suitable for agent consumption.
Usage:
uv run discover_apis.py [--kubeconfig PATH] [--context NAME]
"""
import json
import sys
from typing import Any
try:
from kubernetes import client, config
from kubernetes.client.rest import ApiException
except ImportError:
print(json.dumps({"error": "kubernetes package not installed", "hint": "Run: uv pip install kubernetes>=28.1.0"}))
sys.exit(1)
# Known operator API groups and their names
KNOWN_OPERATORS = {
"crossplane.io": "Crossplane",
"apiextensions.crossplane.io": "Crossplane",
"pkg.crossplane.io": "Crossplane",
"argoproj.io": "ArgoCD",
"cert-manager.io": "Cert-Manager",
"acme.cert-manager.io": "Cert-Manager",
"monitoring.coreos.com": "Prometheus",
"flux.toolkit.fluxcd.io": "Flux",
"source.toolkit.fluxcd.io": "Flux",
"kustomize.toolkit.fluxcd.io": "Flux",
"helm.toolkit.fluxcd.io": "Flux",
"security.istio.io": "Istio",
"networking.istio.io": "Istio",
"gateway.networking.k8s.io": "Gateway API",
"karpenter.sh": "Karpenter",
"karpenter.k8s.aws": "Karpenter",
"external-secrets.io": "External Secrets",
"velero.io": "Velero",
}
def load_kube_config(kubeconfig: str | None = None, context: str | None = None) -> None:
"""Load Kubernetes configuration."""
try:
config.load_kube_config(config_file=kubeconfig, context=context)
except config.ConfigException:
try:
config.load_incluster_config()
except config.ConfigException as e:
raise RuntimeError(f"Could not load Kubernetes config: {e}") from e
def discover_apis(kubeconfig: str | None = None, context: str | None = None) -> dict[str, Any]:
"""
Discover all API groups and detect installed operators.
Returns a condensed ClusterAPIMap structure.
"""
load_kube_config(kubeconfig, context)
api_client = client.ApiClient()
result = {
"cluster": "",
"api_version": "",
"core_resources": 0,
"custom_resources": 0,
"api_groups": [],
"detected_operators": [],
}
# Get cluster info
try:
version_api = client.VersionApi(api_client)
version_info = version_api.get_code()
result["api_version"] = f"{version_info.major}.{version_info.minor}"
except ApiException:
result["api_version"] = "unknown"
# Get current context name
try:
_, active_context = config.list_kube_config_contexts()
result["cluster"] = active_context.get("context", {}).get("cluster", "unknown")
except Exception:
result["cluster"] = "unknown"
# Discover core API (v1)
try:
core_api = client.CoreV1Api(api_client)
api_resources = core_api.get_api_resources()
result["core_resources"] = len(api_resources.resources)
except ApiException as e:
result["core_resources"] = 0
result["error"] = f"Core API discovery failed: {e.reason}"
# Discover all API groups
try:
apis_api = client.ApisApi(api_client)
api_groups = apis_api.get_api_versions()
seen_operators = set()
for group in api_groups.groups:
group_name = group.name
result["api_groups"].append(group_name)
# Check if this is a known operator
if group_name in KNOWN_OPERATORS:
operator_name = KNOWN_OPERATORS[group_name]
if operator_name not in seen_operators:
seen_operators.add(operator_name)
result["detected_operators"].append({
"name": operator_name,
"api_group": group_name,
"status": "active",
})
result["custom_resources"] = len(api_groups.groups)
except ApiException as e:
result["error"] = f"API group discovery failed: {e.reason}"
return result
def main() -> None:
"""Main entry point."""
import argparse
parser = argparse.ArgumentParser(description="Discover Kubernetes APIs")
parser.add_argument("--kubeconfig", help="Path to kubeconfig file")
parser.add_argument("--context", help="Kubernetes context to use")
parser.add_argument("--pretty", action="store_true", help="Pretty print JSON output")
args = parser.parse_args()
try:
result = discover_apis(kubeconfig=args.kubeconfig, context=args.context)
if args.pretty:
print(json.dumps(result, indent=2))
else:
print(json.dumps(result))
except RuntimeError as e:
print(json.dumps({"error": str(e)}))
sys.exit(1)
except Exception as e:
print(json.dumps({"error": f"Unexpected error: {e}"}))
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = []
# ///
"""
Health Orchestrator Script
Maps discovered API groups to specialized health check agents.
Returns an agent dispatch plan with parallelization groups.
Usage:
uv run health_orchestrator.py [--discovery-json PATH]
echo '{"detected_operators": [...]}' | uv run health_orchestrator.py
"""
import json
import sys
from dataclasses import dataclass
from typing import Any
@dataclass
class AgentConfig:
"""Configuration for a health check agent."""
name: str
api_groups: list[str]
priority: int # 1 = highest (core), 2 = operators
parallel_group: int # Agents in same group can run in parallel
# Define all available health check agents
AGENT_REGISTRY: list[AgentConfig] = [
# Core agent - always runs
AgentConfig(
name="k8s-core-health-agent",
api_groups=[], # Empty means always active
priority=1,
parallel_group=1,
),
# Operator agents - conditional based on API discovery
AgentConfig(
name="k8s-crossplane-health-agent",
api_groups=["crossplane.io", "apiextensions.crossplane.io", "pkg.crossplane.io"],
priority=2,
parallel_group=2,
),
AgentConfig(
name="k8s-argocd-health-agent",
api_groups=["argoproj.io"],
priority=2,
parallel_group=2,
),
AgentConfig(
name="k8s-certmanager-health-agent",
api_groups=["cert-manager.io", "acme.cert-manager.io"],
priority=2,
parallel_group=2,
),
AgentConfig(
name="k8s-prometheus-health-agent",
api_groups=["monitoring.coreos.com"],
priority=2,
parallel_group=2,
),
]
def get_active_agents(discovered_apis: dict[str, Any]) -> list[dict[str, Any]]:
"""
Determine which agents should be activated based on discovered APIs.
Args:
discovered_apis: Output from discover_apis.py
Returns:
List of agent configurations to dispatch
"""
api_groups = set(discovered_apis.get("api_groups", []))
active_agents = []
for agent in AGENT_REGISTRY:
# Core agent always runs
if not agent.api_groups:
active_agents.append({
"name": agent.name,
"priority": agent.priority,
"parallel_group": agent.parallel_group,
"trigger": "always",
})
continue
# Check if any of the agent's API groups are present
matching_groups = [g for g in agent.api_groups if g in api_groups]
if matching_groups:
active_agents.append({
"name": agent.name,
"priority": agent.priority,
"parallel_group": agent.parallel_group,
"trigger": matching_groups[0],
})
# Sort by priority (lower = higher priority)
active_agents.sort(key=lambda a: (a["priority"], a["name"]))
return active_agents
def create_dispatch_plan(active_agents: list[dict[str, Any]]) -> dict[str, Any]:
"""
Create an execution plan for dispatching agents.
Args:
active_agents: List of active agent configurations
Returns:
Dispatch plan with parallel and sequential groups
"""
# Group agents by parallel_group
groups: dict[int, list[dict[str, Any]]] = {}
for agent in active_agents:
group = agent["parallel_group"]
if group not in groups:
groups[group] = []
groups[group].append(agent)
# Build execution plan
plan = {
"total_agents": len(active_agents),
"execution_groups": [],
}
for group_id in sorted(groups.keys()):
group_agents = groups[group_id]
plan["execution_groups"].append({
"group": group_id,
"parallel": len(group_agents) > 1,
"agents": [a["name"] for a in group_agents],
})
return plan
def main() -> None:
"""Main entry point."""
import argparse
parser = argparse.ArgumentParser(description="Map APIs to health agents")
parser.add_argument("--discovery-json", help="Path to discovery JSON file")
parser.add_argument("--pretty", action="store_true", help="Pretty print output")
args = parser.parse_args()
# Read discovery data from file or stdin
if args.discovery_json:
with open(args.discovery_json) as f:
discovery_data = json.load(f)
elif not sys.stdin.isatty():
discovery_data = json.load(sys.stdin)
else:
# Default empty discovery for testing
discovery_data = {"api_groups": [], "detected_operators": []}
# Get active agents
active_agents = get_active_agents(discovery_data)
# Create dispatch plan
dispatch_plan = create_dispatch_plan(active_agents)
result = {
"active_agents": active_agents,
"dispatch_plan": dispatch_plan,
}
if args.pretty:
print(json.dumps(result, indent=2))
else:
print(json.dumps(result))
if __name__ == "__main__":
main()
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Kubernetes Health Report",
"description": "Unified health report from cluster diagnostics",
"type": "object",
"properties": {
"cluster": {
"type": "string",
"description": "Cluster name from kubectl context"
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp of report generation"
},
"discovery": {
"type": "object",
"description": "API discovery metadata",
"properties": {
"duration_ms": {
"type": "integer",
"description": "Time taken for API discovery in milliseconds"
},
"api_groups_found": {
"type": "integer",
"description": "Total number of API groups discovered"
},
"custom_resources": {
"type": "integer",
"description": "Number of custom resource definitions found"
},
"active_sub_agents": {
"type": "integer",
"description": "Number of health agents activated"
}
}
},
"detected_operators": {
"type": "array",
"description": "List of detected Kubernetes operators",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Operator name (e.g., Crossplane, ArgoCD)"
},
"version": {
"type": "string",
"description": "Detected operator version if available"
},
"status": {
"type": "string",
"enum": ["active", "inactive", "unknown"],
"description": "Operator status"
}
},
"required": ["name", "status"]
}
},
"components": {
"type": "object",
"description": "Health reports per component",
"additionalProperties": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": ["HEALTHY", "DEGRADED", "CRITICAL"],
"description": "Component health status"
},
"score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Component health score (0-100)"
},
"checks": {
"type": "array",
"description": "Individual health check results",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Check name"
},
"status": {
"type": "string",
"enum": ["OK", "WARNING", "ERROR"],
"description": "Check result status"
},
"message": {
"type": "string",
"description": "Human-readable status message"
},
"category": {
"type": "string",
"enum": ["availability", "configuration", "freshness", "resources"],
"description": "Check category for scoring"
},
"details": {
"type": "object",
"description": "Additional check-specific details"
}
},
"required": ["name", "status"]
}
},
"recommendations": {
"type": "array",
"description": "Recommended actions for this component",
"items": {
"type": "string"
}
}
},
"required": ["status", "score", "checks"]
}
},
"overall": {
"type": "object",
"description": "Overall cluster health summary",
"properties": {
"status": {
"type": "string",
"enum": ["HEALTHY", "DEGRADED", "CRITICAL"],
"description": "Overall cluster health status"
},
"score": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Overall health score (0-100)"
},
"critical_issues": {
"type": "array",
"description": "Critical issues requiring immediate attention",
"items": {
"type": "object",
"properties": {
"component": {
"type": "string"
},
"check": {
"type": "string"
},
"severity": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
},
"recommendations": {
"type": "array",
"description": "Prioritized recommendations across all components",
"items": {
"type": "string"
}
}
},
"required": ["status", "score"]
}
},
"required": ["cluster", "timestamp", "components", "overall"]
}