k8stools 2.2.1__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.
- {k8stools-2.2.1 → k8stools-2.3.0}/CHANGELOG.md +59 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/CLAUDE.md +15 -3
- {k8stools-2.2.1 → k8stools-2.3.0}/PKG-INFO +36 -7
- {k8stools-2.2.1 → k8stools-2.3.0}/README.md +35 -6
- {k8stools-2.2.1 → k8stools-2.3.0}/designs/mock-state-capture.md +6 -2
- {k8stools-2.2.1 → k8stools-2.3.0}/pyproject.toml +1 -1
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/__init__.py +3 -1
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/capture.py +2 -0
- k8stools-2.3.0/src/k8stools/fixtures/otel-demo.json +1088 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/k8s_tools.py +196 -6
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/mock_state.py +17 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/mock_tools.py +7 -0
- k8stools-2.3.0/tests/test_builtin_fixture.py +180 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_capture.py +1 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_events.py +3 -2
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_k8s_tools_realk8s.py +54 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_mock_tools.py +5 -5
- k8stools-2.3.0/tests/test_node_conditions.py +132 -0
- k8stools-2.3.0/tests/test_workloads.py +233 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/uv.lock +1 -1
- k8stools-2.2.1/src/k8stools/fixtures/otel-demo.json +0 -570
- {k8stools-2.2.1 → k8stools-2.3.0}/.claude/settings.local.json +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/.github/workflows/unit-tests.yml +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/.gitignore +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/.python-version +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/.vscode/mcp.json +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/.vscode/settings.json +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/LICENSE +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/chat-with-cluster.png +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/docs/ROADMAP.md +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/envrc.template +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/mcp_client.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/mcp_server.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/src/k8stools/redaction.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/conftest.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_cluster_binding.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_duration_format.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_k8s_tools.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_log_decoding.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_mcp_client.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_mock_state.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_new_tools.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_redaction.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_replicaset_tool.py +0 -0
- {k8stools-2.2.1 → k8stools-2.3.0}/tests/test_version.py +0 -0
|
@@ -3,6 +3,65 @@
|
|
|
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
|
+
|
|
6
65
|
## 2.2.1 — 2026-09-29
|
|
7
66
|
|
|
8
67
|
### Documentation
|
|
@@ -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` (
|
|
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
|
|
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.
|
|
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
|
|
505
|
-
|
|
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
|
|
508
|
-
|
|
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
|
|
285
|
-
|
|
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
|
|
288
|
-
|
|
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
|
|
@@ -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.
|
|
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)
|