Compare commits

...

2 Commits

Author SHA1 Message Date
github-actions[bot] 604106c688 ci: update artifact hub metadata for v0.2.2 2026-02-11 18:32:26 +00:00
Chris Farhood 44a0016a4d feat: add configurable Polaris dashboard URL setting
- Add getDashboardUrl() and setDashboardUrl() functions to polaris.ts
- Update PolarisSettings with dashboard URL input field
- Replace hardcoded POLARIS_DASHBOARD_PROXY with configurable getPolarisProxyUrl()
- Increase namespace detail panel width to 800px
- Remove unused 'Skipped' field from overview dashboard
- Version bump to 0.2.2

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-11 13:31:40 -05:00
8 changed files with 65 additions and 31 deletions
+3 -3
View File
@@ -1,4 +1,4 @@
version: 0.2.1 version: 0.2.2
name: headlamp-polaris-plugin name: headlamp-polaris-plugin
displayName: Polaris displayName: Polaris
createdAt: "2026-02-05T19:00:00Z" createdAt: "2026-02-05T19:00:00Z"
@@ -28,7 +28,7 @@ maintainers:
- name: cpfarhood - name: cpfarhood
email: "chris@farhood.org" email: "chris@farhood.org"
annotations: annotations:
headlamp/plugin/archive-url: "https://github.com/cpfarhood/headlamp-polaris-plugin/releases/download/v0.2.1/headlamp-polaris-plugin-0.2.1.tar.gz" headlamp/plugin/archive-url: "https://github.com/cpfarhood/headlamp-polaris-plugin/releases/download/v0.2.2/headlamp-polaris-plugin-0.2.2.tar.gz"
headlamp/plugin/version-compat: ">=0.26" headlamp/plugin/version-compat: ">=0.26"
headlamp/plugin/archive-checksum: sha256:0ee98511e23590699e623fc597c56aef6619793b0fe7dfa4bb4c21ca28b6fdaf headlamp/plugin/archive-checksum: sha256:22400516eed9645e463873e7a5ab6c2dab8bfa8fca8a51bb51b0475079ca8c83
headlamp/plugin/distro-compat: in-cluster headlamp/plugin/distro-compat: in-cluster
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "headlamp-polaris-plugin", "name": "headlamp-polaris-plugin",
"version": "0.1.3", "version": "0.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "headlamp-polaris-plugin", "name": "headlamp-polaris-plugin",
"version": "0.1.3", "version": "0.2.0",
"devDependencies": { "devDependencies": {
"@kinvolk/headlamp-plugin": "^0.13.0", "@kinvolk/headlamp-plugin": "^0.13.0",
"@playwright/test": "^1.58.2" "@playwright/test": "^1.58.2"
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "headlamp-polaris-plugin", "name": "headlamp-polaris-plugin",
"version": "0.2.1", "version": "0.2.2",
"description": "Headlamp plugin for Fairwinds Polaris audit results", "description": "Headlamp plugin for Fairwinds Polaris audit results",
"scripts": { "scripts": {
"start": "headlamp-plugin start", "start": "headlamp-plugin start",
+26 -8
View File
@@ -125,11 +125,14 @@ export const INTERVAL_OPTIONS = [
{ label: '30 minutes', value: 1800 }, { label: '30 minutes', value: 1800 },
]; ];
const STORAGE_KEY = 'polaris-plugin-refresh-interval'; const REFRESH_STORAGE_KEY = 'polaris-plugin-refresh-interval';
const DEFAULT_INTERVAL_SECONDS = 300; // 5 minutes const DEFAULT_INTERVAL_SECONDS = 300; // 5 minutes
const URL_STORAGE_KEY = 'polaris-plugin-dashboard-url';
const DEFAULT_DASHBOARD_URL = '/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/';
export function getRefreshInterval(): number { export function getRefreshInterval(): number {
const stored = localStorage.getItem(STORAGE_KEY); const stored = localStorage.getItem(REFRESH_STORAGE_KEY);
if (stored !== null) { if (stored !== null) {
const parsed = parseInt(stored, 10); const parsed = parseInt(stored, 10);
if (!isNaN(parsed) && parsed > 0) { if (!isNaN(parsed) && parsed > 0) {
@@ -140,13 +143,26 @@ export function getRefreshInterval(): number {
} }
export function setRefreshInterval(seconds: number): void { export function setRefreshInterval(seconds: number): void {
localStorage.setItem(STORAGE_KEY, String(seconds)); localStorage.setItem(REFRESH_STORAGE_KEY, String(seconds));
}
export function getDashboardUrl(): string {
const stored = localStorage.getItem(URL_STORAGE_KEY);
if (stored !== null && stored.trim() !== '') {
return stored.trim();
}
return DEFAULT_DASHBOARD_URL;
}
export function setDashboardUrl(url: string): void {
localStorage.setItem(URL_STORAGE_KEY, url.trim());
} }
// --- Polaris dashboard proxy URL --- // --- Polaris dashboard proxy URL ---
export const POLARIS_DASHBOARD_PROXY = export function getPolarisProxyUrl(): string {
'/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/'; return getDashboardUrl();
}
// --- Score computation --- // --- Score computation ---
@@ -157,8 +173,10 @@ export function computeScore(counts: ResultCounts): number {
// --- Data fetching hook --- // --- Data fetching hook ---
const POLARIS_API_PATH = function getPolarisApiPath(): string {
'/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json'; const baseUrl = getDashboardUrl();
return baseUrl.endsWith('/') ? `${baseUrl}results.json` : `${baseUrl}/results.json`;
}
interface PolarisDataState { interface PolarisDataState {
data: AuditData | null; data: AuditData | null;
@@ -177,7 +195,7 @@ export function usePolarisData(refreshIntervalSeconds: number): PolarisDataState
async function fetchData() { async function fetchData() {
try { try {
const result: AuditData = await ApiProxy.request(POLARIS_API_PATH); const result: AuditData = await ApiProxy.request(getPolarisApiPath());
if (!cancelled) { if (!cancelled) {
setData(result); setData(result);
setError(null); setError(null);
-9
View File
@@ -26,7 +26,6 @@ function OverviewSection(props: { data: AuditData; counts: ResultCounts }) {
{ name: 'Pass', value: counts.pass, fill: COLORS.pass }, { name: 'Pass', value: counts.pass, fill: COLORS.pass },
{ name: 'Warning', value: counts.warning, fill: COLORS.warning }, { name: 'Warning', value: counts.warning, fill: COLORS.warning },
{ name: 'Danger', value: counts.danger, fill: COLORS.danger }, { name: 'Danger', value: counts.danger, fill: COLORS.danger },
{ name: 'Skipped', value: counts.skipped, fill: COLORS.skipped },
]; ];
return ( return (
@@ -51,14 +50,6 @@ function OverviewSection(props: { data: AuditData; counts: ResultCounts }) {
name: 'Danger', name: 'Danger',
value: <StatusLabel status="error">{counts.danger}</StatusLabel>, value: <StatusLabel status="error">{counts.danger}</StatusLabel>,
}, },
{
name: 'Skipped',
value: (
<span title="Only counts checks with Severity=ignore. Annotation-based exemptions are not included.">
{counts.skipped}
</span>
),
},
]} ]}
/> />
</SectionBox> </SectionBox>
+2 -2
View File
@@ -12,7 +12,7 @@ import {
computeScore, computeScore,
countResultsForItems, countResultsForItems,
filterResultsByNamespace, filterResultsByNamespace,
POLARIS_DASHBOARD_PROXY, getPolarisProxyUrl,
Result, Result,
ResultCounts, ResultCounts,
} from '../api/polaris'; } from '../api/polaris';
@@ -89,7 +89,7 @@ export default function NamespaceDetailView() {
{ {
name: 'Polaris Dashboard', name: 'Polaris Dashboard',
value: ( value: (
<a href={POLARIS_DASHBOARD_PROXY} target="_blank" rel="noopener noreferrer"> <a href={getPolarisProxyUrl()} target="_blank" rel="noopener noreferrer">
View in Polaris Dashboard View in Polaris Dashboard
</a> </a>
), ),
+3 -3
View File
@@ -13,7 +13,7 @@ import {
countResultsForItems, countResultsForItems,
filterResultsByNamespace, filterResultsByNamespace,
getNamespaces, getNamespaces,
POLARIS_DASHBOARD_PROXY, getPolarisProxyUrl,
Result, Result,
ResultCounts, ResultCounts,
} from '../api/polaris'; } from '../api/polaris';
@@ -102,7 +102,7 @@ function NamespaceDetailPanel({ namespace, onClose }: NamespaceDetailPanelProps)
right: 0, right: 0,
top: 0, top: 0,
bottom: 0, bottom: 0,
width: '600px', width: '800px',
backgroundColor: 'var(--background-paper, #fff)', backgroundColor: 'var(--background-paper, #fff)',
boxShadow: '-2px 0 8px rgba(0,0,0,0.15)', boxShadow: '-2px 0 8px rgba(0,0,0,0.15)',
overflowY: 'auto', overflowY: 'auto',
@@ -140,7 +140,7 @@ function NamespaceDetailPanel({ namespace, onClose }: NamespaceDetailPanelProps)
{ {
name: 'Polaris Dashboard', name: 'Polaris Dashboard',
value: ( value: (
<a href={POLARIS_DASHBOARD_PROXY} target="_blank" rel="noopener noreferrer"> <a href={getPolarisProxyUrl()} target="_blank" rel="noopener noreferrer">
View in Polaris Dashboard View in Polaris Dashboard
</a> </a>
), ),
+28 -3
View File
@@ -1,6 +1,6 @@
import { NameValueTable, SectionBox } from '@kinvolk/headlamp-plugin/lib/CommonComponents'; import { NameValueTable, SectionBox } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
import React from 'react'; import React from 'react';
import { getRefreshInterval, INTERVAL_OPTIONS, setRefreshInterval } from '../api/polaris'; import { getDashboardUrl, getRefreshInterval, INTERVAL_OPTIONS, setDashboardUrl, setRefreshInterval } from '../api/polaris';
interface PluginSettingsProps { interface PluginSettingsProps {
data?: { [key: string]: string | number | boolean }; data?: { [key: string]: string | number | boolean };
@@ -10,13 +10,20 @@ interface PluginSettingsProps {
export default function PolarisSettings(props: PluginSettingsProps) { export default function PolarisSettings(props: PluginSettingsProps) {
const { data, onDataChange } = props; const { data, onDataChange } = props;
const currentInterval = (data?.refreshInterval as number) ?? getRefreshInterval(); const currentInterval = (data?.refreshInterval as number) ?? getRefreshInterval();
const currentUrl = (data?.dashboardUrl as string) ?? getDashboardUrl();
function handleChange(e: React.ChangeEvent<HTMLSelectElement>) { function handleIntervalChange(e: React.ChangeEvent<HTMLSelectElement>) {
const seconds = Number(e.target.value); const seconds = Number(e.target.value);
setRefreshInterval(seconds); setRefreshInterval(seconds);
onDataChange?.({ ...data, refreshInterval: seconds }); onDataChange?.({ ...data, refreshInterval: seconds });
} }
function handleUrlChange(e: React.ChangeEvent<HTMLInputElement>) {
const url = e.target.value;
setDashboardUrl(url);
onDataChange?.({ ...data, dashboardUrl: url });
}
return ( return (
<SectionBox title="Polaris Settings"> <SectionBox title="Polaris Settings">
<NameValueTable <NameValueTable
@@ -24,7 +31,7 @@ export default function PolarisSettings(props: PluginSettingsProps) {
{ {
name: 'Refresh Interval', name: 'Refresh Interval',
value: ( value: (
<select value={currentInterval} onChange={handleChange}> <select value={currentInterval} onChange={handleIntervalChange}>
{INTERVAL_OPTIONS.map(opt => ( {INTERVAL_OPTIONS.map(opt => (
<option key={opt.value} value={opt.value}> <option key={opt.value} value={opt.value}>
{opt.label} {opt.label}
@@ -33,6 +40,24 @@ export default function PolarisSettings(props: PluginSettingsProps) {
</select> </select>
), ),
}, },
{
name: 'Dashboard URL',
value: (
<input
type="text"
value={currentUrl}
onChange={handleUrlChange}
placeholder="/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/"
style={{
width: '100%',
padding: '4px 8px',
border: '1px solid #ccc',
borderRadius: '4px',
fontSize: '14px',
}}
/>
),
},
]} ]}
/> />
</SectionBox> </SectionBox>