k8stools 2.2.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {k8stools-2.2.0 → k8stools-2.3.0}/CHANGELOG.md +76 -0
  2. {k8stools-2.2.0 → k8stools-2.3.0}/CLAUDE.md +15 -3
  3. {k8stools-2.2.0 → k8stools-2.3.0}/PKG-INFO +36 -7
  4. {k8stools-2.2.0 → k8stools-2.3.0}/README.md +35 -6
  5. {k8stools-2.2.0 → k8stools-2.3.0}/designs/mock-state-capture.md +6 -2
  6. {k8stools-2.2.0 → k8stools-2.3.0}/pyproject.toml +1 -1
  7. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/__init__.py +3 -1
  8. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/capture.py +2 -0
  9. k8stools-2.3.0/src/k8stools/fixtures/otel-demo.json +1088 -0
  10. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/k8s_tools.py +233 -7
  11. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/mock_state.py +17 -0
  12. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/mock_tools.py +7 -0
  13. k8stools-2.3.0/tests/test_builtin_fixture.py +180 -0
  14. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_capture.py +1 -0
  15. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_events.py +17 -2
  16. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_k8s_tools_realk8s.py +54 -0
  17. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_mock_tools.py +5 -5
  18. k8stools-2.3.0/tests/test_node_conditions.py +132 -0
  19. k8stools-2.3.0/tests/test_workloads.py +233 -0
  20. {k8stools-2.2.0 → k8stools-2.3.0}/uv.lock +1 -1
  21. k8stools-2.2.0/src/k8stools/fixtures/otel-demo.json +0 -570
  22. {k8stools-2.2.0 → k8stools-2.3.0}/.claude/settings.local.json +0 -0
  23. {k8stools-2.2.0 → k8stools-2.3.0}/.github/workflows/unit-tests.yml +0 -0
  24. {k8stools-2.2.0 → k8stools-2.3.0}/.gitignore +0 -0
  25. {k8stools-2.2.0 → k8stools-2.3.0}/.python-version +0 -0
  26. {k8stools-2.2.0 → k8stools-2.3.0}/.vscode/mcp.json +0 -0
  27. {k8stools-2.2.0 → k8stools-2.3.0}/.vscode/settings.json +0 -0
  28. {k8stools-2.2.0 → k8stools-2.3.0}/LICENSE +0 -0
  29. {k8stools-2.2.0 → k8stools-2.3.0}/chat-with-cluster.png +0 -0
  30. {k8stools-2.2.0 → k8stools-2.3.0}/docs/ROADMAP.md +0 -0
  31. {k8stools-2.2.0 → k8stools-2.3.0}/envrc.template +0 -0
  32. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/mcp_client.py +0 -0
  33. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/mcp_server.py +0 -0
  34. {k8stools-2.2.0 → k8stools-2.3.0}/src/k8stools/redaction.py +0 -0
  35. {k8stools-2.2.0 → k8stools-2.3.0}/tests/conftest.py +0 -0
  36. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_cluster_binding.py +0 -0
  37. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_duration_format.py +0 -0
  38. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_k8s_tools.py +0 -0
  39. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_log_decoding.py +0 -0
  40. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_mcp_client.py +0 -0
  41. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_mock_state.py +0 -0
  42. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_new_tools.py +0 -0
  43. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_redaction.py +0 -0
  44. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_replicaset_tool.py +0 -0
  45. {k8stools-2.2.0 → k8stools-2.3.0}/tests/test_version.py +0 -0
