Skip to content

feat(api): topology names the sections Kubernetes refused to read - #509

Merged
bmscomp merged 2 commits into
mainfrom
feat/topology-unread
Oct 26, 2026
Merged

bmscomp merged 2 commits into
mainfrom
feat/topology-unread

Conversation

@bmscomp

@bmscomp bmscomp commented Oct 26, 2026

Copy link
Copy Markdown
Owner

Why

On an admin-less install (rbac.scope=namespace), the Kates API may read only part of the Kafka namespace: the chart's Role grants what a conformity check reads, and nodes are cluster-scoped. ClusterTopologyService swallowed every failed read, so a refused section looked like an empty cluster, for example "0 topics", no services and no node list. When the Role was missing entirely, the refused pod list came back as "requires the backend to be running on Kubernetes", which was wrong: the backend was on Kubernetes but lacked permission.

This is PR 1 of the admin-less topology study: report what was refused before adding fallbacks or new grants.

What

  • GET /api/cluster/topology adds unread: each section Kubernetes refused a read for (HTTP 403), with the API server's message, which names the verb, resource and namespace. It is always present, and empty when everything was read. A refused Kafka resource names every section built from it (cluster, kafkaConfig, entityOperator, cruiseControl, kafkaExporter, certificates, metrics). Sections keep their current, possibly empty, values, so existing consumers (REST, gRPC) see no change.
  • The pod-list check names the grant. A 403 now answers 503 with "The Kates API's service account may not list pods in <ns>: bind it a Role there that grants it, such as the kates chart's with rbac.scope=namespace. The API server said: …", the same wording conformity uses. Other failures keep the old "not on Kubernetes" message.
  • kates cluster topology warns under the banner with the unread sections, grouped by message. On an error it prints the backend's message instead of the fixed "only available when deployed on Kubernetes" text.
  • MCP cluster_topology reports topology.unread (section + fenced reason) for the sections it reads: kafka, nodePools and nodes. Its 503 explanation and the topology-unread-looks-empty caveat mention the refused case. The golden file is regenerated.
  • Troubleshooting appendix: the topology row covers the new 503 message for both scopes, and a new row explains the "Kubernetes refused some reads" warning.
  • CLI line references into ClusterTopologyService.java and ClusterResource.java are re-pointed after the shift.

Deliberately not done

  • Fallbacks (PR 2 of the study): nodes hosting Kafka pods from pod.spec.nodeName, zones from EndpointSlices, the Strimzi version from status.operatorLastSuccessfulVersion, and Endpoints → EndpointSlices.
  • A topology Role behind rbac.topologyReads (PR 3).
  • gRPC GetClusterTopology: unread would need a kates.proto change, and it can't be regenerated on this Mac.

Tests

New: ClusterTopologyServiceTest, which uses the fabric8 mock server with real API-server 403 Status bodies:

  • a cluster read in full has an empty unread;
  • refused reads of the Kafka resource, nodes, KafkaTopics (label-selected) and NetworkPolicies give exactly the expected sections, in the API server's words;
  • a refused pod list names the grant.

CLI: TestClusterTopologyCmdNamesTheUnreadSections, TestClusterTopologyCmdPassesTheBackendsRefusalOn and TestMCPClusterTopologyNamesRefusedReads.

Run locally, all green:

  • ./mvnw test -Dquarkus.grpc.codegen.skip=true -Dquarkus.http.test-port=0: 1,738 tests, 0 failures. spotless:check passes.
  • cli: go test ./..., go vet ./..., golangci-lint run --new-from-rev=origin/main ./... (0 issues), govulncheck ./...; scripts/check-cli-style.sh, scripts/check-cli-compat.sh.
  • Book: check-book-style.sh, gen-version-matrix.sh --check, gen-chart-table.sh --check, check-book-contrast.py, check-mermaid.sh. In a scratch-copy render, HTML plus check-book-fragments.py (8,137 links OK) and the PDF both pass. The PDF's missing glyphs are only the usual six local emoji.

Left to CI: *IT tests, the native image, Kind integration and the Docker builds (Docker is off here). I haven't run this against a live admin-less install.

An admin-less Kates (rbac.scope=namespace) may read only part of the Kafka
namespace, and nodes not at all. ClusterTopologyService swallowed every
failed read, so a refused section looked like an empty cluster: no topics,
no services, no nodes.

- GET /api/cluster/topology adds `unread`: each section Kubernetes refused
  a read for (HTTP 403), with the API server's message, which names the
  verb, resource and namespace. A refused Kafka resource names every
  section built from it.
- A refused pod list in the Kafka namespace now says so, and how to grant
  it, instead of "requires the backend to be running on Kubernetes".
- kates cluster topology warns with the unread sections, grouped by
  message, and shows the backend's message on a 503.
- The cluster_topology MCP tool reports topology.unread for the sections
  it reads (kafka, nodePools, nodes), the API server's message fenced.
- Troubleshooting appendix: both new messages.
# Conflicts:
#	docs/book/appendix-b-troubleshooting.md
@bmscomp
bmscomp merged commit 6cbae12 into main Oct 26, 2026
60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant