diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e62a7..aa1813b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.2.2] - 2026-02-19 + +### Fixed + +- **Duplicate columns** — Protocol and Pool columns on mixed-driver clusters (tns-csi + rook-ceph) are now merged into a single shared column rather than duplicated; whichever plugin loads first owns the column and the second merges into it +- **Plugin settings name** — settings entry now registers as `tns-csi` instead of `headlamp-tns-csi-plugin` + ## [0.2.1] - 2026-02-19 ### Fixed @@ -55,7 +62,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - TypeScript strict mode with zero `any` types - ESLint + Prettier code quality tooling -[Unreleased]: https://github.com/privilegedescalation/headlamp-tns-csi-plugin/compare/v0.2.1...HEAD +[Unreleased]: https://github.com/privilegedescalation/headlamp-tns-csi-plugin/compare/v0.2.2...HEAD +[0.2.2]: https://github.com/privilegedescalation/headlamp-tns-csi-plugin/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/privilegedescalation/headlamp-tns-csi-plugin/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/privilegedescalation/headlamp-tns-csi-plugin/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/privilegedescalation/headlamp-tns-csi-plugin/releases/tag/v0.1.0 diff --git a/artifacthub-pkg.yml b/artifacthub-pkg.yml index a6da6d3..366873c 100644 --- a/artifacthub-pkg.yml +++ b/artifacthub-pkg.yml @@ -1,4 +1,4 @@ -version: "0.2.1" +version: "0.2.2" name: headlamp-tns-csi-plugin displayName: TrueNAS CSI (tns-csi) description: >- @@ -47,19 +47,13 @@ links: url: https://github.com/longhorn/kbench changes: - - kind: added - description: "Overview dashboard: driver health, storage summary, protocol distribution, non-Bound PVC alerts" - - kind: added - description: "Storage Classes, Volumes, Snapshots pages with slide-in detail panels" - - kind: added - description: "Metrics page: Prometheus WebSocket health, volume ops, CSI ops from controller pod" - - kind: added - description: "Benchmark page: kbench Job+PVC lifecycle, FIO log parser, results with IOPS/bandwidth/latency cards" - - kind: added - description: "PVC detail injection: TNS-CSI section on Headlamp PVC detail pages" + - kind: fixed + description: "Duplicate Protocol/Pool columns on mixed-driver clusters now merged into a single shared column" + - kind: fixed + description: "Plugin settings entry renamed from headlamp-tns-csi-plugin to tns-csi" annotations: - headlamp/plugin/archive-url: "https://github.com/privilegedescalation/headlamp-tns-csi-plugin/releases/download/v0.2.1/headlamp-tns-csi-plugin-0.2.1.tar.gz" - headlamp/plugin/archive-checksum: "sha256:9d53ae89996be6edbfbdb239c95d9d808fcab2de19c23b8f7b8d9abe1c51877b" + headlamp/plugin/archive-url: "https://github.com/privilegedescalation/headlamp-tns-csi-plugin/releases/download/v0.2.2/headlamp-tns-csi-plugin-0.2.2.tar.gz" + headlamp/plugin/archive-checksum: "sha256:fa6506d581ea5a609598912596995f3dd2b2501ee4f66aff5f6cd93beb525549" headlamp/plugin/version-compat: ">=0.20.0" headlamp/plugin/distro-compat: "in-cluster,web,app" diff --git a/headlamp-tns-csi-plugin-0.2.2.tar.gz b/headlamp-tns-csi-plugin-0.2.2.tar.gz new file mode 100644 index 0000000..66f3239 Binary files /dev/null and b/headlamp-tns-csi-plugin-0.2.2.tar.gz differ diff --git a/package.json b/package.json index 5791188..3ba3925 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "headlamp-tns-csi-plugin", - "version": "0.2.1", + "version": "0.2.2", "description": "Headlamp plugin for TNS-CSI driver visibility and benchmarking", "repository": { "type": "git", diff --git a/src/index.tsx b/src/index.tsx index ecea5b8..ebcf33e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -187,12 +187,40 @@ registerDetailsViewSection(({ resource }) => { // Table column processors — native StorageClass and PV tables // --------------------------------------------------------------------------- +// Merges incoming columns into existing ones by label. +// If a column with the same label already exists, the incoming getValue/render +// takes priority and falls back to the existing one (for mixed-driver tables). +function mergeColumns( + existing: T[], + incoming: Array<{ label: string; getValue: (r: unknown) => unknown; render: (r: unknown) => React.ReactNode }> +): T[] { + type ObjCol = { label: string; getValue: (r: unknown) => unknown; render: (r: unknown) => React.ReactNode }; + const isObjCol = (c: unknown): c is ObjCol => + typeof c === 'object' && c !== null && 'label' in c; + const result = [...existing]; + const toAppend: typeof incoming = []; + for (const col of incoming) { + const idx = result.findIndex(c => isObjCol(c) && (c as ObjCol).label === col.label); + if (idx !== -1) { + const prev = result[idx] as ObjCol; + result[idx] = { + label: col.label, + getValue: (r: unknown) => col.getValue(r) ?? prev.getValue(r), + render: (r: unknown) => col.getValue(r) !== null ? col.render(r) : prev.render(r), + } as unknown as T; + } else { + toAppend.push(col); + } + } + return [...result, ...(toAppend as unknown as T[])]; +} + registerResourceTableColumnsProcessor(({ id, columns }) => { if (id === 'headlamp-storageclasses') { - return [...columns, ...buildStorageClassColumns()]; + return mergeColumns(columns, buildStorageClassColumns()); } if (id === 'headlamp-persistentvolumes') { - return [...columns, ...buildPVColumns()]; + return mergeColumns(columns, buildPVColumns()); } return columns; }); @@ -210,4 +238,4 @@ registerDetailsViewHeaderAction(({ resource }) => { // Plugin settings // --------------------------------------------------------------------------- -registerPluginSettings('headlamp-tns-csi-plugin', TnsCsiSettings, true); +registerPluginSettings('tns-csi', TnsCsiSettings, true);