Repository navigation
Conversation
Extend GET /api/v1/dashboard/summary with three new additive fields
that surface platform-wide telemetry for the operator:
- hosts: node count, ready nodes, allocatable CPU and memory
(millicores / bytes), distinct kubelet versions, OS images
and architectures. Sourced from core/v1/nodes via
KubeVirtDriver.ClusterMetrics.
- hosts.usage: real CPU and memory consumption aggregated from
metrics.k8s.io/v1beta1/nodes (Tier 2 — requires
metrics-server, documented prerequisite in helm-charts).
Served with a 30s in-memory cache, and degrades gracefully
when the API is absent, Forbidden, or transiently
unavailable.
- storage: tenant-scoped PVC capacity and consumption (Bound vs
reserved) plus the count. Sourced from the existing PVC
list path in KubeVirtDriver.
- addons: cluster-wide health of critical dependencies
(KubeVirt, CDI, Multus, metrics-server, networking, and
any chart that follows the Helm label conventions),
discovered dynamically from CustomResourceDefinitions
with the 'app.kubernetes.io/name' label and a small alias
table for the well-known API groups. Each entry reports
'ok' (Established) or 'degraded'.
All four reads are fail-soft: 404, 403, 401, 503, timeouts, and
connection-refused return (nil, nil) so the endpoint never 500s.
Memory mode and nil client leave the fields empty.
docs/rbac-contract.yaml documents the dynamic-client access to
metrics.k8s.io in a new dynamicClient section — rbac_contract_test
keeps passing because the typed client AST scan does not see
Discovery().RESTClient().
PlatformService gains a kv field so the service can read the
hypervisor. CHANGELOG entry under Unreleased. A new
2026-10-08-dashboard-host-metrics.md captures the design and the
locked decisions for this slice.
Surface the new hosts/storage/addons fields from the previous slice
on the existing Dashboard page:
- Rename the section title from 'Cluster nodes' to 'Cluster
overview' to fit the broader scope (nodes + capacity + addons).
- Four cards inside the section: Nodes, Ready nodes, Allocatable
CPU, Allocatable memory. The CPU/memory cards now carry a
usage bar (used / allocatable) and a percent line, with a
colour ramp on 60% / 85% thresholds. A small 'Todos prontos' /
'N degraded' / 'None ready' badge sits next to the Ready
nodes count.
- The four cards share a common baseline via h-full + flex-1 so
the percent and the bars line up at the bottom regardless of
how much content the other cards carry.
- A new 'Storage' card replaces the redundant 'Networks' tile in
the stats row, formatted with auto-scaled units (B / KiB / MiB
/ GiB / TiB) and a percent line.
- A 'Cluster addons' strip below the Kubelet / OS / Arch footer
renders one dot per critical dependency, green and pulsing
when Established, an outlined ring when absent, yellow and
pulsing when unreachable. The list is discovered dynamically
by the backend — only addons actually present on the cluster
are shown.
- The dashboard query polls every 10s when /ws/events is up and
5s when it is down, so the usage bars and the addons strip
stay current without a manual refresh. The existing
realtime-invalidation path still drives VM transitions.
The polling is on a fixed schedule rather than the previous
'health == ok' gate because the underlying data sources
(metrics.k8s.io, the addon probe) have no /ws/events counterpart.
i18n: PT and EN parity for every new key, asserted by i18n.test.ts.
typecheck: tsc --noEmit reports 0 errors new to this commit (the
'isFetching' unused warning is pre-existing).
The dashboard Cluster overview (node counts, capacity, addons) is
platform-wide telemetry, not tenant-scoped. Hide it from any actor
whose role is not 'root'.
Two layers:
1. service.DashboardSummary now takes a role argument. When the
role is anything other than platform.RoleRoot, the service
skips populateHosts, populateStorage and populateAddons; the
JSON response carries no 'hosts', 'storage' or 'addons' field,
so nothing leaks through the Network tab or other consumers.
2. The Dashboard React component gates the entire <SurfaceCard>
with selectIsRoot, so non-root never even renders the card.
Tenant admins and tenant users still see the rest of the dashboard:
volume / VPC / SG / network counts, recent activity, the VM
running% badge, and the health card. Only the platform-wide
telemetry is gone.
The handler passes actor.Role (empty string when unauthenticated,
which is also not root). Two new tests
(TestDashboardSummaryHidesClusterOverviewForNonRoot and
TestDashboardSummaryShowsClusterOverviewForRoot) lock the
behaviour; the existing hosts/storage/addons tests now pass
platform.RoleRoot explicitly so they keep exercising the populated
path.
Matheus-Thurler
left a comment
There was a problem hiding this comment.
Good structure: root-only gating in both service and UI, 30s cache and fail-soft on metrics. Points to address:
- RBAC is missing in the chart. The API ClusterRole in
helm-charts/charts/virtfoundry/templates/rbac.yamlhas noapiextensions.k8s.io/customresourcedefinitions(get/list) normetrics.k8s.io/nodes(get/list). Without them the addons strip is always empty and usage is absent, silently, since Forbidden is swallowed. Please add the rules (anddocs/rbac-contract.yamlentries) in a helm-charts PR, rules first per our RBAC ordering. populateHostscan 500 the whole dashboard. IfClusterUsagereturns an error that is not caught byisMetricsAbsent,DashboardSummaryfails for root. Usage is optional: log and leaveHosts.Usagenil instead of returning the error.resource.MustParsecan panic on a malformed quantity from metrics.k8s.io. Useresource.ParseQuantityand skip the item on error.probeAddonslists every CRD with full schemas and has no timeout of its own. Please add a call timeout like the usage call, and request metadata only if possible (PartialObjectMetadataListAccept header).- Storage "used" is not consumption. It sums
requestsof Bound PVCs, which is allocated capacity. Rename it (allocated/bound) or the UI label, so it is not read as real usage.
- populateHosts: cluster usage is optional — an unexpected ClusterUsage error is logged and leaves hosts.usage nil instead of failing the whole dashboard summary (500) for root. - fetchClusterUsage: parse node quantities with resource.ParseQuantity and skip the item on error; resource.MustParse could panic on a malformed metrics.k8s.io payload. - probeAddons: bound the CRD list with a 4s timeout (same bound as the usage call) and request a PartialObjectMetadataList so only names and labels cross the wire, not full OpenAPI schemas. The API group is derived from the CRD object name (<plural>.<group>); Established is not part of ObjectMeta, so a discovered addon is now ok by presence. The apiextensions.k8s.io/customresourcedefinitions get/list grant is listed under dynamicClient in docs/rbac-contract.yaml. - StorageSummary: rename used_bytes to bound_bytes and reword the UI label to "allocated" — the figures are spec.requests sums, i.e. allocated capacity, not real guest consumption. - WithNamespace: build a fresh driver instead of copying the struct — the copy cloned the new cache mutexes (go vet copylocks) while sharing the cache entries. Chart grants land in virtfoundry/helm-charts#141 (rules first).
|
Thanks for the review, @Matheus-Thurler — all five points addressed in
Also fixed a latent issue the branch introduced and Re-ran everything: |
) * feat(dashboard): add cluster overview backend telemetry Extend GET /api/v1/dashboard/summary with three new additive fields that surface platform-wide telemetry for the operator: - hosts: node count, ready nodes, allocatable CPU and memory (millicores / bytes), distinct kubelet versions, OS images and architectures. Sourced from core/v1/nodes via KubeVirtDriver.ClusterMetrics. - hosts.usage: real CPU and memory consumption aggregated from metrics.k8s.io/v1beta1/nodes (Tier 2 — requires metrics-server, documented prerequisite in helm-charts). Served with a 30s in-memory cache, and degrades gracefully when the API is absent, Forbidden, or transiently unavailable. - storage: tenant-scoped PVC capacity and consumption (Bound vs reserved) plus the count. Sourced from the existing PVC list path in KubeVirtDriver. - addons: cluster-wide health of critical dependencies (KubeVirt, CDI, Multus, metrics-server, networking, and any chart that follows the Helm label conventions), discovered dynamically from CustomResourceDefinitions with the 'app.kubernetes.io/name' label and a small alias table for the well-known API groups. Each entry reports 'ok' (Established) or 'degraded'. All four reads are fail-soft: 404, 403, 401, 503, timeouts, and connection-refused return (nil, nil) so the endpoint never 500s. Memory mode and nil client leave the fields empty. docs/rbac-contract.yaml documents the dynamic-client access to metrics.k8s.io in a new dynamicClient section — rbac_contract_test keeps passing because the typed client AST scan does not see Discovery().RESTClient(). PlatformService gains a kv field so the service can read the hypervisor. CHANGELOG entry under Unreleased. A new 2026-10-08-dashboard-host-metrics.md captures the design and the locked decisions for this slice. * feat(ui): render cluster overview in the dashboard Surface the new hosts/storage/addons fields from the previous slice on the existing Dashboard page: - Rename the section title from 'Cluster nodes' to 'Cluster overview' to fit the broader scope (nodes + capacity + addons). - Four cards inside the section: Nodes, Ready nodes, Allocatable CPU, Allocatable memory. The CPU/memory cards now carry a usage bar (used / allocatable) and a percent line, with a colour ramp on 60% / 85% thresholds. A small 'Todos prontos' / 'N degraded' / 'None ready' badge sits next to the Ready nodes count. - The four cards share a common baseline via h-full + flex-1 so the percent and the bars line up at the bottom regardless of how much content the other cards carry. - A new 'Storage' card replaces the redundant 'Networks' tile in the stats row, formatted with auto-scaled units (B / KiB / MiB / GiB / TiB) and a percent line. - A 'Cluster addons' strip below the Kubelet / OS / Arch footer renders one dot per critical dependency, green and pulsing when Established, an outlined ring when absent, yellow and pulsing when unreachable. The list is discovered dynamically by the backend — only addons actually present on the cluster are shown. - The dashboard query polls every 10s when /ws/events is up and 5s when it is down, so the usage bars and the addons strip stay current without a manual refresh. The existing realtime-invalidation path still drives VM transitions. The polling is on a fixed schedule rather than the previous 'health == ok' gate because the underlying data sources (metrics.k8s.io, the addon probe) have no /ws/events counterpart. i18n: PT and EN parity for every new key, asserted by i18n.test.ts. typecheck: tsc --noEmit reports 0 errors new to this commit (the 'isFetching' unused warning is pre-existing). * feat(security): restrict the Cluster overview section to root The dashboard Cluster overview (node counts, capacity, addons) is platform-wide telemetry, not tenant-scoped. Hide it from any actor whose role is not 'root'. Two layers: 1. service.DashboardSummary now takes a role argument. When the role is anything other than platform.RoleRoot, the service skips populateHosts, populateStorage and populateAddons; the JSON response carries no 'hosts', 'storage' or 'addons' field, so nothing leaks through the Network tab or other consumers. 2. The Dashboard React component gates the entire <SurfaceCard> with selectIsRoot, so non-root never even renders the card. Tenant admins and tenant users still see the rest of the dashboard: volume / VPC / SG / network counts, recent activity, the VM running% badge, and the health card. Only the platform-wide telemetry is gone. The handler passes actor.Role (empty string when unauthenticated, which is also not root). Two new tests (TestDashboardSummaryHidesClusterOverviewForNonRoot and TestDashboardSummaryShowsClusterOverviewForRoot) lock the behaviour; the existing hosts/storage/addons tests now pass platform.RoleRoot explicitly so they keep exercising the populated path. * fix(dashboard): address cluster overview review feedback - populateHosts: cluster usage is optional — an unexpected ClusterUsage error is logged and leaves hosts.usage nil instead of failing the whole dashboard summary (500) for root. - fetchClusterUsage: parse node quantities with resource.ParseQuantity and skip the item on error; resource.MustParse could panic on a malformed metrics.k8s.io payload. - probeAddons: bound the CRD list with a 4s timeout (same bound as the usage call) and request a PartialObjectMetadataList so only names and labels cross the wire, not full OpenAPI schemas. The API group is derived from the CRD object name (<plural>.<group>); Established is not part of ObjectMeta, so a discovered addon is now ok by presence. The apiextensions.k8s.io/customresourcedefinitions get/list grant is listed under dynamicClient in docs/rbac-contract.yaml. - StorageSummary: rename used_bytes to bound_bytes and reword the UI label to "allocated" — the figures are spec.requests sums, i.e. allocated capacity, not real guest consumption. - WithNamespace: build a fresh driver instead of copying the struct — the copy cloned the new cache mutexes (go vet copylocks) while sharing the cache entries. Chart grants land in virtfoundry/helm-charts#141 (rules first). * fix(ui): adapt cluster overview to redesigned dashboard Render the overview with StatsCard/StatusBadge/SurfaceCard and add demo fixtures for hosts, storage and addons. --------- Co-authored-by: Weslei Paulo Pereira <[email protected]>
|
Superseded by #260 (rebased on the new UI). |
Summary
Add a Cluster overview section to the dashboard that surfaces four new platform-wide telemetry fields on
GET /api/v1/dashboard/summary:hosts— node count, ready nodes, allocatable CPU and memory (millicores / bytes), distinct kubelet versions, OS images and architectures. Sourced fromcore/v1/nodesviaKubeVirtDriver.ClusterMetrics.hosts.usage— real CPU and memory consumption aggregated frommetrics.k8s.io/v1beta1/nodes(Tier 2 — requiresmetrics-server, documented prerequisite inhelm-charts). Served with a 30s in-memory cache, fail-soft on 404/403/5xx/timeout.storage— tenant-scoped PVC allocated capacity (bound_bytes=spec.requestssum of Bound PVCs vstotal_bytesreserved) plus the count. Never real guest consumption — the API cannot see filesystem usage. Sourced from the existing PVC list path inKubeVirtDriver.addons— cluster-wide health of critical dependencies, discovered dynamically fromCustomResourceDefinitionslisted metadata-only (PartialObjectMetadataListAccept header, 4s call timeout — names and labels only, never the OpenAPI schemas). Resolved via the Helm labelapp.kubernetes.io/namewith fallback topart-of/component, plus a small alias table over the API group derived from the CRD object name (<plural>.<group>):kubevirt.io,cdi.kubevirt.io,k8s.cni.cncf.io,cert-manager.io,istio.io,monitoring.coreos.com,tekton.dev,argoproj.io. Any chart that follows the convention shows up automatically — no hard-coded list.The UI renders a
Cluster overviewcard with four tiles (Nodes, Ready nodes, Allocatable CPU, Allocatable memory), a usage bar on the capacity tiles with a< 60%primary /60–85%warning /≥ 85%error colour ramp, aStoragetile in the stats row ("{bound} of {total} allocated"), and aCluster addonsstrip with one dot per discovered addon. The dashboard polls every 10s when/ws/eventsis up and 5s when it is down, so the usage bars and the addons strip stay current without a manual refresh.Security: the entire section is now gated to
platform.RoleRootat both layers — the service omits thehosts,storageandaddonsfields from the JSON response for any non-root actor, and the React side hides the card whenselectIsRootis false. Tenant admins and tenant users keep the rest of the dashboard (volumes, VPCs, SGs, networks, recent VMs, health badge) but the platform-wide telemetry is gone — both from the rendered page and from the JSON response.Four commits on
dashboard/cluster-overview:feat(dashboard): add cluster overview backend telemetry— types, four driver methods, service injection, RBAC contractdynamicClientsection, design docfeat(ui): render cluster overview in the dashboard— UI, i18n, pollingfeat(security): restrict the Cluster overview section to root— role check, hidden field, hidden cardfix(dashboard): address cluster overview review feedback— usage fail-soft at the service layer, panic-free quantity parsing, bounded metadata-only addons probe,bound_bytesrename, no mutex copy inWithNamespaceTest plan
go test ./...— all packages pass, 0 failurescd ui && npm test(vitest) — 5 files, 27 tests, 0 failurescd ui && npx tsc --noEmit— 0 errors new to this branch (pre-existingisFetchingunused warning unrelated)rbac_contract_testpasses — thedynamicCliententries (metrics.k8s.io/nodes,apiextensions.k8s.io/customresourcedefinitions) indocs/rbac-contract.yamlare documented for the chart; the AST scan does not seeDiscovery().RESTClient()metrics-serverinstalled (Tier 1 + Tier 2 verified)root:hosts,storage,addonspopulated, all dots greentenant_admin: section hidden,hosts/storage/addonsabsent from the JSON responseUnreleased(Added / Changed / Security)helm-charts: feat(chart): grant the API read access for the dashboard cluster overview helm-charts#141 — API ClusterRole grantsapiextensions.k8s.io/customresourcedefinitionsandmetrics.k8s.io/nodesget/list (rules first per RBAC ordering). MINOR bump ofcore + helm-chartstogether perRELEASES.mdstays with the release PR.metrics-serverrequirement) — follow-up inhelm-chartsdocsConventions
feat(dashboard): …,feat(ui): …,feat(security): …)dashboard/cluster-overview)main; PR is opened from a feature branchhelm-chartschange is a follow-up; version bump will land together perRELEASES.mdomitempty); older clients keep workinggo build ./...,go test ./...,npm run buildAGENTS.md— change was deployed to a local kind cluster and verified