@@ -3,6 +3,82 @@
3
3
  All notable changes to k8stools. Versions follow [semantic versioning](https://semver.org/);
4
4
  dates are release-tag dates.
5
5
 
6
+ ## 2.3.0 — 2026-10-03
7
+
8
+ ### Added
9
+ - **`conditions_since` on nodes** (issue #9). `get_node_summaries` now reports,
10
+ for each node condition, the time since its status last changed (the API's
11
+ `lastTransitionTime`, which was dropped), next to the unchanged `conditions`
12
+ map. A condition with no transition time is left out. `print_node_summaries`
13
+ gains a READY-SINCE column.
14
+ - The tool description says what it does not show: a restart in which the node
15
+ comes back Ready without being marked NotReady or Unknown in between does not
16
+ reset it. On a minikube cluster stopped and started with its control plane,
17
+ `Ready` stayed "True since the node was created", 167 days, across a reboot.
18
+ It points to the node's `Starting`/`Rebooted` events and the start times of
19
+ kube-proxy and the control-plane containers for dating a node's last start.
20
+ - Captures store it as `conditions_since_seconds` and replay advances it like
21
+ every other age; captures from before 2.3.0 replay it as an empty map. (The
22
+ capture format had no handling for a map of durations: without it the values
23
+ would have been replayed as fixed spans that never advanced.)
24
+ - **`get_daemonset_summaries` tool** (issue #10), like `kubectl get daemonsets -o wide`:
25
+ desired, current, ready, up-to-date, available and misscheduled counts, the pod
26
+ template's node selector and images, update strategy, and age. Until now a
27
+ DaemonSet was visible only through its pods, whose `<name>-<5 chars>` names
28
+ can't be told apart from other names. Captures store DaemonSets; captures from
29
+ before 2.3.0 replay none. `print_daemonset_summaries` is its companion.
30
+ - **`owner` on pods.** `get_pod_summaries` reports each pod's controlling owner
31
+ as `"Kind/name"` (e.g. `"DaemonSet/otel-collector-agent"`), so pods can be
32
+ grouped into workloads without guessing from name suffixes. It is the direct
33
+ owner: a Deployment's pods name a replica set, whose `owner_deployment` names
34
+ the Deployment. `None` for a pod nobody manages; captures from before 2.3.0
35
+ replay `None`. `print_pod_summaries` gains an OWNER column.
36
+ - **The built-in `--mock` fixture shows the 2.3.0 fields** and now describes a
37
+ cluster that could exist. It gains an `otel-collector-agent` DaemonSet and its
38
+ pod, pod owners, node condition times, and event counts. Every workload now has
39
+ its pods (`test-deployment`'s three, and `postgres-0`, which a PVC already
40
+ named as mounting it), services select the pods they front, and the
41
+ `postgres` StatefulSet's headless service exists. It also fixes
42
+ contradictions in the old data that an agent would reason from:
43
+ - the `ad` pod ran its previous revision's image;
44
+ - its last instance's status said it ran 2 seconds while its log covered 2
45
+ minutes;
46
+ - its 93 restarts were more than its age allows at the 5-minute backoff cap
47
+ (now 67);
48
+ - several logs were dated 14 months before the capture;
49
+ - the namespaces were older than anything else in the cluster;
50
+ - `test-pod-123`'s spec declared one container while its status had two.
51
+
52
+ The `ad` container now shows the case `previous=True` is for: it restarted 58
53
+ seconds ago, so its current log is JVM startup and the OOM is only in the
54
+ previous one. A new test file keeps the fixture consistent.
55
+
56
+ ### Changed
57
+ - **A capture needs `list` on `daemonsets`** (apps). `k8s-capture-state` now
58
+ captures DaemonSets, so it fails where the cluster role does not grant them,
59
+ rather than writing a capture that replays as if there were none. Add
60
+ `daemonsets` to the role's apps resources (see README, "Permissions").
61
+
62
+ ### Documentation
63
+ - README lists the read-only RBAC rules that cover every tool.
64
+
65
+ ## 2.2.1 — 2026-09-29
66
+
67
+ ### Documentation
68
+ - **Event records can lag behind what they count** (follow-up to issue #8). The
69
+ kubelet counts every occurrence but, by default, writes at most one update per
70
+ object and event type every 5 minutes once a burst of 25 is used up (client-go's
71
+ event spam filter). A crash-looping pod's `Created` record read `last_seen`
72
+ 29 minutes while the container had restarted 46 seconds earlier, then its count
73
+ jumped by six in one update. The `get_pod_events` and `get_events` descriptions
74
+ now say so: `count / (first_seen - last_seen)` still gives the rate, since both
75
+ lag together, but `last_seen` is not the time of the last restart (use the
76
+ container status or `PodSummary.last_restart`), and counts for different reasons
77
+ can differ by a few for the same restarts.
78
+ - `PodSummary.last_restart` is described as what it is: time since a container
79
+ last *terminated*. For a container waiting in CrashLoopBackOff, that is its last
80
+ crash, not a restart.
81
+
6
82
  ## 2.2.0 — 2026-09-28
7
83
 
8
84
  ### Added
@@ -14,7 +14,7 @@ src/k8stools/
14
14
  mcp_server.py - MCP server (stdio or streamable-http transport)
15
15
  mcp_client.py - MCP client used for manual testing
16
16
  fixtures/
17
- otel-demo.json - Built-in capture served by `--mock` (real Minikube + OTel Demo)
17
+ otel-demo.json - Built-in capture served by `--mock` (hand-maintained, modeled on Minikube + OTel Demo)
18
18
  tests/
19
19
  test_k8s_tools.py - Unit tests for the original tools (mocked K8s API)
20
20
  test_new_tools.py - Unit tests for the 1.1.0 tools (ConfigMaps, CronJobs/Jobs, PVCs, StatefulSets, events, log enhancements)
@@ -26,6 +26,9 @@ tests/
26
26
  test_mock_tools.py - Tests for mock_tools module (incl. parity with k8s_tools.TOOLS)
27
27
  test_events.py - Event aggregation (count/first_seen), incl. the events.k8s.io fallback
28
28
  test_cluster_binding.py - Cluster selection: configure(), the shared binding, get_cluster_info, server flags
29
+ test_node_conditions.py - NodeSummary.conditions_since (condition transition times), incl. capture replay
30
+ test_workloads.py - get_daemonset_summaries and PodSummary.owner, incl. capture replay
31
+ test_builtin_fixture.py - The built-in fixture describes a cluster that could exist (owners, images, ages, log windows)
29
32
  test_log_decoding.py - Log decoding at the API boundary (bytes vs str), `previous` plumbing, log redaction
30
33
  test_mcp_client.py - MCP client tests
31
34
  test_version.py - Asserts pyproject and package __version__ agree
@@ -224,6 +227,9 @@ Invariants worth preserving when touching this code:
224
227
  but read back unchanged. Replay tests that freeze the clock can't tell the two
225
228
  apart (frozen elapsed time is zero), so test a new duration field on an
226
229
  advancing clock with a `server_start_time` in the past.
230
+ A `dict[str, Duration]` (`NodeSummary.conditions_since`) is stored as a map
231
+ under `<field>_seconds` and each value advances (the codec looks through a
232
+ dict to its value type).
227
233
  - **`MockState` must match the real tool's documented behavior**, not just its
228
234
  types — filter semantics, result ordering (`get_replicaset_summaries` re-sorts
229
235
  at query time), and which failures raise vs. return empty.
@@ -235,7 +241,13 @@ Invariants worth preserving when touching this code:
235
241
  synthesizes answers for unknown pod names.
236
242
 
237
243
  The built-in fixture lives under `src/k8stools/` (not `tests/`) because
238
- `--mock` serves it, so it has to ship in the wheel.
244
+ `--mock` serves it, so it has to ship in the wheel. It is **hand-maintained**, not
245
+ a raw capture (it began as the pre-2.0 mock data, re-encoded): a real capture is
246
+ megabytes, and a test cluster has no CronJobs, Jobs or PVCs to show. When a tool
247
+ gains a field, add it to the fixture by hand, with values that agree with the
248
+ rest of it. `tests/test_builtin_fixture.py` checks that it does (owners exist and
249
+ run the pod's images, nothing predates its namespace, logs fall inside the
250
+ instance that wrote them, restarts fit the pod's age at the backoff cap).
239
251
 
240
252
  Captures are written **indented, and uncompressed by default**, on purpose: these
241
253
  files get re-captured and re-committed, and git deltas successive versions of
@@ -310,4 +322,4 @@ Example `mcp.json`:
310
322
 
311
323
  - Read-only tools first; state-modifying tools will be kept in a separate module when added
312
324
  - Quality over quantity: well-documented, strongly typed, useful for real monitoring/RCA agents
313
- - Mock data captured from a real Minikube instance running the OpenTelemetry Demo app
325
+ - Mock data modeled on a Minikube instance running the OpenTelemetry Demo app, kept internally consistent by tests
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: k8stools
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Tools and an MCP server for building Kubernetes agents
5
5
  Project-URL: Homepage, https://github.com/BenedatLLC/k8stools
6
6
  Project-URL: Changelog, https://github.com/BenedatLLC/k8stools/blob/main/CHANGELOG.md
@@ -277,6 +277,32 @@ replayable file).
277
277
  What changed in each release is in the
278
278
  [changelog](https://github.com/BenedatLLC/k8stools/blob/main/CHANGELOG.md).
279
279
 
280
+ ### Permissions
281
+
282
+ The tools only read, with the `get` and `list` verbs. A read-only ClusterRole
283
+ that covers every tool:
284
+
285
+ ```yaml
286
+ apiVersion: rbac.authorization.k8s.io/v1
287
+ kind: ClusterRole
288
+ metadata:
289
+ name: k8stools-reader
290
+ rules:
291
+ - apiGroups: [""]
292
+ resources: [namespaces, nodes, pods, pods/log, events, services, configmaps,
293
+ persistentvolumeclaims]
294
+ verbs: [get, list]
295
+ - apiGroups: [apps]
296
+ resources: [deployments, replicasets, statefulsets, daemonsets]
297
+ verbs: [get, list]
298
+ - apiGroups: [batch]
299
+ resources: [jobs, cronjobs]
300
+ verbs: [get, list]
301
+ ```
302
+
303
+ `get_cluster_info` also reads the server version (`/version`), which every
304
+ authenticated user can read by default. No tool reads Secrets.
305
+
280
306
  ## Current tools
281
307
 
282
308
  These are the tools we define:
@@ -284,7 +310,7 @@ These are the tools we define:
284
310
  * `get_cluster_info` - which cluster the tools are answering from: kubeconfig context, API server URL and version, or the capture being replayed
285
311
  * `get_namespaces` - get a list of namespaces, like `kubectl get namespace`
286
312
  * `get_node_summaries` - get a list of nodes, like `kubectl get nodes -o wide` (includes capacity/allocatable/conditions/taints/labels)
287
- * `get_pod_summaries` - get a list of pods, like `kubectl get pods -o wide`
313
+ * `get_pod_summaries` - get a list of pods, like `kubectl get pods -o wide`, with each pod's controlling owner (e.g. `DaemonSet/otel-collector-agent`)
288
314
  * `get_pod_container_statuses` - return the status for each of the container in a pod
289
315
  * `get_pod_events` - return the events for a pod
290
316
  * `get_pod_spec` - retrieves the spec for a given pod
@@ -295,6 +321,7 @@ These are the tools we define:
295
321
  * `get_configmap_summaries` - get a list of ConfigMaps, like `kubectl get configmaps`
296
322
  * `get_configmap` - retrieve the full contents of a single ConfigMap
297
323
  * `get_statefulset_summaries` - get a list of StatefulSets, like `kubectl get statefulsets`
324
+ * `get_daemonset_summaries` - get a list of DaemonSets, like `kubectl get daemonsets -o wide` (includes node selector and images)
298
325
  * `get_cronjob_summaries` - get a list of CronJobs, like `kubectl get cronjobs`
299
326
  * `get_job_summaries` - get a list of Jobs, like `kubectl get jobs`
300
327
  * `get_logs_for_job` - retrieve logs from a Job's most-recent pod
@@ -315,6 +342,7 @@ We also define a set of associated "print_" functions that are helpful in debugg
315
342
  * `print_configmap_summaries`
316
343
  * `print_configmap`
317
344
  * `print_statefulset_summaries`
345
+ * `print_daemonset_summaries`
318
346
  * `print_cronjob_summaries`
319
347
  * `print_job_summaries`
320
348
  * `print_pvc_summaries`
@@ -501,11 +529,12 @@ data: {"jsonrpc":"2.0","id":1,"result":{"tools":[.... long text elided ...]}}
501
529
  ## Mock tools
502
530
  When building agents, it can be helpful to test them against *mock* versions that do
503
531
  not go against a real cluster, but return realistic values. The module
504
- `k8stools.mock_tools` does just that. The data values were captured when running
505
- against a real Minikube instance running the
532
+ `k8stools.mock_tools` does just that. The data is a small, hand-maintained
533
+ cluster modeled on a Minikube instance running the
506
534
  [Open Telemetry Demo](https://github.com/open-telemetry/opentelemetry-demo)
507
- application. When running the MCP server, this may be enabled by using the
508
- `--mock` command line option.
535
+ application: a crash-looping `ad` service mid-upgrade, a DaemonSet, a CronJob and
536
+ its Job, a StatefulSet with PVCs, and a few events. When running the MCP server,
537
+ this may be enabled by using the `--mock` command line option.
509
538
 
510
539
  The mock serves a *capture* — a JSON snapshot of one cluster — rather than a set of
511
540
  per-tool canned answers, so the tools agree with each other: a pod that is not in the
@@ -580,7 +609,7 @@ Everything the tools can read, so that every tool answers on replay:
580
609
  |---|---|
581
610
  | Cluster | context name, API server URL and version, so `get_cluster_info` answers on replay (not the kubeconfig path, which names a file on the capturing machine) |
582
611
  | Cluster-wide | namespaces, nodes |
583
- | Per namespace | deployments, replica sets, services, statefulsets, cronjobs, jobs, PVCs, events |
612
+ | Per namespace | deployments, replica sets, services, statefulsets, daemonsets, cronjobs, jobs, PVCs, events |
584
613
  | ConfigMaps | summary **and** full contents, so `get_configmap` works too |
585
614
  | Per pod | summary, labels, container statuses, spec, and per-container logs |
586
615
 
@@ -57,6 +57,32 @@ replayable file).
57
57
  What changed in each release is in the
58
58
  [changelog](https://github.com/BenedatLLC/k8stools/blob/main/CHANGELOG.md).
59
59
 
60
+ ### Permissions
61
+
62
+ The tools only read, with the `get` and `list` verbs. A read-only ClusterRole
63
+ that covers every tool:
64
+
65
+ ```yaml
66
+ apiVersion: rbac.authorization.k8s.io/v1
67
+ kind: ClusterRole
68
+ metadata:
69
+ name: k8stools-reader
70
+ rules:
71
+ - apiGroups: [""]
72
+ resources: [namespaces, nodes, pods, pods/log, events, services, configmaps,
73
+ persistentvolumeclaims]
74
+ verbs: [get, list]
75
+ - apiGroups: [apps]
76
+ resources: [deployments, replicasets, statefulsets, daemonsets]
77
+ verbs: [get, list]
78
+ - apiGroups: [batch]
79
+ resources: [jobs, cronjobs]
80
+ verbs: [get, list]
81
+ ```
82
+
83
+ `get_cluster_info` also reads the server version (`/version`), which every
84
+ authenticated user can read by default. No tool reads Secrets.
85
+
60
86
  ## Current tools
61
87
 
62
88
  These are the tools we define:
@@ -64,7 +90,7 @@ These are the tools we define:
64
90
  * `get_cluster_info` - which cluster the tools are answering from: kubeconfig context, API server URL and version, or the capture being replayed
65
91
  * `get_namespaces` - get a list of namespaces, like `kubectl get namespace`
66
92
  * `get_node_summaries` - get a list of nodes, like `kubectl get nodes -o wide` (includes capacity/allocatable/conditions/taints/labels)
67
- * `get_pod_summaries` - get a list of pods, like `kubectl get pods -o wide`
93
+ * `get_pod_summaries` - get a list of pods, like `kubectl get pods -o wide`, with each pod's controlling owner (e.g. `DaemonSet/otel-collector-agent`)
68
94
  * `get_pod_container_statuses` - return the status for each of the container in a pod
69
95
  * `get_pod_events` - return the events for a pod
70
96
  * `get_pod_spec` - retrieves the spec for a given pod
@@ -75,6 +101,7 @@ These are the tools we define:
75
101
  * `get_configmap_summaries` - get a list of ConfigMaps, like `kubectl get configmaps`
76
102
  * `get_configmap` - retrieve the full contents of a single ConfigMap
77
103
  * `get_statefulset_summaries` - get a list of StatefulSets, like `kubectl get statefulsets`
104
+ * `get_daemonset_summaries` - get a list of DaemonSets, like `kubectl get daemonsets -o wide` (includes node selector and images)
78
105
  * `get_cronjob_summaries` - get a list of CronJobs, like `kubectl get cronjobs`
79
106
  * `get_job_summaries` - get a list of Jobs, like `kubectl get jobs`
80
107
  * `get_logs_for_job` - retrieve logs from a Job's most-recent pod
@@ -95,6 +122,7 @@ We also define a set of associated "print_" functions that are helpful in debugg
95
122
  * `print_configmap_summaries`
96
123
  * `print_configmap`
97
124
  * `print_statefulset_summaries`
125
+ * `print_daemonset_summaries`
98
126
  * `print_cronjob_summaries`
99
127
  * `print_job_summaries`
100
128
  * `print_pvc_summaries`
@@ -281,11 +309,12 @@ data: {"jsonrpc":"2.0","id":1,"result":{"tools":[.... long text elided ...]}}
281
309
  ## Mock tools
282
310
  When building agents, it can be helpful to test them against *mock* versions that do
283
311
  not go against a real cluster, but return realistic values. The module
284
- `k8stools.mock_tools` does just that. The data values were captured when running
285
- against a real Minikube instance running the
312
+ `k8stools.mock_tools` does just that. The data is a small, hand-maintained
313
+ cluster modeled on a Minikube instance running the
286
314
  [Open Telemetry Demo](https://github.com/open-telemetry/opentelemetry-demo)
287
- application. When running the MCP server, this may be enabled by using the
288
- `--mock` command line option.
315
+ application: a crash-looping `ad` service mid-upgrade, a DaemonSet, a CronJob and
316
+ its Job, a StatefulSet with PVCs, and a few events. When running the MCP server,
317
+ this may be enabled by using the `--mock` command line option.
289
318
 
290
319
  The mock serves a *capture* — a JSON snapshot of one cluster — rather than a set of
291
320
  per-tool canned answers, so the tools agree with each other: a pod that is not in the
@@ -360,7 +389,7 @@ Everything the tools can read, so that every tool answers on replay:
360
389
  |---|---|
361
390
  | Cluster | context name, API server URL and version, so `get_cluster_info` answers on replay (not the kubeconfig path, which names a file on the capturing machine) |
362
391
  | Cluster-wide | namespaces, nodes |
363
- | Per namespace | deployments, replica sets, services, statefulsets, cronjobs, jobs, PVCs, events |
392
+ | Per namespace | deployments, replica sets, services, statefulsets, daemonsets, cronjobs, jobs, PVCs, events |
364
393
  | ConfigMaps | summary **and** full contents, so `get_configmap` works too |
365
394
  | Per pod | summary, labels, container statuses, spec, and per-container logs |
366
395
 
@@ -151,6 +151,7 @@ A single JSON file per capture. Top-level keys:
151
151
  "services": [ ... ],
152
152
  "configmaps": [ ... ],
153
153
  "statefulsets": [ ... ],
154
+ "daemonsets": [ ... ],
154
155
  "cronjobs": [ ... ],
155
156
  "jobs": [ ... ],
156
157
  "pvcs": [ ... ],
@@ -162,7 +163,9 @@ The `configmaps`..`events` keys were added alongside the 1.1.0 tool batch
162
163
  (ConfigMaps, StatefulSets, CronJobs/Jobs, PVCs, and cluster-wide events). See
163
164
  [Resource records added in 1.1.0](#resource-records-added-in-110) below for their
164
165
  shapes. `replicasets` was added alongside the 1.2.0 tool batch — see
165
- [Resource records added in 1.2.0](#resource-records-added-in-120).
166
+ [Resource records added in 1.2.0](#resource-records-added-in-120). `daemonsets` was added
167
+ in 2.3.0: `DaemonSetSummary` fields with `age` → `age_seconds`; captures without it
168
+ replay no DaemonSets.
166
169
 
167
170
  `redacted` records which mode the capture was taken in — see
168
171
  [Redaction](#redaction). It exists so a consumer can tell without guessing;
@@ -448,6 +451,7 @@ class MockState:
448
451
  def get_configmap_summaries(self, namespace: str | None = None) -> list[ConfigMapSummary]: ...
449
452
  def get_configmap(self, name: str, namespace: str) -> dict[str, Any]: ...
450
453
  def get_statefulset_summaries(self, namespace: str | None = None) -> list[StatefulSetSummary]: ...
454
+ def get_daemonset_summaries(self, namespace: str | None = None) -> list[DaemonSetSummary]: ... # 2.3.0
451
455
  def get_cronjob_summaries(self, namespace: str | None = None) -> list[CronJobSummary]: ...
452
456
  def get_job_summaries(self, namespace: str | None = None) -> list[JobSummary]: ...
453
457
  def get_logs_for_job(self, job_name: str, namespace: str, container_name: str | None = None,
@@ -538,7 +542,7 @@ Behavior:
538
542
  (`configmaps`, `statefulsets`, `cronjobs`, `jobs`, `pvcs`), the 1.2.0 addition
539
543
  (`replicasets`), and a namespace/cluster-wide `get_events` sweep. If
540
544
  `--namespace` is given, only captures namespaced resources (pods/deployments/
541
- replicasets/services/configmaps/statefulsets/cronjobs/jobs/pvcs/events) in those
545
+ replicasets/services/configmaps/statefulsets/daemonsets/cronjobs/jobs/pvcs/events) in those
542
546
  namespaces; namespaces and nodes are always captured in full.
543
547
  3. For each pod and each container, calls `get_logs_for_pod_and_container` unless `--no-logs`. Default log cap: 1000 lines (override with `--max-log-lines`).
544
548
  4. For each container whose `restart_count > 0`, calls the same tool with
@@ -1,7 +1,7 @@
1
1
 
2
2
  [project]
3
3
  name = "k8stools"
4
- version = "2.2.0"
4
+ version = "2.3.0"
5
5
  description = "Tools and an MCP server for building Kubernetes agents"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.12"
@@ -38,6 +38,7 @@ These are the tools we define:
38
38
  * get_configmap_summaries - get a list of ConfigMaps, like `kubectl get configmaps`
39
39
  * get_configmap - retrieve the full contents of a single ConfigMap
40
40
  * get_statefulset_summaries - get a list of StatefulSets, like `kubectl get statefulsets`
41
+ * get_daemonset_summaries - get a list of DaemonSets, like `kubectl get daemonsets`
41
42
  * get_cronjob_summaries - get a list of CronJobs, like `kubectl get cronjobs`
42
43
  * get_job_summaries - get a list of Jobs, like `kubectl get jobs`
43
44
  * get_logs_for_job - retrieve logs from a Job's most-recent pod
@@ -59,6 +60,7 @@ We also define a set of associated "print_" functions that are helpful in debugg
59
60
  * print_configmap_summaries
60
61
  * print_configmap
61
62
  * print_statefulset_summaries
63
+ * print_daemonset_summaries
62
64
  * print_cronjob_summaries
63
65
  * print_job_summaries
64
66
  * print_pvc_summaries
@@ -78,4 +80,4 @@ the built-in OTel Demo snapshot by default, or your own via load_mock_state() or
78
80
  the server's --state-file option.
79
81
  """
80
82
 
81
- __version__ = "2.2.0"
83
+ __version__ = "2.3.0"
@@ -185,6 +185,7 @@ def capture_state(namespaces: Optional[list[str]] = None,
185
185
  "services": [],
186
186
  "configmaps": [],
187
187
  "statefulsets": [],
188
+ "daemonsets": [],
188
189
  "cronjobs": [],
189
190
  "jobs": [],
190
191
  "pvcs": [],
@@ -196,6 +197,7 @@ def capture_state(namespaces: Optional[list[str]] = None,
196
197
  state["replicasets"] += _encode_all(redactor(k8s_tools.get_replicaset_summaries(ns)), captured_at)
197
198
  state["services"] += _encode_all(redactor(k8s_tools.get_service_summaries(ns)), captured_at)
198
199
  state["statefulsets"] += _encode_all(redactor(k8s_tools.get_statefulset_summaries(ns)), captured_at)
200
+ state["daemonsets"] += _encode_all(redactor(k8s_tools.get_daemonset_summaries(ns)), captured_at)
199
201
  state["cronjobs"] += _encode_all(redactor(k8s_tools.get_cronjob_summaries(ns)), captured_at)
200
202
  state["jobs"] += _encode_all(redactor(k8s_tools.get_job_summaries(ns)), captured_at)
201
203
  state["pvcs"] += _encode_all(redactor(k8s_tools.get_pvc_summaries(ns)), captured_at)