62c24e3857
- Register AppBarClusterBadge via registerAppBarAction (was dead code) - Add Rook 1.12+ CSI pod labels to CephPodDetailSection alongside legacy labels - Add sidebar entries for Storage Classes and Volumes pages - Add role="dialog", aria-modal, aria-labelledby, and Escape key to all detail drawers - Replace hardcoded hex colors with CSS custom properties for dark/light theme compat - Remove duplicate parseStorageToBytes from OverviewPage (import from k8s.ts) - Add endpoints field to CephObjectStoreStatus interface (remove unsafe cast) - Use ROOK_CEPH_API_GROUP/VERSION constants in API URL construction - Hoist extractJsonData to module level - Remove dead extractPoolFromVolumeHandle function - Fix redundant storageClasses.length guard in OverviewPage - Fix lint indent warnings - Update CLAUDE.md and CHANGELOG.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
105 lines
4.6 KiB
Markdown
105 lines
4.6 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project
|
|
|
|
Headlamp plugin for Rook-Ceph cluster visibility.
|
|
|
|
- **Plugin name**: `rook`
|
|
- **Rook-Ceph API group**: `ceph.rook.io/v1`
|
|
- **Default namespace**: `rook-ceph`
|
|
- **Reference plugin**: `../headlamp-tns-csi-plugin`
|
|
|
|
## Commands
|
|
|
|
```bash
|
|
npm start # dev server with hot reload
|
|
npm run build # production build
|
|
npm run package # package for headlamp
|
|
npm run tsc # TypeScript type check (no emit)
|
|
npm run lint # ESLint
|
|
npm run lint:fix # ESLint with auto-fix
|
|
npm run format # Prettier write
|
|
npm run format:check # Prettier check
|
|
npm test # vitest run (all tests)
|
|
npm run test:watch # vitest watch mode
|
|
npx vitest run src/api/k8s.test.ts # run a single test file
|
|
```
|
|
|
|
All tests and `tsc` must pass before committing.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
src/
|
|
├── index.tsx # Plugin entry: registerRoute, registerSidebarEntry, registerAppBarAction, etc.
|
|
├── api/
|
|
│ ├── k8s.ts # Types + filtering helpers (ceph.rook.io)
|
|
│ └── RookCephDataContext.tsx # Shared React context provider
|
|
└── components/
|
|
├── OverviewPage.tsx
|
|
├── BlockPoolsPage.tsx
|
|
├── StorageClassesPage.tsx
|
|
├── VolumesPage.tsx
|
|
├── PodsPage.tsx
|
|
├── FilesystemsPage.tsx
|
|
├── ObjectStoresPage.tsx
|
|
├── ClusterStatusCard.tsx
|
|
├── AppBarClusterBadge.tsx # Cluster health badge in Headlamp top nav bar
|
|
├── PVCDetailSection.tsx # Injected into Headlamp PVC detail view
|
|
├── PVDetailSection.tsx # Injected into Headlamp PV detail view
|
|
├── CephPodDetailSection.tsx # Injected into Headlamp Pod detail view
|
|
└── integrations/
|
|
└── StorageClassColumns.tsx # Column processors for SC + PV tables
|
|
```
|
|
|
|
## Data flow
|
|
|
|
`RookCephDataContext.tsx` uses **two fetching strategies**:
|
|
|
|
1. **Headlamp hooks** (`K8s.ResourceClasses.*.useList()`) — for StorageClasses, PVs, PVCs. These return Headlamp `KubeObject` class instances whose raw JSON is stored under `.jsonData`. The `extractJsonData()` helper in the context provider unwraps them to plain objects before passing to the type-guard filters in `k8s.ts`.
|
|
|
|
2. **`ApiProxy.request()`** — for Rook CRDs (`cephclusters`, `cephblockpools`, `cephfilesystems`, `cephobjectstores`) and daemon pods, since these aren't in Headlamp's built-in resource classes. Fetched in a single `useEffect` keyed on `refreshKey`.
|
|
|
|
All pages consume data exclusively via `useRookCephContext()`. The provider is re-wrapped per route and per detail-section registration in `index.tsx`.
|
|
|
|
## Key constants (src/api/k8s.ts)
|
|
|
|
- Namespace: `rook-ceph`
|
|
- API group: `ceph.rook.io/v1`
|
|
- RBD provisioner: `rook-ceph.rbd.csi.ceph.com`
|
|
- CephFS provisioner: `rook-ceph.cephfs.csi.ceph.com`
|
|
- Custom namespace provisioners: any string ending in `.rbd.csi.ceph.com` or `.cephfs.csi.ceph.com`
|
|
- Pod selectors: `app=rook-ceph-operator`, `app=rook-ceph-mon`, `app=rook-ceph-osd`, `app=rook-ceph-mgr`, `app=rook-ceph-mds`, `app=rook-ceph-rgw`
|
|
- CSI pod selectors (Rook 1.12+): `app=rook-ceph.rbd.csi.ceph.com-ctrlplugin`, `app=rook-ceph.cephfs.csi.ceph.com-ctrlplugin`
|
|
- CSI pod selectors (legacy): `app=csi-rbdplugin-provisioner`, `app=csi-cephfsplugin-provisioner`, `app=csi-rbdplugin`, `app=csi-cephfsplugin`
|
|
|
|
## Code conventions
|
|
|
|
- Functional React components only — no class components
|
|
- All imports from `@kinvolk/headlamp-plugin/lib` and `@kinvolk/headlamp-plugin/lib/CommonComponents`
|
|
- No additional UI libraries (no MUI direct imports, no Ant Design, etc.)
|
|
- TypeScript strict mode — no `any`, use `unknown` + type guards at API boundaries
|
|
- Context provider (`RookCephDataProvider`) wraps each route component in `index.tsx`
|
|
- Tests: vitest + @testing-library/react, mock with `vi.mock('@kinvolk/headlamp-plugin/lib', ...)`
|
|
- `vitest.setup.ts` provides a spec-compliant `localStorage` shim for Node 22+ compatibility
|
|
|
|
## Testing
|
|
|
|
Mock pattern for headlamp APIs:
|
|
```typescript
|
|
vi.mock('@kinvolk/headlamp-plugin/lib', () => ({
|
|
ApiProxy: { request: vi.fn().mockResolvedValue({ items: [] }) },
|
|
K8s: {
|
|
ResourceClasses: {
|
|
StorageClass: { useList: vi.fn(() => [[], null]) },
|
|
PersistentVolume: { useList: vi.fn(() => [[], null]) },
|
|
PersistentVolumeClaim: { useList: vi.fn(() => [[], null]) },
|
|
},
|
|
},
|
|
}));
|
|
```
|
|
|
|
The mock import must appear **before** the module under test is imported (see `RookCephDataContext.test.tsx`).
|