Files
headlamp-tns-csi-plugin/docs/architecture/overview.md
T
privilegedescalation-engineer[bot] b0110e474c chore: remove E2E testing infrastructure (#50)
* docs: update install docs to headlamp namespace (PRI-434)

- Update Helm/plugin install URLs from v0.2.4 to v1.0.0
- README: add pods/proxy RBAC scope, clarify controller is in kube-system
- docs/getting-started/*: update all download URLs to v1.0.0
- docs/deployment/helm.md: update install URLs to v1.0.0
- docs/architecture/overview.md: Headlamp Pod label → headlamp namespace
- docs/README.md: fix ArtifactHub URL
- CHANGELOG.md: add [Unreleased] entry

Note: driver/API-path references to kube-system are preserved
as they describe where the tns-csi controller workload runs,
not where Headlamp is installed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* chore: remove E2E testing infrastructure

- Delete e2e/ directory (auth.setup.ts, tns-csi.spec.ts)
- Delete playwright.config.ts
- Delete scripts/deploy-e2e-headlamp.sh
- Delete scripts/teardown-e2e-headlamp.sh
- Delete .github/workflows/e2e.yaml
- Remove e2e script from package.json
- Remove @playwright/test dependency from package.json

Context: [PRI-1133](/PRI/issues/PRI-1133) — full E2E purge across org.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: update pnpm-lock.yaml after E2E deletion

Remove @playwright/test dependencies after E2E infrastructure cleanup.
Resolves ERR_PNPM_OUTDATED_LOCKFILE on PR.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Chris Farhood <chris@farhood.org>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-05-11 20:11:07 +00:00

7.6 KiB

Architecture Overview

System Architecture

The TNS-CSI plugin is a single-page React application bundled as a Headlamp plugin. It runs entirely in the browser and communicates with Kubernetes exclusively through Headlamp's proxied API.

┌─────────────────────────────────────────────────────┐
│                    Browser                          │
│                                                     │
│  ┌──────────────────────────────────────────────┐   │
│  │            React Plugin Bundle               │   │
│  │                                              │   │
│  │  index.tsx  ──  registerRoute/Sidebar/etc.   │   │
│  │                                              │   │
│  │  TnsCsiDataProvider (React Context)          │   │
│  │  ├── K8s.ResourceClasses hooks (live watch)  │   │
│  │  └── ApiProxy.request (async fetch)          │   │
│  │                                              │   │
│  │  Pages:                                      │   │
│  │  OverviewPage  StorageClassesPage            │   │
│  │  VolumesPage   SnapshotsPage                 │   │
│  │  MetricsPage   BenchmarkPage                 │   │
│  │                                              │   │
│  │  PVCDetailSection (injected into PVC views)  │   │
│  └──────────────────────────────────────────────┘   │
└───────────────────────┬─────────────────────────────┘
                        │ HTTPS
                        ▼
┌─────────────────────────────────────────────────────┐
│              Headlamp Pod (headlamp namespace)              │
│                                                     │
│  Headlamp UI server + API proxy                     │
│  (forwards requests using service account token     │
│   or user-supplied OIDC token)                      │
└───────────────────────┬─────────────────────────────┘
                        │ in-cluster
                        ▼
┌─────────────────────────────────────────────────────┐
│           Kubernetes API Server                     │
│                                                     │
│  ├── /apis/storage.k8s.io/v1/storageclasses         │
│  ├── /api/v1/persistentvolumes                      │
│  ├── /api/v1/persistentvolumeclaims                 │
│  ├── /api/v1/namespaces/kube-system/pods            │
│  ├── /apis/storage.k8s.io/v1/csidrivers            │
│  ├── /apis/snapshot.storage.k8s.io/v1/...          │
│  ├── /api/v1/namespaces/kube-system/pods/<pod>/proxy/metrics
│  └── (Benchmark) /apis/batch/v1/jobs               │
└─────────────────────────────────────────────────────┘

Component Hierarchy

index.tsx
└── TnsCsiDataProvider
    ├── OverviewPage
    │   └── DriverStatusCard
    ├── StorageClassesPage
    │   └── StorageClassDetailPanel (slide-in)
    ├── VolumesPage
    │   └── VolumeDetailPanel (slide-in)
    ├── SnapshotsPage
    │   └── SnapshotDetailPanel (slide-in)
    ├── MetricsPage
    └── BenchmarkPage

registerDetailsViewSection
└── TnsCsiDataProvider
    └── PVCDetailSection (injected)

Data Sources

Data Source Mechanism
StorageClasses storage.k8s.io/v1 K8s.ResourceClasses.StorageClass.useList() — live watch
PersistentVolumes core/v1 K8s.ResourceClasses.PersistentVolume.useList() — live watch
PersistentVolumeClaims core/v1 K8s.ResourceClasses.PersistentVolumeClaim.useList() — live watch
CSIDriver storage.k8s.io/v1 ApiProxy.request — one-shot fetch
Controller pods core/v1 ApiProxy.request with label selector — one-shot fetch
Node pods core/v1 ApiProxy.request with label selector — one-shot fetch
VolumeSnapshots snapshot.storage.k8s.io/v1 ApiProxy.request — graceful degradation if CRD absent
Prometheus metrics Controller pod port 8080 ApiProxy.request pod proxy
kbench FIO logs Benchmark Job pod ApiProxy.request pod log

Key Design Decisions

KubeObject jsonData Extraction

Headlamp's useList() hooks return KubeObject class instances, not plain JSON objects. The class only exposes getter-defined fields (provisioner, reclaimPolicy, volumeBindingMode, allowVolumeExpansion for StorageClass). All other fields — including parameters, spec, and status — must be accessed via .jsonData.

TnsCsiDataContext.tsx extracts jsonData from every item before passing to filter/type helpers:

const extractJsonData = (items: unknown[]): unknown[] =>
  items.map(item =>
    item && typeof item === 'object' && 'jsonData' in item
      ? (item as { jsonData: unknown }).jsonData
      : item
  );

This is the single most important architectural invariant to preserve when working with headlamp hook data.

Context Provider Pattern

TnsCsiDataProvider wraps every route component. This ensures:

  • All data fetching happens once per page navigation (not once per component)
  • All pages share the same filtered StorageClasses, PVs, PVCs, and pod lists
  • The refresh() callback triggers a refreshKey increment which re-runs async fetches

Read-Only Constraint

The only write operation in the entire plugin is BenchmarkPage.tsx, which creates and deletes a Kubernetes Job and PVC. All other pages are strictly read-only. This is intentional and should be preserved.

Detail Panel Pattern

Slide-in detail panels use URL hash state (location.hash) so:

  • Panel state survives browser refresh
  • Back button closes the panel
  • Deep-linking to a specific resource is possible

Pattern: history.push(\${location.pathname}#${name}`)to open,history.push(location.pathname)` to close.

Graceful Degradation

The snapshot CRD (snapshot.storage.k8s.io/v1) may not be installed. The context provider catches the 404/405 error and sets snapshotCrdAvailable: false. The Snapshots page shows an informational message instead of an error. Prometheus metrics similarly fall back to placeholder cards.

Module Responsibilities

File Responsibility
src/index.tsx All registrations — sidebar entries, routes, detail section, plugin settings
src/api/k8s.ts Type definitions, type guards, filter helpers, format utilities
src/api/metrics.ts Prometheus text format parser, fetchControllerMetrics
src/api/kbench.ts kbench manifest builders, FIO log parser, BenchmarkState discriminated union
src/api/TnsCsiDataContext.tsx Shared data fetching and filtering; the extractJsonData pattern
src/components/*.tsx Page and panel UI components