Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8dd71772f5 | |||
| c3b2877c51 | |||
| 62aa181433 | |||
| 2c077907e9 | |||
| 17495d4883 | |||
| 01eed82efc | |||
| 5dab426fe8 | |||
| 5eaa6603f1 |
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
version: 0.3.1
|
version: 0.3.3
|
||||||
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.3.1/headlamp-polaris-plugin-0.3.1.tar.gz"
|
headlamp/plugin/archive-url: "https://github.com/cpfarhood/headlamp-polaris-plugin/releases/download/v0.3.3/headlamp-polaris-plugin-0.3.3.tar.gz"
|
||||||
headlamp/plugin/version-compat: ">=0.26"
|
headlamp/plugin/version-compat: ">=0.26"
|
||||||
headlamp/plugin/archive-checksum: sha256:36a79050e920ee26c9371763182fc8bc2a4ddcac5086e108a7828f0467b111c8
|
headlamp/plugin/archive-checksum: sha256:b18a94682d991e4d8b5297ff58c5e56d86f93f0657a9485297c1b62a0504ff57
|
||||||
headlamp/plugin/distro-compat: in-cluster
|
headlamp/plugin/distro-compat: in-cluster
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "headlamp-polaris-plugin",
|
"name": "headlamp-polaris-plugin",
|
||||||
"version": "0.3.1",
|
"version": "0.3.3",
|
||||||
"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",
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { Chip } from '@mui/material';
|
|
||||||
import { Shield as ShieldIcon } from '@mui/icons-material';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { usePolarisDataContext } from '../api/PolarisDataContext';
|
import { usePolarisDataContext } from '../api/PolarisDataContext';
|
||||||
@@ -21,10 +19,10 @@ export default function AppBarScoreBadge() {
|
|||||||
const score = computeScore(counts);
|
const score = computeScore(counts);
|
||||||
|
|
||||||
// Color based on score
|
// Color based on score
|
||||||
const getColor = (score: number): 'success' | 'warning' | 'error' => {
|
const getColor = (score: number): string => {
|
||||||
if (score >= 80) return 'success';
|
if (score >= 80) return '#4caf50'; // green
|
||||||
if (score >= 50) return 'warning';
|
if (score >= 50) return '#ff9800'; // orange
|
||||||
return 'error';
|
return '#f44336'; // red
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
@@ -32,13 +30,26 @@ export default function AppBarScoreBadge() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Chip
|
<button
|
||||||
icon={<ShieldIcon />}
|
|
||||||
label={`Polaris: ${score}%`}
|
|
||||||
color={getColor(score)}
|
|
||||||
size="small"
|
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
style={{ cursor: 'pointer', marginRight: '8px' }}
|
style={{
|
||||||
/>
|
cursor: 'pointer',
|
||||||
|
marginRight: '8px',
|
||||||
|
padding: '4px 12px',
|
||||||
|
borderRadius: '16px',
|
||||||
|
border: 'none',
|
||||||
|
backgroundColor: getColor(score),
|
||||||
|
color: 'white',
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 500,
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '4px',
|
||||||
|
}}
|
||||||
|
aria-label={`Polaris cluster score: ${score}%`}
|
||||||
|
>
|
||||||
|
<span>🛡️</span>
|
||||||
|
<span>Polaris: {score}%</span>
|
||||||
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import {
|
|||||||
SimpleTable,
|
SimpleTable,
|
||||||
StatusLabel,
|
StatusLabel,
|
||||||
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
import { Button } from '@mui/material';
|
|
||||||
import { Refresh as RefreshIcon } from '@mui/icons-material';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AuditData, computeScore, countResults, ResultCounts } from '../api/polaris';
|
import { AuditData, computeScore, countResults, ResultCounts } from '../api/polaris';
|
||||||
import { usePolarisDataContext } from '../api/PolarisDataContext';
|
import { usePolarisDataContext } from '../api/PolarisDataContext';
|
||||||
@@ -114,9 +112,25 @@ export default function DashboardView() {
|
|||||||
<span style={{ fontSize: '14px', color: 'var(--mui-palette-text-secondary, #666)' }}>
|
<span style={{ fontSize: '14px', color: 'var(--mui-palette-text-secondary, #666)' }}>
|
||||||
Last updated: {formatAuditTime(data.AuditTime)}
|
Last updated: {formatAuditTime(data.AuditTime)}
|
||||||
</span>
|
</span>
|
||||||
<Button variant="outlined" startIcon={<RefreshIcon />} onClick={refresh} size="small">
|
<button
|
||||||
Refresh
|
onClick={refresh}
|
||||||
</Button>
|
style={{
|
||||||
|
padding: '6px 16px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: '#1976d2',
|
||||||
|
border: '1px solid #1976d2',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 500,
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '4px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>🔄</span>
|
||||||
|
<span>Refresh</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { NameValueTable, SectionBox, Dialog } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
import { NameValueTable, SectionBox, Dialog } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
|
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
|
||||||
import { Button, Checkbox, FormControlLabel, FormGroup } from '@mui/material';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Result } from '../api/polaris';
|
import { Result } from '../api/polaris';
|
||||||
import { getCheckName } from '../api/checkMapping';
|
import { getCheckName } from '../api/checkMapping';
|
||||||
@@ -147,16 +146,23 @@ export default function ExemptionManager({
|
|||||||
rows={currentExemptions.map(exemption => ({
|
rows={currentExemptions.map(exemption => ({
|
||||||
name: exemption,
|
name: exemption,
|
||||||
value: (
|
value: (
|
||||||
<Button
|
<button
|
||||||
size="small"
|
style={{
|
||||||
color="error"
|
padding: '4px 12px',
|
||||||
|
backgroundColor: '#f44336',
|
||||||
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '12px',
|
||||||
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// Remove exemption logic
|
// Remove exemption logic
|
||||||
alert('Remove exemption: ' + exemption);
|
alert('Remove exemption: ' + exemption);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Remove
|
Remove
|
||||||
</Button>
|
</button>
|
||||||
),
|
),
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
@@ -164,58 +170,100 @@ export default function ExemptionManager({
|
|||||||
<p>No exemptions configured</p>
|
<p>No exemptions configured</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<button
|
||||||
variant="outlined"
|
|
||||||
onClick={() => setDialogOpen(true)}
|
onClick={() => setDialogOpen(true)}
|
||||||
disabled={failingChecks.length === 0}
|
disabled={failingChecks.length === 0}
|
||||||
style={{ marginTop: '8px' }}
|
style={{
|
||||||
|
marginTop: '8px',
|
||||||
|
padding: '6px 16px',
|
||||||
|
backgroundColor: failingChecks.length === 0 ? '#ccc' : 'transparent',
|
||||||
|
color: failingChecks.length === 0 ? '#999' : '#1976d2',
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: failingChecks.length === 0 ? '#ccc' : '#1976d2',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: failingChecks.length === 0 ? 'not-allowed' : 'pointer',
|
||||||
|
fontSize: '13px',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Add Exemption
|
Add Exemption
|
||||||
</Button>
|
</button>
|
||||||
</SectionBox>
|
</SectionBox>
|
||||||
|
|
||||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} title="Add Exemptions">
|
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} title="Add Exemptions">
|
||||||
<div style={{ padding: '16px', minWidth: '400px' }}>
|
<div style={{ padding: '16px', minWidth: '400px' }}>
|
||||||
<FormControlLabel
|
<label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
|
||||||
control={
|
<input
|
||||||
<Checkbox checked={exemptAll} onChange={e => setExemptAll(e.target.checked)} />
|
type="checkbox"
|
||||||
}
|
checked={exemptAll}
|
||||||
label="Exempt from all checks"
|
onChange={e => setExemptAll(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
|
<span>Exempt from all checks</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
{!exemptAll && (
|
{!exemptAll && (
|
||||||
<>
|
<>
|
||||||
<div style={{ marginTop: '16px', marginBottom: '8px', fontWeight: 600 }}>
|
<div style={{ marginTop: '16px', marginBottom: '8px', fontWeight: 600 }}>
|
||||||
Select checks to exempt:
|
Select checks to exempt:
|
||||||
</div>
|
</div>
|
||||||
<FormGroup>
|
<div>
|
||||||
{failingChecks.map(check => (
|
{failingChecks.map(check => (
|
||||||
<FormControlLabel
|
<label
|
||||||
key={check.checkId}
|
key={check.checkId}
|
||||||
control={
|
style={{
|
||||||
<Checkbox
|
display: 'flex',
|
||||||
checked={selectedChecks.has(check.checkId)}
|
alignItems: 'center',
|
||||||
onChange={() => handleCheckToggle(check.checkId)}
|
gap: '8px',
|
||||||
/>
|
marginBottom: '8px',
|
||||||
}
|
cursor: 'pointer',
|
||||||
label={check.checkName}
|
}}
|
||||||
/>
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedChecks.has(check.checkId)}
|
||||||
|
onChange={() => handleCheckToggle(check.checkId)}
|
||||||
|
/>
|
||||||
|
<span>{check.checkName}</span>
|
||||||
|
</label>
|
||||||
))}
|
))}
|
||||||
</FormGroup>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{ marginTop: '16px', display: 'flex', gap: '8px', justifyContent: 'flex-end' }}
|
style={{ marginTop: '16px', display: 'flex', gap: '8px', justifyContent: 'flex-end' }}
|
||||||
>
|
>
|
||||||
<Button onClick={() => setDialogOpen(false)}>Cancel</Button>
|
<button
|
||||||
<Button
|
onClick={() => setDialogOpen(false)}
|
||||||
variant="contained"
|
style={{
|
||||||
|
padding: '6px 16px',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
color: '#1976d2',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '13px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
onClick={applyExemptions}
|
onClick={applyExemptions}
|
||||||
disabled={applying || (!exemptAll && selectedChecks.size === 0)}
|
disabled={applying || (!exemptAll && selectedChecks.size === 0)}
|
||||||
|
style={{
|
||||||
|
padding: '6px 16px',
|
||||||
|
backgroundColor:
|
||||||
|
applying || (!exemptAll && selectedChecks.size === 0) ? '#ccc' : '#1976d2',
|
||||||
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor:
|
||||||
|
applying || (!exemptAll && selectedChecks.size === 0) ? 'not-allowed' : 'pointer',
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{applying ? 'Applying...' : 'Apply'}
|
{applying ? 'Applying...' : 'Apply'}
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
StatusLabel,
|
StatusLabel,
|
||||||
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
} from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
|
import { ApiProxy } from '@kinvolk/headlamp-plugin/lib';
|
||||||
import { Button } from '@mui/material';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
getDashboardUrl,
|
getDashboardUrl,
|
||||||
@@ -124,14 +123,22 @@ export default function PolarisSettings(props: PluginSettingsProps) {
|
|||||||
name: 'Connection Test',
|
name: 'Connection Test',
|
||||||
value: (
|
value: (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<button
|
||||||
variant="contained"
|
|
||||||
onClick={testConnection}
|
onClick={testConnection}
|
||||||
disabled={testing}
|
disabled={testing}
|
||||||
size="small"
|
style={{
|
||||||
|
padding: '6px 16px',
|
||||||
|
backgroundColor: testing ? '#ccc' : '#1976d2',
|
||||||
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: testing ? 'not-allowed' : 'pointer',
|
||||||
|
fontSize: '13px',
|
||||||
|
fontWeight: 500,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{testing ? 'Testing...' : 'Test Connection'}
|
{testing ? 'Testing...' : 'Test Connection'}
|
||||||
</Button>
|
</button>
|
||||||
{testResult && (
|
{testResult && (
|
||||||
<div style={{ marginTop: '8px' }}>
|
<div style={{ marginTop: '8px' }}>
|
||||||
<StatusLabel status={testResult.success ? 'success' : 'error'}>
|
<StatusLabel status={testResult.success ? 'success' : 'error'}>
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ registerRoute({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Register plugin settings
|
// Register plugin settings
|
||||||
registerPluginSettings('polaris', PolarisSettings);
|
registerPluginSettings('headlamp-polaris-plugin', PolarisSettings, true);
|
||||||
|
|
||||||
// Register details view section for supported controller types
|
// Register details view section for supported controller types
|
||||||
registerDetailsViewSection(({ resource }) => {
|
registerDetailsViewSection(({ resource }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user