<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Iman Tabatabaei</title>
    <description>The latest articles on DEV Community by Iman Tabatabaei (@iman_tabatabaei_d0d8c726e).</description>
    <link>https://dev.to/iman_tabatabaei_d0d8c726e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2326813%2F61e9c46f-678f-4637-ad79-0d64198f38ea.jpg</url>
      <title>DEV Community: Iman Tabatabaei</title>
      <link>https://dev.to/iman_tabatabaei_d0d8c726e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iman_tabatabaei_d0d8c726e"/>
    <language>en</language>
    <item>
      <title>Building a read-only Kubernetes troubleshooting CLI in Go</title>
      <dc:creator>Iman Tabatabaei</dc:creator>
      <pubDate>Mon, 20 Jul 2026 06:55:37 +0000</pubDate>
      <link>https://dev.to/iman_tabatabaei_d0d8c726e/building-a-read-only-kubernetes-troubleshooting-cli-in-go-4j2</link>
      <guid>https://dev.to/iman_tabatabaei_d0d8c726e/building-a-read-only-kubernetes-troubleshooting-cli-in-go-4j2</guid>
      <description>&lt;p&gt;Every Kubernetes incident I've handled starts the same way: &lt;code&gt;kubectl get pods&lt;/code&gt;, spot the sad one, &lt;code&gt;kubectl describe&lt;/code&gt;, scroll the events, check the node, guess. It works, but it's slow, and it's the same detective work every single time. I wanted a command that does that sweep for me and just tells me &lt;em&gt;why&lt;/em&gt; something is broken. That turned into &lt;strong&gt;kubeagent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubeagent scan&lt;/code&gt; reads a whole cluster and, for each unhealthy pod, node, or Service, names the root cause — CrashLoopBackOff, ImagePullBackOff, OOMKilled, Pending/Unschedulable, Multi-Attach volume errors, failing probes, stale kubelet leases, Services with zero ready endpoints, and more. It's read-only, works offline, and ships as a single Go binary.&lt;/p&gt;

&lt;p&gt;Three design decisions are worth walking through.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Deterministic first, AI optional
&lt;/h2&gt;

&lt;p&gt;It would have been easy to pipe everything to an LLM and call it a day. I didn't want that. The core is &lt;strong&gt;deterministic&lt;/strong&gt;: each failure mode is detected by explicit rules against the cluster state, so it works offline, gives the same answer every time, and I can unit-test it.&lt;/p&gt;

&lt;p&gt;There's an optional &lt;code&gt;--explain&lt;/code&gt; flag that makes a &lt;em&gt;single&lt;/em&gt; Claude API call to summarize the findings in plain English — but it only ever sees a structured summary (namespace, kind, status, restart counts, the detector's finding), never raw pod specs, env vars, or secrets. The tool is fully useful with no API key at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Read-only by default (the guardrails)
&lt;/h2&gt;

&lt;p&gt;A troubleshooting tool you're afraid to run in prod is useless. kubeagent issues only &lt;code&gt;get&lt;/code&gt;/&lt;code&gt;list&lt;/code&gt;/&lt;code&gt;watch&lt;/code&gt; calls by default — nothing else.&lt;/p&gt;

&lt;p&gt;There's an opt-in &lt;code&gt;--fix&lt;/code&gt; for a couple of reversible remediations (roll back a Deployment stuck on a bad image; uncordon an accidentally-cordoned node), but every write is guard-railed: a fixed allowlist of actions, never in &lt;code&gt;kube-system&lt;/code&gt;, each behind a &lt;code&gt;[y/N]&lt;/code&gt; confirm, preconditions re-checked against live state, and the result re-verified afterward. Crucially, &lt;strong&gt;no LLM ever decides a write&lt;/strong&gt; — the remediations are hardcoded and deterministic.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Detectors as pure functions
&lt;/h2&gt;

&lt;p&gt;This is the part I'm happiest with as a Go design. Each detector is a &lt;strong&gt;pure function&lt;/strong&gt;: it takes a snapshot of cluster objects (pods, nodes, events…) and returns findings. No I/O, no client calls inside them. That buys a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They unit-test against &lt;strong&gt;fake objects&lt;/strong&gt; — construct a pod struct in a &lt;code&gt;CrashLoopBackOff&lt;/code&gt; state, assert the detector flags it. No cluster, no mock-of-a-mock gymnastics.&lt;/li&gt;
&lt;li&gt;Adding a new failure mode is cheap and isolated: write the failing test, write the function, done.&lt;/li&gt;
&lt;li&gt;The I/O layer that actually lists objects is separated out and tested against client-go's &lt;strong&gt;fake clientset&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built the whole thing TDD — failing test first — and this pure-function shape is what made that pleasant instead of painful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Go stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Built directly on &lt;strong&gt;client-go&lt;/strong&gt;, the same library &lt;code&gt;kubectl&lt;/code&gt; and operators use. No framework.&lt;/li&gt;
&lt;li&gt;The CLI uses the &lt;strong&gt;standard-library &lt;code&gt;flag&lt;/code&gt;&lt;/strong&gt; package — no Cobra. For a focused tool it's plenty.&lt;/li&gt;
&lt;li&gt;Single static binary (&lt;code&gt;CGO_ENABLED=0&lt;/code&gt;); install with &lt;code&gt;go install github.com/imantaba/kubeagent@latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;There's also a &lt;code&gt;watch&lt;/code&gt; daemon that runs in-cluster: informers + a heartbeat ticker + an HTTP server, re-running the same deterministic diagnosis on change and exposing findings as &lt;strong&gt;Prometheus metrics&lt;/strong&gt; — with hand-rolled metric output and zero extra dependencies (informers ship with client-go).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/imantaba/kubeagent@latest
kubeagent scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's MIT-licensed and read-only, so it's safe to point at any cluster you can already reach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/imantaba/kubeagent" rel="noopener noreferrer"&gt;https://github.com/imantaba/kubeagent&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://k8sproject.top" rel="noopener noreferrer"&gt;https://k8sproject.top&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, I'd love to know what failure modes you hit that it &lt;em&gt;doesn't&lt;/em&gt; catch yet — the detectors are cheap to add, and that's exactly the feedback that makes it better.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>go</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
