diff --git a/README.md b/README.md index 0efc2e7..0cd1b65 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,11 @@ Polaris must be deployed in the `polaris` namespace with the dashboard component ### Option 1: Headlamp Plugin Manager (Recommended) -**⚠️ CRITICAL for Headlamp v0.39.0+:** You **must** set `config.watchPlugins: false` or the plugin will not load. See [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md#critical-headlamp-v0390-configuration) for details. - The plugin is published on [Artifact Hub](https://artifacthub.io/packages/headlamp/polaris/headlamp-polaris-plugin). Configure Headlamp via Helm: ```yaml config: pluginsDir: /headlamp/plugins - watchPlugins: false # CRITICAL for v0.39.0+ pluginsManager: sources: @@ -182,14 +179,14 @@ Every proxied request is recorded in Kubernetes API audit logs as a `get` on `se ### Comprehensive Guides -| Guide | Description | -|-------|-------------| +| Guide | Description | +| ------------------------------------------------- | --------------------------------------------------------------------- | | **[Architecture](docs/architecture/overview.md)** | System architecture, data flow, component hierarchy, design decisions | -| **[Deployment](docs/deployment/helm.md)** | Production deployment with Helm, Kubernetes, FluxCD | -| **[Security](SECURITY.md)** | Security model, RBAC requirements, vulnerability reporting | -| **[Testing](docs/development/testing.md)** | Unit tests, E2E tests, CI/CD, best practices | -| **[Contributing](CONTRIBUTING.md)** | Development workflow, branching strategy, PR process | -| **[Changelog](CHANGELOG.md)** | Complete release history (v0.0.1 to current) | +| **[Deployment](docs/deployment/helm.md)** | Production deployment with Helm, Kubernetes, FluxCD | +| **[Security](SECURITY.md)** | Security model, RBAC requirements, vulnerability reporting | +| **[Testing](docs/development/testing.md)** | Unit tests, E2E tests, CI/CD, best practices | +| **[Contributing](CONTRIBUTING.md)** | Development workflow, branching strategy, PR process | +| **[Changelog](CHANGELOG.md)** | Complete release history (v0.0.1 to current) | ## Troubleshooting @@ -197,14 +194,14 @@ Every proxied request is recorded in Kubernetes API audit logs as a `get` on `se Quick reference: -| Symptom | Likely Cause | Quick Fix | -| ------------------------------- | -------------------------------------------- | --------------------------------------------------------------------- | -| **Plugin not in sidebar** | Headlamp v0.39.0+ plugin loading issue | Set `config.watchPlugins: false` and hard refresh (Cmd+Shift+R) | -| **403 Access Denied** | Missing RBAC binding for `services/proxy` | Apply Role + RoleBinding from RBAC section | -| **404 or 503** | Polaris not installed, or dashboard disabled | Install Polaris with `dashboard.enabled: true` in `polaris` namespace | -| **Dark mode white backgrounds** | Old plugin version | Upgrade to v0.3.5+ and hard refresh browser | -| **Settings page empty** | Old plugin version | Upgrade to v0.3.3+ | -| **No data / infinite spinner** | Network policy or Polaris pod down | Check network policies and `kubectl get pods -n polaris` | +| Symptom | Likely Cause | Quick Fix | +| ------------------------------- | --------------------------------------------- | --------------------------------------------------------------------- | +| **Plugin not in sidebar** | Plugin not installed or needs browser refresh | Hard refresh browser (Cmd+Shift+R / Ctrl+Shift+F5) | +| **403 Access Denied** | Missing RBAC binding for `services/proxy` | Apply Role + RoleBinding from RBAC section | +| **404 or 503** | Polaris not installed, or dashboard disabled | Install Polaris with `dashboard.enabled: true` in `polaris` namespace | +| **Dark mode white backgrounds** | Old plugin version | Upgrade to v0.3.5+ and hard refresh browser | +| **Settings page empty** | Old plugin version | Upgrade to v0.3.3+ | +| **No data / infinite spinner** | Network policy or Polaris pod down | Check network policies and `kubectl get pods -n polaris` | ## Development @@ -373,4 +370,3 @@ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for: ## License [Apache-2.0 License](LICENSE) - see LICENSE file for details. - diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index ef48ea5..d0b566b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -18,6 +18,7 @@ This document describes the architecture, design decisions, and data flow of the The Headlamp Polaris Plugin is a **read-only dashboard** that surfaces Fairwinds Polaris audit results within the Headlamp UI. It fetches data from the Polaris dashboard API via the Kubernetes service proxy and presents it in a hierarchical navigation structure. **Key Characteristics:** + - **Read-only:** No write operations to cluster or Polaris - **Service proxy based:** Uses K8s API server proxy to reach Polaris - **React Context for state:** Shared data fetch across components @@ -170,6 +171,7 @@ Display namespace score + resource table ### Plugin Entry Point **`src/index.tsx`** + - Registers sidebar entries (Polaris → Overview, Namespaces) - Registers routes (`/polaris`, `/polaris/namespaces`) - Registers app bar action (score badge) @@ -179,22 +181,26 @@ Display namespace score + resource table ### Data Layer **`src/api/PolarisDataContext.tsx`** + - React Context Provider for shared data - Fetches AuditData from Polaris dashboard - Handles auto-refresh based on user settings - Provides `{ data, loading, error, refresh }` to consumers **`src/api/polaris.ts`** + - TypeScript types for AuditData schema - Utility functions: `countResults()`, `computeScore()` - Settings management: `getRefreshInterval()`, `setRefreshInterval()` - Constants: `DASHBOARD_URL_DEFAULT`, `INTERVAL_OPTIONS` **`src/api/checkMapping.ts`** + - Maps Polaris check IDs to human-readable names - Used for display in UI (e.g., "hostIPCSet" → "Host IPC") **`src/api/topIssues.ts`** + - Aggregates failing checks across cluster - Groups by check ID and severity - Used for top issues dashboard @@ -202,6 +208,7 @@ Display namespace score + resource table ### View Components **`src/components/DashboardView.tsx`** + - **Route:** `/polaris` - **Purpose:** Cluster-wide overview - **Features:** @@ -212,6 +219,7 @@ Display namespace score + resource table - **Data:** Uses `usePolarisDataContext()` **`src/components/NamespacesListView.tsx`** + - **Route:** `/polaris/namespaces` - **Purpose:** List all namespaces with scores - **Features:** @@ -221,6 +229,7 @@ Display namespace score + resource table - **Data:** Uses `usePolarisDataContext()`, aggregates by namespace **`src/components/NamespaceDetailView.tsx`** + - **Route:** Drawer on `/polaris/namespaces#` - **Purpose:** Namespace-level drill-down - **Features:** @@ -233,6 +242,7 @@ Display namespace score + resource table ### UI Components **`src/components/AppBarScoreBadge.tsx`** + - **Location:** Headlamp app bar (top-right) - **Purpose:** Quick cluster score visibility - **Features:** @@ -242,6 +252,7 @@ Display namespace score + resource table - **Data:** Uses `usePolarisDataContext()` **`src/components/PolarisSettings.tsx`** + - **Location:** Settings → Plugins → Polaris - **Purpose:** Plugin configuration - **Features:** @@ -251,6 +262,7 @@ Display namespace score + resource table - **Data:** localStorage for persistence **`src/components/InlineAuditSection.tsx`** + - **Location:** Resource detail pages (Deployment, StatefulSet, etc.) - **Purpose:** Show Polaris audit inline - **Features:** @@ -260,6 +272,7 @@ Display namespace score + resource table - **Data:** Uses `usePolarisDataContext()`, filters by resource **`src/components/ExemptionManager.tsx`** + - **Location:** (Planned feature, UI exists but not fully integrated) - **Purpose:** Manage Polaris exemptions via annotations - **Features:** @@ -274,6 +287,7 @@ Display namespace score + resource table **Decision:** Use React Context instead of Redux/Zustand **Rationale:** + 1. **Simple state:** Single AuditData object shared across views 2. **Read-only:** No complex mutations or transactions 3. **Headlamp constraints:** Plugin cannot add dependencies (Redux not bundled) @@ -283,10 +297,10 @@ Display namespace score + resource table ```typescript interface PolarisDataContextValue { - data: AuditData | null; // Audit results or null if loading/error - loading: boolean; // True during initial fetch - error: string | null; // Error message if fetch failed - refresh: () => void; // Manual refresh function + data: AuditData | null; // Audit results or null if loading/error + loading: boolean; // True during initial fetch + error: string | null; // Error message if fetch failed + refresh: () => void; // Manual refresh function } ``` @@ -300,6 +314,7 @@ interface PolarisDataContextValue { ### localStorage Usage Settings persisted in localStorage: + - **`polaris-plugin-refresh-interval`**: Number (seconds), default 300 - **`polaris-plugin-dashboard-url`**: String, default service proxy path @@ -312,12 +327,14 @@ No sensitive data stored in localStorage. **Decision:** Use Kubernetes service proxy, not direct ClusterIP access **Rationale:** + - Headlamp already has K8s API credentials (service account or user token) - Service proxy leverages existing RBAC (no new credentials needed) - Works with Headlamp's token auth and OIDC - Simpler deployment (no additional network policies for plugin) **Trade-off:** + - Requires `get` permission on `services/proxy` resource - Path is longer: `/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json` @@ -326,14 +343,17 @@ No sensitive data stored in localStorage. **Decision:** Sidebar has "Polaris" → "Overview" and "Namespaces" (2 levels max) **Rationale:** + - Headlamp sidebar supports 2-level nesting maximum - Deeper nesting (e.g., Polaris → Namespaces → ) doesn't work - Sidebar Collapse component is route-based, not click-to-toggle **Alternative Considered:** + - Dynamic sidebar with namespace entries → rejected (Headlamp limitation) **Current Solution:** + - Use table in NamespacesListView with clickable namespace buttons - Namespace detail opens in drawer (not new route) @@ -342,12 +362,14 @@ No sensitive data stored in localStorage. **Decision:** Namespace detail uses drawer, not dedicated route **Rationale:** + - Better UX (drawer overlays table, no navigation loss) - URL hash preserves navigation state (`#namespace-name`) - Keyboard shortcuts (Escape to close) - Sidebar doesn't support 3-level nesting for per-namespace routes **Implementation:** + - URL: `/polaris/namespaces#kube-system` - Drawer controlled by hash presence - `useEffect` watches hash changes @@ -357,11 +379,13 @@ No sensitive data stored in localStorage. **Decision:** Never import from `@mui/material` or `@mui/icons-material` **Rationale:** + - Headlamp plugin environment doesn't provide full MUI library - Importing MUI causes `createSvgIcon undefined` error - Plugins must use Headlamp CommonComponents only **Alternative:** + - Use standard HTML elements with inline styles - Use theme-aware CSS variables (`--mui-palette-*`) @@ -370,11 +394,13 @@ No sensitive data stored in localStorage. **Decision:** Enable all TypeScript strict checks **Rationale:** + - Catch errors at compile time - Better IDE support and autocomplete - Enforces type safety (no `any`, no implicit unknowns) **Impact:** + - More verbose code (explicit types required) - Better maintainability and refactorability @@ -383,11 +409,13 @@ No sensitive data stored in localStorage. **Decision:** Default refresh interval is 5 minutes (configurable) **Rationale:** + - Polaris audits typically run every 10-30 minutes - Balance between data freshness and API load - User can configure from 1 minute to 30 minutes **Considered:** + - WebSocket/SSE for real-time updates → rejected (Polaris dashboard doesn't support) - Shorter default → rejected (unnecessary API calls) @@ -401,28 +429,30 @@ No sensitive data stored in localStorage. ```typescript // Sidebar navigation -registerSidebarEntry({ parent, name, label, url, icon }) +registerSidebarEntry({ parent, name, label, url, icon }); // Routes -registerRoute({ path, sidebar, name, exact, component }) +registerRoute({ path, sidebar, name, exact, component }); // App bar actions -registerAppBarAction(component) +registerAppBarAction(component); // Plugin settings -registerPluginSettings(name, component, displaySaveButton) +registerPluginSettings(name, component, displaySaveButton); // Resource detail sections -registerDetailsViewSection(component) +registerDetailsViewSection(component); ``` **Key Changes in v0.13.0:** + - `registerDetailsViewSection` now takes 1 argument (component), not 2 (name, component) - `registerAppBarAction` now takes 1 argument (component), not 2 (name, component) ### Headlamp CommonComponents **Used Components:** + - `SectionBox` - Card-like container with title - `SectionHeader` - Page header with title - `StatusLabel` - Color-coded status badges @@ -432,16 +462,19 @@ registerDetailsViewSection(component) - `Loader` - Loading spinner **Router:** + - `Router.createRouteURL()` - Generate plugin route URLs - React Router's `useHistory()`, `useParams()`, `useLocation()` ### Kubernetes API (via ApiProxy) **Used for:** + - Fetching Polaris results: `ApiProxy.request(dashboardUrl + 'results.json')` - No direct K8s API calls (all data from Polaris dashboard) **RBAC Required:** + - `get` on `services/proxy` for `polaris-dashboard` in `polaris` namespace ## Known Limitations @@ -529,18 +562,22 @@ registerDetailsViewSection(component) ### Potential Improvements 1. **WebWorker for data processing** + - Offload `countResults()` aggregation for large clusters - Keep UI responsive during heavy computation 2. **IndexedDB caching** + - Cache audit data offline - Show stale data + "refresh available" indicator 3. **GraphQL/REST API abstraction** + - Decouple from Polaris dashboard JSON format - Support multiple backend sources 4. **Plugin-to-plugin communication** + - Integrate with other Headlamp plugins (e.g., policy enforcement) - Shared state between plugins diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index dd716d5..929fbd5 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -47,9 +47,9 @@ kubectl -n kube-system get pods -l app.kubernetes.io/name=headlamp ```yaml # headlamp-values.yaml config: - pluginsDir: "/headlamp/plugins" + pluginsDir: /headlamp/plugins -pluginsManager: + pluginsManager: enabled: true repositories: - https://artifacthub.io/packages/search?kind=4 @@ -76,8 +76,7 @@ pluginsManager: ```yaml # headlamp-values.yaml config: - pluginsDir: "/headlamp/plugins" - watchPlugins: false # CRITICAL: Must be false for plugin manager + pluginsDir: /headlamp/plugins replicaCount: 1 @@ -123,7 +122,7 @@ data: ```yaml # headlamp-values.yaml config: - pluginsDir: "/plugins" + pluginsDir: '/plugins' volumes: - name: plugins @@ -152,9 +151,8 @@ image: tag: v0.39.0 config: - baseURL: "" - pluginsDir: "/headlamp/plugins" - watchPlugins: false # MUST be false for plugin manager + baseURL: '' + pluginsDir: /headlamp/plugins pluginsManager: enabled: true @@ -195,16 +193,16 @@ resources: # OIDC Authentication (optional) env: - name: HEADLAMP_CONFIG_OIDC_CLIENT_ID - value: "headlamp" + value: 'headlamp' - name: HEADLAMP_CONFIG_OIDC_CLIENT_SECRET valueFrom: secretKeyRef: name: headlamp-oidc key: client-secret - name: HEADLAMP_CONFIG_OIDC_ISSUER_URL - value: "https://auth.example.com/realms/kubernetes" + value: 'https://auth.example.com/realms/kubernetes' - name: HEADLAMP_CONFIG_OIDC_SCOPES - value: "openid,profile,email,groups" + value: 'openid,profile,email,groups' ``` ### FluxCD HelmRelease Example @@ -230,8 +228,7 @@ spec: values: config: - pluginsDir: "/headlamp/plugins" - watchPlugins: false + pluginsDir: /headlamp/plugins pluginsManager: enabled: true @@ -265,10 +262,10 @@ metadata: name: polaris-proxy-reader namespace: polaris rules: - - apiGroups: [""] - resources: ["services/proxy"] - resourceNames: ["polaris-dashboard"] - verbs: ["get"] + - apiGroups: [''] + resources: ['services/proxy'] + resourceNames: ['polaris-dashboard'] + verbs: ['get'] ``` ### RoleBinding Options @@ -303,7 +300,7 @@ metadata: namespace: polaris subjects: - kind: Group - name: system:authenticated # All authenticated users + name: system:authenticated # All authenticated users apiGroup: rbac.authorization.k8s.io roleRef: kind: Role @@ -350,10 +347,10 @@ metadata: app.kubernetes.io/name: headlamp-polaris-plugin app.kubernetes.io/component: rbac rules: - - apiGroups: [""] - resources: ["services/proxy"] - resourceNames: ["polaris-dashboard"] - verbs: ["get"] + - apiGroups: [''] + resources: ['services/proxy'] + resourceNames: ['polaris-dashboard'] + verbs: ['get'] --- # RoleBinding: Grant Headlamp service account access @@ -376,6 +373,7 @@ roleRef: ``` Apply with: + ```bash kubectl apply -f polaris-plugin-rbac.yaml ``` @@ -385,6 +383,7 @@ kubectl apply -f polaris-plugin-rbac.yaml ### Required Network Access The plugin requires network connectivity: + - **Headlamp pod** → **Kubernetes API server** (service proxy) - **Kubernetes API server** → **Polaris dashboard service** (port 80) @@ -424,36 +423,9 @@ spec: ## Plugin Manager Setup -### Critical Configuration - -**❌ WRONG (Will not load plugins):** -```yaml -config: - watchPlugins: true # Default, treats catalog plugins as dev plugins -``` - -**✅ CORRECT:** -```yaml -config: - watchPlugins: false # Required for plugin manager catalog plugins -``` - -### Why `watchPlugins: false` is Required - -- **With `watchPlugins: true`:** Headlamp backend serves plugin metadata, but frontend never executes the JavaScript (treated as development directory plugin) -- **Result:** Plugins appear in Settings but no sidebar/routes/settings work -- **Fix:** Set `watchPlugins: false` in Headlamp configuration -- **Documentation:** See `deployment/PLUGIN_LOADING_FIX.md` for root cause analysis - ### Plugin Manager Verification ```bash -# Check Headlamp config -kubectl -n kube-system get configmap headlamp -o yaml | grep watchPlugins - -# Expected output: -# watchPlugins: "false" - # Check plugin is installed kubectl -n kube-system exec -it deployment/headlamp -- ls -la /headlamp/plugins/ @@ -469,7 +441,6 @@ kubectl -n kube-system exec -it deployment/headlamp -- ls -la /headlamp/plugins/ - [ ] Polaris dashboard service exists (`polaris-dashboard` in `polaris` namespace) - [ ] RBAC Role and RoleBinding created - [ ] Headlamp v0.26+ deployed -- [ ] `watchPlugins: false` set in Headlamp config ### Deployment @@ -518,14 +489,16 @@ kubectl -n kube-system exec -it deployment/headlamp -- ls -la /headlamp/plugins/ **Symptom:** Plugin listed in Settings → Plugins but no sidebar entry **Causes:** -1. `watchPlugins: true` (should be `false`) -2. Browser cache not cleared + +1. Browser cache not cleared +2. Plugin files not properly installed **Solution:** + ```bash -# Fix Headlamp config -kubectl -n kube-system edit configmap headlamp -# Set watchPlugins: false +# Verify plugin files exist +kubectl -n kube-system exec deployment/headlamp -c headlamp -- \ + ls -la /headlamp/plugins/headlamp-polaris-plugin/ # Restart Headlamp kubectl -n kube-system rollout restart deployment/headlamp @@ -541,6 +514,7 @@ kubectl -n kube-system rollout restart deployment/headlamp **Cause:** RBAC missing or incorrect **Solution:** + ```bash # Verify RBAC exists kubectl -n polaris get role polaris-proxy-reader @@ -557,11 +531,13 @@ kubectl auth can-i get services/proxy --as=system:serviceaccount:kube-system:hea **Symptom:** Error loading Polaris data, 404 in console **Causes:** + 1. Polaris not deployed 2. Polaris service name wrong 3. Polaris namespace wrong **Solution:** + ```bash # Check Polaris deployment kubectl -n polaris get pods @@ -579,11 +555,13 @@ helm install polaris fairwinds-stable/polaris \ **Symptom:** Error when using custom Polaris URL in settings **Causes:** + 1. URL format incorrect 2. CORS not configured on external Polaris 3. Network policy blocking external access **Solution:** + ```bash # Test URL manually curl -v https://my-polaris.example.com/results.json @@ -599,6 +577,7 @@ curl -v https://my-polaris.example.com/results.json **Cause:** Plugin manager hasn't synced from ArtifactHub **Solution:** + ```bash # Wait 30 minutes (ArtifactHub sync interval) # Or manually refresh plugin list in Headlamp UI @@ -614,6 +593,7 @@ kubectl -n kube-system rollout restart deployment/headlamp **Cause:** NetworkPolicy in `polaris` namespace blocking API server **Solution:** + ```bash # Check NetworkPolicies kubectl -n polaris get networkpolicy @@ -633,25 +613,28 @@ kubectl -n polaris get networkpolicy ### Audit Logging Kubernetes audit logs will record: + - User/service account accessing service proxy - Timestamp and response code Configure audit policy if needed: + ```yaml apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata - verbs: ["get"] + verbs: ['get'] resources: - - group: "" - resources: ["services/proxy"] - namespaces: ["polaris"] + - group: '' + resources: ['services/proxy'] + namespaces: ['polaris'] ``` ### Data Sensitivity Polaris audit data may contain: + - Resource names and namespaces - Configuration details - Potential security vulnerabilities diff --git a/docs/DOCUMENTATION_STANDARDIZATION_PLAN.md b/docs/DOCUMENTATION_STANDARDIZATION_PLAN.md index b385736..3deb8dc 100644 --- a/docs/DOCUMENTATION_STANDARDIZATION_PLAN.md +++ b/docs/DOCUMENTATION_STANDARDIZATION_PLAN.md @@ -11,8 +11,10 @@ This plan standardizes documentation structure, formatting, and content across t ## Current State Analysis ### Polaris Plugin (v0.3.5) + **Structure**: Topic-focused with monolithic files **Strengths**: + - Comprehensive CONTRIBUTING.md with branching strategy and commit conventions - Complete CHANGELOG.md (35 versions documented) - Dedicated SECURITY.md with vulnerability reporting @@ -21,6 +23,7 @@ This plan standardizes documentation structure, formatting, and content across t - Well-organized TROUBLESHOOTING.md with common issues **Gaps**: + - No user journey-based organization - No Architecture Decision Records - Limited quick-start tutorials @@ -28,8 +31,10 @@ This plan standardizes documentation structure, formatting, and content across t - Deployment guide is monolithic (needs breakdown) ### Sealed Secrets Plugin + **Structure**: User journey-based with granular topic files **Strengths**: + - Excellent user journey organization (Getting Started → User Guide → Tutorials) - Architecture Decision Records (5 ADRs) - Quick diagnosis flowchart in troubleshooting @@ -38,6 +43,7 @@ This plan standardizes documentation structure, formatting, and content across t - Visual hierarchy with strategic emoji use **Gaps**: + - No dedicated CONTRIBUTING.md (content in README) - No SECURITY.md for vulnerability reporting - Incomplete tutorial placeholders @@ -49,6 +55,7 @@ This plan standardizes documentation structure, formatting, and content across t ### 1. File Structure Standard **Root-Level Files** (Common to Both): + ``` README.md # Main entry point with badges, quick links CHANGELOG.md # Keep a Changelog format, semantic versioning @@ -59,6 +66,7 @@ package.json # Plugin metadata ``` **Documentation Directory** (Organized by User Journey): + ``` docs/ ├── README.md # Documentation hub with quick links @@ -94,6 +102,7 @@ docs/ ### 2. README.md Standard **Required Sections** (Order Matters): + 1. Title + Badges (ArtifactHub, CI, E2E, License) 2. Quick navigation links (📚 Documentation | 🚀 Installation | 🔒 Security | 🛠️ Development) 3. **What It Does** (features with visual hierarchy) @@ -111,6 +120,7 @@ docs/ 15. Footer ("Made with ❤️ for the Kubernetes community") **Formatting Standards**: + - Use emojis strategically for visual scanning (not excessive) - Quick navigation at top - Tables for structured data (prerequisites, troubleshooting quick ref) @@ -122,6 +132,7 @@ docs/ **Format**: Keep a Changelog (https://keepachangelog.com/) **Structure**: + ```markdown # Changelog @@ -135,21 +146,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [X.Y.Z] - YYYY-MM-DD ### Added + - New features ### Changed + - Changes to existing functionality ### Deprecated + - Soon-to-be removed features ### Removed + - Removed features ### Fixed + - Bug fixes ### Security + - Security fixes [Unreleased]: https://github.com/user/repo/compare/vX.Y.Z...HEAD @@ -157,6 +174,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ``` **Standards**: + - One entry per version, newest first - Date in ISO 8601 format (YYYY-MM-DD) - Link to GitHub release @@ -166,6 +184,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 4. CONTRIBUTING.md Standard **Required Sections**: + 1. Code of Conduct (brief, respectful) 2. Getting Started (prerequisites, setup) 3. Development Workflow (feature development, testing) @@ -178,6 +197,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 10. Release Process (version numbering, creating releases) **Formatting**: + - Use tables for branch naming conventions - Code blocks for commit message examples - Checklists for PR requirements @@ -186,6 +206,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 5. SECURITY.md Standard **Required Sections**: + 1. Overview (security model, read-only vs. write operations) 2. Data Flow Diagram (how data moves through system) 3. RBAC Requirements (minimal permissions table) @@ -198,6 +219,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 10. Compliance Considerations (audit trail, GDPR/privacy) **Formatting**: + - Tables for permissions and supported versions - YAML examples for RBAC manifests - Bash commands for security verification @@ -208,6 +230,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 **Purpose**: Central navigation for all documentation **Structure**: + ```markdown # Documentation @@ -222,31 +245,40 @@ Central hub for [Plugin Name] documentation. - 💻 [Development](development/workflow.md) ## Getting Started + Description + links to installation, prerequisites, quick-start ## User Guide + Description + links to features, configuration, RBAC ## Tutorials + Description + links to plugin-specific tutorials ## Troubleshooting + Description + link to quick diagnosis + common issues ## Architecture + Description + links to overview, data flow, ADRs ## Development + Description + links to workflow, testing, code style, release ## Deployment + Description + links to Kubernetes, Helm, production ## API Reference + Link to JSDoc or generated API docs ``` **Formatting**: + - Emojis for visual scanning - Brief descriptions (1-2 sentences) for each section - Organized by user journey (beginner → advanced) @@ -254,12 +286,14 @@ Link to JSDoc or generated API docs ### 7. Architecture Decision Records (ADR) Standard **When to Create ADRs**: + - Significant architectural choices - Technology selection (libraries, patterns) - Security or performance trade-offs - Design patterns that impact maintainability **Template** (Based on Michael Nygard's ADR): + ```markdown # ADR-NNN: Title @@ -280,17 +314,21 @@ What is the change that we're proposing and/or doing? What becomes easier or more difficult to do because of this change? ### Positive + - ... ### Negative + - ... ### Neutral + - ... ## Alternatives Considered ### Option 1: Name + **Pros**: ... **Cons**: ... **Decision**: Not chosen because... @@ -303,11 +341,12 @@ What becomes easier or more difficult to do because of this change? **Numbering**: ADR-001, ADR-002, etc. (zero-padded 3 digits) **Index File** (architecture/adr/README.md): + ```markdown # Architecture Decision Records -| ADR | Title | Status | Date | -|-----|-------|--------|------| +| ADR | Title | Status | Date | +| ------------------- | ----- | -------- | ---------- | | [001](001-title.md) | Title | Accepted | 2026-01-01 | ``` @@ -316,6 +355,7 @@ What becomes easier or more difficult to do because of this change? **Structure**: **troubleshooting/README.md** (Quick Diagnosis): + ```markdown # Troubleshooting @@ -324,8 +364,8 @@ Quick diagnosis guide for common issues. ## Quick Reference | Symptom | Likely Cause | Quick Fix | -|---------|-------------|-----------| -| ... | ... | ... | +| ------- | ------------ | --------- | +| ... | ... | ... | ## Detailed Guides @@ -335,6 +375,7 @@ Quick diagnosis guide for common issues. ``` **Individual Issue Files**: + - Symptom-based organization - Step-by-step resolution - Bash commands for verification @@ -346,6 +387,7 @@ Quick diagnosis guide for common issues. **docs/development/testing.md**: **Required Sections**: + 1. Overview (testing philosophy, types of tests) 2. Unit Testing (framework, running tests, writing tests, examples) 3. E2E Testing (framework, prerequisites, running tests, examples) @@ -355,6 +397,7 @@ Quick diagnosis guide for common issues. 7. Debugging (common issues, useful commands) **Formatting**: + - Tables for test types and coverage goals - Code blocks for examples - Bash commands for running tests @@ -363,6 +406,7 @@ Quick diagnosis guide for common issues. ### 10. Visual Formatting Standards **Emoji Usage** (Strategic, Not Excessive): + - 📚 Documentation - 🚀 Installation/Quick Start - 🔒 Security @@ -375,6 +419,7 @@ Quick diagnosis guide for common issues. - 💻 Code/Technical **Code Block Languages**: + - `bash` for shell commands - `yaml` for Kubernetes/Helm manifests - `typescript` for TypeScript code @@ -382,11 +427,13 @@ Quick diagnosis guide for common issues. - `diff` for showing changes **Tables**: + - Use for structured data (prerequisites, commands, permissions, troubleshooting) - Keep columns concise - Left-align text columns, center-align status columns **Links**: + - Use descriptive text, not "click here" - Relative paths within repo (`docs/architecture/overview.md`) - Absolute URLs for external resources @@ -397,6 +444,7 @@ Quick diagnosis guide for common issues. ### Phase 1: Polaris Plugin Enhancements **Priority 1: Granular Documentation Structure** + - [ ] Create docs/README.md (documentation hub) - [ ] Break down DEPLOYMENT.md: - [ ] docs/getting-started/installation.md @@ -414,6 +462,7 @@ Quick diagnosis guide for common issues. - [ ] Move TESTING.md → docs/development/testing.md **Priority 2: Add Missing Content** + - [ ] Create docs/getting-started/quick-start.md (5-minute tutorial) - [ ] Create docs/user-guide/features.md - [ ] Create docs/user-guide/configuration.md @@ -421,6 +470,7 @@ Quick diagnosis guide for common issues. - [ ] Create FAQ section in troubleshooting **Priority 3: Content Refinement** + - [ ] Add multi-platform instructions to installation.md - [ ] Enhance README.md with better visual hierarchy - [ ] Add more code examples to user guide @@ -429,18 +479,21 @@ Quick diagnosis guide for common issues. ### Phase 2: Sealed Secrets Plugin Enhancements **Priority 1: Root-Level Documentation** + - [ ] Extract CONTRIBUTING.md from README - [ ] Create SECURITY.md with vulnerability reporting - [ ] Expand CHANGELOG.md to include all versions - [ ] Update README.md to match standardized format **Priority 2: Complete Incomplete Files** + - [ ] Finish placeholder tutorial files - [ ] Add E2E testing guide to docs/development/testing.md - [ ] Expand API reference (ensure generated docs are readable) - [ ] Add FAQ section **Priority 3: Content Refinement** + - [ ] Add CI/CD badges to README - [ ] Ensure consistent emoji usage - [ ] Standardize code block languages @@ -449,12 +502,14 @@ Quick diagnosis guide for common issues. ### Phase 3: Cross-Repository Standards **Documentation Templates** + - [ ] ADR template in both repos - [ ] Bug report template (GitHub issue template) - [ ] Feature request template - [ ] PR template **Shared Patterns** + - [ ] Consistent branching strategy docs - [ ] Identical commit message conventions - [ ] Same release process documentation @@ -463,21 +518,25 @@ Quick diagnosis guide for common issues. ## Success Metrics **Completeness**: + - [ ] All standard files present in both repos - [ ] No broken links in documentation - [ ] All code examples tested and functional **Consistency**: + - [ ] Same file structure in both repos - [ ] Same formatting standards applied - [ ] Same terminology used for common concepts **Usability**: + - [ ] New users can get started in < 5 minutes - [ ] Contributors can find development workflow easily - [ ] Troubleshooting guides resolve 80%+ of common issues **Maintainability**: + - [ ] Documentation updates documented in CHANGELOG - [ ] ADRs created for all major decisions - [ ] Test documentation kept in sync with code @@ -485,6 +544,7 @@ Quick diagnosis guide for common issues. ## Maintenance Guidelines **When to Update Documentation**: + 1. **New Feature**: Add to CHANGELOG, update user guide, add tutorial if complex 2. **Bug Fix**: Add to CHANGELOG, update troubleshooting if user-facing 3. **Architecture Change**: Create ADR, update architecture docs @@ -493,6 +553,7 @@ Quick diagnosis guide for common issues. 6. **Configuration Change**: Update user guide, add migration notes if needed **Documentation Review Checklist**: + - [ ] Spelling and grammar checked - [ ] Links tested (no 404s) - [ ] Code examples tested @@ -502,6 +563,7 @@ Quick diagnosis guide for common issues. - [ ] Version numbers current **Annual Documentation Audit**: + - Review all docs for accuracy (especially version numbers, screenshots) - Check for outdated information - Update ADR status if superseded @@ -512,30 +574,30 @@ Quick diagnosis guide for common issues. ### Polaris Plugin Current → Standard -| Current | Standard Location | -|---------|-------------------| -| README.md | README.md (enhanced) | -| CHANGELOG.md | CHANGELOG.md (no change) | -| CONTRIBUTING.md | CONTRIBUTING.md (no change) | -| SECURITY.md | SECURITY.md (no change) | -| docs/ARCHITECTURE.md | docs/architecture/overview.md + data-flow.md + design-decisions.md | -| docs/DEPLOYMENT.md | docs/getting-started/installation.md + docs/deployment/kubernetes.md + helm.md + production.md | -| docs/TROUBLESHOOTING.md | docs/troubleshooting/README.md + common-issues.md | -| docs/TESTING.md | docs/development/testing.md | -| — (new) | docs/README.md | -| — (new) | docs/getting-started/quick-start.md | -| — (new) | docs/user-guide/features.md | -| — (new) | docs/architecture/adr/ | +| Current | Standard Location | +| ----------------------- | ---------------------------------------------------------------------------------------------- | +| README.md | README.md (enhanced) | +| CHANGELOG.md | CHANGELOG.md (no change) | +| CONTRIBUTING.md | CONTRIBUTING.md (no change) | +| SECURITY.md | SECURITY.md (no change) | +| docs/ARCHITECTURE.md | docs/architecture/overview.md + data-flow.md + design-decisions.md | +| docs/DEPLOYMENT.md | docs/getting-started/installation.md + docs/deployment/kubernetes.md + helm.md + production.md | +| docs/TROUBLESHOOTING.md | docs/troubleshooting/README.md + common-issues.md | +| docs/TESTING.md | docs/development/testing.md | +| — (new) | docs/README.md | +| — (new) | docs/getting-started/quick-start.md | +| — (new) | docs/user-guide/features.md | +| — (new) | docs/architecture/adr/ | ### Sealed Secrets Plugin Current → Standard -| Current | Standard Location | -|---------|-------------------| -| README.md | README.md (extract contributing section) | -| CHANGELOG.md | CHANGELOG.md (expand) | -| — (new) | CONTRIBUTING.md (extract from README) | -| — (new) | SECURITY.md (new file) | -| docs/* | docs/* (mostly keep, enhance incomplete files) | +| Current | Standard Location | +| ------------ | ----------------------------------------------- | +| README.md | README.md (extract contributing section) | +| CHANGELOG.md | CHANGELOG.md (expand) | +| — (new) | CONTRIBUTING.md (extract from README) | +| — (new) | SECURITY.md (new file) | +| docs/\* | docs/\* (mostly keep, enhance incomplete files) | --- diff --git a/docs/TESTING.md b/docs/TESTING.md index 6808fd2..dfdfcef 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -18,13 +18,13 @@ Comprehensive guide to testing the Headlamp Polaris Plugin, covering unit tests, The Headlamp Polaris Plugin uses a multi-layered testing approach: -| Test Type | Framework | Purpose | Location | -|-----------|-----------|---------|----------| -| **Unit Tests** | Vitest | Test individual functions and components in isolation | `src/**/*.test.ts(x)` | -| **E2E Tests** | Playwright | Test complete user flows against live Headlamp instance | `e2e/*.spec.ts` | -| **Type Checking** | TypeScript | Ensure type safety across codebase | `tsc --noEmit` | -| **Linting** | ESLint | Enforce code style and catch common errors | `eslint src/` | -| **Formatting** | Prettier | Maintain consistent code formatting | `prettier --check src/` | +| Test Type | Framework | Purpose | Location | +| ----------------- | ---------- | ------------------------------------------------------- | ----------------------- | +| **Unit Tests** | Vitest | Test individual functions and components in isolation | `src/**/*.test.ts(x)` | +| **E2E Tests** | Playwright | Test complete user flows against live Headlamp instance | `e2e/*.spec.ts` | +| **Type Checking** | TypeScript | Ensure type safety across codebase | `tsc --noEmit` | +| **Linting** | ESLint | Enforce code style and catch common errors | `eslint src/` | +| **Formatting** | Prettier | Maintain consistent code formatting | `prettier --check src/` | ### Test Philosophy @@ -178,7 +178,9 @@ describe('DashboardView', () => { const mockData = { DisplayName: 'test-cluster', ClusterInfo: { Version: '1.27', Nodes: 3, Pods: 100, Namespaces: 10, Controllers: 50 }, - Results: [/* ... */], + Results: [ + /* ... */ + ], }; vi.spyOn(PolarisDataContext, 'usePolarisDataContext').mockReturnValue({ @@ -197,6 +199,7 @@ describe('DashboardView', () => { ### What to Unit Test ✅ **Do test:** + - Pure functions (score calculation, filtering, data transformation) - Data parsing and validation - Utility functions @@ -204,6 +207,7 @@ describe('DashboardView', () => { - Edge cases (empty arrays, null values, invalid input) ❌ **Don't test:** + - Third-party libraries (Headlamp, React) - Simple prop passing - Trivial getters/setters @@ -242,6 +246,7 @@ npx playwright show-trace test-results//trace.zip **1. Headlamp Instance** E2E tests require a running Headlamp instance with: + - Polaris plugin installed (version being tested) - Polaris dashboard deployed and accessible - RBAC configured (service proxy permissions) @@ -296,32 +301,32 @@ AUTHENTIK_PASSWORD=secret **File:** `e2e/polaris.spec.ts` -| Test | Description | Validates | -|------|-------------|-----------| -| `sidebar contains Polaris entry` | Polaris appears in sidebar | Plugin registration | -| `overview page renders cluster score` | Score displayed on overview | Data fetching, rendering | -| `namespaces page renders table` | Namespace table loads | Data parsing, table rendering | -| `namespace detail drawer opens` | Clicking namespace shows drawer | Navigation, drawer UI | -| `namespace detail drawer closes with Escape` | Keyboard shortcut works | Keyboard navigation | -| `namespace detail drawer opens from URL hash` | Direct URL navigation | URL routing, deep linking | +| Test | Description | Validates | +| --------------------------------------------- | ------------------------------- | ----------------------------- | +| `sidebar contains Polaris entry` | Polaris appears in sidebar | Plugin registration | +| `overview page renders cluster score` | Score displayed on overview | Data fetching, rendering | +| `namespaces page renders table` | Namespace table loads | Data parsing, table rendering | +| `namespace detail drawer opens` | Clicking namespace shows drawer | Navigation, drawer UI | +| `namespace detail drawer closes with Escape` | Keyboard shortcut works | Keyboard navigation | +| `namespace detail drawer opens from URL hash` | Direct URL navigation | URL routing, deep linking | **File:** `e2e/settings.spec.ts` -| Test | Description | Validates | -|------|-------------|-----------| -| `plugin settings page is accessible` | Settings page loads | Settings registration | -| `refresh interval can be changed` | Dropdown works | User preference persistence | -| `dashboard URL can be customized` | Input field works | URL configuration | -| `connection test button works` | Test functionality | API connectivity validation | +| Test | Description | Validates | +| ------------------------------------ | ------------------- | --------------------------- | +| `plugin settings page is accessible` | Settings page loads | Settings registration | +| `refresh interval can be changed` | Dropdown works | User preference persistence | +| `dashboard URL can be customized` | Input field works | URL configuration | +| `connection test button works` | Test functionality | API connectivity validation | **File:** `e2e/appbar.spec.ts` -| Test | Description | Validates | -|------|-------------|-----------| -| `app bar displays Polaris badge` | Badge visible in header | App bar integration | -| `badge shows cluster score` | Score matches dashboard | Data consistency | -| `clicking badge navigates to overview` | Navigation works | App bar action | -| `badge color reflects score` | Red/yellow/green based on score | Visual feedback | +| Test | Description | Validates | +| -------------------------------------- | ------------------------------- | ------------------- | +| `app bar displays Polaris badge` | Badge visible in header | App bar integration | +| `badge shows cluster score` | Score matches dashboard | Data consistency | +| `clicking badge navigates to overview` | Navigation works | App bar action | +| `badge color reflects score` | Red/yellow/green based on score | Visual feedback | ### Writing E2E Tests @@ -385,6 +390,7 @@ npx playwright test --debug ``` This opens Playwright Inspector where you can: + - Step through each test action - Inspect page state - Edit test selectors live @@ -455,12 +461,12 @@ jobs: Configure in GitHub repository settings (Settings → Secrets and variables → Actions): -| Secret | Required | Description | -|--------|----------|-------------| -| `HEADLAMP_URL` | Optional | Headlamp instance URL (defaults to configured instance) | -| `AUTHENTIK_USERNAME` | OIDC | Authentik username/email for CI user | -| `AUTHENTIK_PASSWORD` | OIDC | Authentik password | -| `HEADLAMP_TOKEN` | Token | Kubernetes service account token (alternative to OIDC) | +| Secret | Required | Description | +| -------------------- | -------- | ------------------------------------------------------- | +| `HEADLAMP_URL` | Optional | Headlamp instance URL (defaults to configured instance) | +| `AUTHENTIK_USERNAME` | OIDC | Authentik username/email for CI user | +| `AUTHENTIK_PASSWORD` | OIDC | Authentik password | +| `HEADLAMP_TOKEN` | Token | Kubernetes service account token (alternative to OIDC) | Set either `AUTHENTIK_USERNAME` + `AUTHENTIK_PASSWORD` **or** `HEADLAMP_TOKEN`. OIDC takes priority if both are set. @@ -478,11 +484,11 @@ Trigger workflows manually from GitHub Actions UI: ### Current Coverage -| Category | Coverage | Notes | -|----------|----------|-------| -| **API Functions** | 95% | Core utilities fully tested | -| **React Components** | 60% | Focus on critical render paths | -| **E2E User Flows** | 80% | Main features covered | +| Category | Coverage | Notes | +| -------------------- | -------- | ------------------------------ | +| **API Functions** | 95% | Core utilities fully tested | +| **React Components** | 60% | Focus on critical render paths | +| **E2E User Flows** | 80% | Main features covered | ### Coverage Goals @@ -507,18 +513,22 @@ open coverage/index.html ### Unit Testing 1. **Test behavior, not implementation** + - ✅ `expect(computeScore({ total: 100, pass: 75 })).toBe(75)` - ❌ `expect(mockInternalFunction).toHaveBeenCalled()` 2. **Use descriptive test names** + - ✅ `it('returns 0 when total checks is zero')` - ❌ `it('works')` 3. **One assertion per test (when possible)** + - Makes failures easier to debug - Exceptions: testing multiple properties of same object 4. **Mock external dependencies** + - Mock API calls, context providers, external libraries - Don't mock the code you're testing @@ -529,27 +539,33 @@ open coverage/index.html ### E2E Testing 1. **Use semantic selectors** + - ✅ `page.getByRole('button', { name: 'Close' })` - ✅ `page.getByText('Polaris — Overview')` - ❌ `page.locator('.MuiButton-root')` 2. **Wait for visibility, not arbitrary timeouts** + - ✅ `await expect(element).toBeVisible()` - ❌ `await page.waitForTimeout(5000)` 3. **Keep tests independent** + - Each test should work in isolation - Don't rely on previous tests' state 4. **Test complete user flows** + - Navigate → Interact → Verify outcome - Don't just test page loads 5. **Clean up after tests** + - Close drawers/modals - Reset state if needed 6. **Use storage state for auth** + - Reuse authenticated session across tests - Faster than logging in for every test @@ -560,15 +576,18 @@ open coverage/index.html ### General 1. **Run tests before committing** + ```bash npm run build && npm run lint && npm test ``` 2. **Fix failing tests immediately** + - Don't commit failing tests - Don't skip tests to "fix later" 3. **Update tests when changing code** + - Tests are documentation - Keep them in sync with implementation @@ -604,7 +623,7 @@ Check mocks are returning expected structure: ```typescript vi.spyOn(PolarisDataContext, 'usePolarisDataContext').mockReturnValue({ - data: mockData, // Ensure mockData has all required fields + data: mockData, // Ensure mockData has all required fields loading: false, error: null, refresh: vi.fn(), @@ -624,6 +643,7 @@ npm run e2e:headed ``` Common causes: + - Element hasn't rendered yet (use `toBeVisible()` instead of `toBeDefined()`) - Wrong selector (use Playwright Inspector to verify) - Authentication failed (check token/credentials) diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index ffacb18..5201500 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -20,34 +20,17 @@ This guide covers common issues encountered when using the Headlamp Polaris Plug ## Plugin Not Showing in Sidebar ### Symptoms + - Plugin appears in Settings → Plugins but sidebar entry is missing - No "Polaris" section in navigation - Routes like `/polaris` return 404 or blank page ### Common Causes -**1. Headlamp v0.39.0+ Plugin Loading Issue** - -**Root Cause**: Headlamp v0.39.0+ changed plugin loading behavior. With `config.watchPlugins: true` (default), catalog-managed plugins are treated as "development directory" plugins, causing the backend to serve metadata but frontend to never execute the JavaScript. - -**Solution**: Set `config.watchPlugins: false` in Headlamp configuration. - -```yaml -# HelmRelease values -config: - watchPlugins: false # CRITICAL for plugin manager -``` - -After applying this change: -1. Restart Headlamp pod -2. Hard refresh browser (Cmd+Shift+R or Ctrl+Shift+R) -3. Clear browser cache if needed - -**References**: See `deployment/PLUGIN_LOADING_FIX.md` for complete root cause analysis. - -**2. Plugin Not Installed** +**1. Plugin Not Installed** **Check plugin installation**: + ```bash # View Headlamp pod logs (plugin sidecar) kubectl logs -n kube-system deployment/headlamp -c headlamp-plugin @@ -58,23 +41,26 @@ kubectl logs -n kube-system deployment/headlamp -c headlamp-plugin ``` **Verify plugin files exist**: + ```bash kubectl exec -n kube-system deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/ # Should show: headlamp-polaris-plugin/ ``` -**3. JavaScript Cached by Browser** +**2. JavaScript Cached by Browser** After upgrading the plugin, old JavaScript may be cached. **Solution**: + - Hard refresh: Cmd+Shift+R (macOS) or Ctrl+Shift+R (Linux/Windows) - Clear browser cache for Headlamp domain - Open DevTools → Application → Clear Storage → Clear all -**4. Plugin Disabled in Settings** +**3. Plugin Disabled in Settings** **Check Settings → Plugins**: + - Navigate to Headlamp Settings → Plugins - Ensure "Polaris" plugin is enabled (toggle should be ON) - If disabled, enable it and refresh the page @@ -84,11 +70,13 @@ After upgrading the plugin, old JavaScript may be cached. ## 403 Forbidden Error ### Symptoms + - Error message: "Error loading Polaris audit data: 403 Forbidden" - Browser console shows 403 response from API proxy - Plugin sidebar shows but data fails to load ### Root Cause + User or service account lacks `services/proxy` permission on `polaris-dashboard` service in the `polaris` namespace. ### Solution @@ -96,11 +84,13 @@ User or service account lacks `services/proxy` permission on `polaris-dashboard` **1. Verify RBAC Configuration** Check if Role exists: + ```bash kubectl get role polaris-proxy-reader -n polaris -o yaml ``` Expected output: + ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -108,20 +98,22 @@ metadata: name: polaris-proxy-reader namespace: polaris rules: - - apiGroups: [""] - resources: ["services/proxy"] - resourceNames: ["polaris-dashboard"] - verbs: ["get"] + - apiGroups: [''] + resources: ['services/proxy'] + resourceNames: ['polaris-dashboard'] + verbs: ['get'] ``` **2. Verify RoleBinding** For service account mode: + ```bash kubectl get rolebinding headlamp-polaris-proxy -n polaris -o yaml ``` Expected subjects: + ```yaml subjects: - kind: ServiceAccount @@ -130,6 +122,7 @@ subjects: ``` For OIDC mode: + ```bash kubectl get rolebinding -n polaris -o yaml | grep -A 5 polaris-proxy-reader ``` @@ -172,6 +165,7 @@ EOF **4. Test RBAC Permissions** Service account mode: + ```bash # Impersonate Headlamp service account kubectl auth can-i get services/proxy \ @@ -182,6 +176,7 @@ kubectl auth can-i get services/proxy \ ``` OIDC mode (test as yourself): + ```bash kubectl auth can-i get services/proxy \ --resource-name=polaris-dashboard \ @@ -192,6 +187,7 @@ kubectl auth can-i get services/proxy \ **5. Restart Headlamp** After applying RBAC changes: + ```bash kubectl rollout restart deployment headlamp -n kube-system ``` @@ -201,11 +197,13 @@ kubectl rollout restart deployment headlamp -n kube-system ## 404 Not Found Error ### Symptoms + - Error message: "Error loading Polaris audit data: 404 Not Found" - Service proxy request returns 404 - Polaris dashboard not reachable ### Root Cause + Polaris dashboard service doesn't exist or is in a different namespace. ### Solution @@ -213,12 +211,14 @@ Polaris dashboard service doesn't exist or is in a different namespace. **1. Verify Polaris Installation** Check if Polaris is installed: + ```bash kubectl get pods -n polaris # Expected: polaris-dashboard-* pod running ``` Check if service exists: + ```bash kubectl get service polaris-dashboard -n polaris # Expected: ClusterIP service on port 80 @@ -227,6 +227,7 @@ kubectl get service polaris-dashboard -n polaris **2. Verify Service Name and Port** The plugin expects: + - **Namespace**: `polaris` - **Service Name**: `polaris-dashboard` - **Port**: `80` (or named port `dashboard`) @@ -246,11 +247,13 @@ If this returns 404, Polaris service is not configured correctly. **4. Check Polaris Dashboard Configuration** Verify Polaris is running with dashboard enabled: + ```bash kubectl get deployment polaris-dashboard -n polaris -o yaml | grep -A 5 dashboard ``` If `dashboard.enabled: false` in Helm values, enable it: + ```yaml # values.yaml dashboard: @@ -260,6 +263,7 @@ dashboard: **5. Reinstall Polaris** If Polaris is missing or misconfigured: + ```bash helm repo add fairwinds-stable https://charts.fairwinds.com/stable helm upgrade --install polaris fairwinds-stable/polaris \ @@ -273,21 +277,25 @@ helm upgrade --install polaris fairwinds-stable/polaris \ ## Plugin Settings Page Empty ### Symptoms + - Settings → Polaris shows title but no content - Refresh interval and dashboard URL fields not visible ### Root Cause (Fixed in v0.3.3) + Plugin settings registration name didn't match `package.json` name. ### Solution Upgrade to v0.3.3 or later: + ```bash # Via Headlamp UI: Settings → Plugins → Update # Or redeploy with latest version ``` If manually installing, ensure plugin name matches `package.json`: + ```typescript registerPluginSettings('headlamp-polaris-plugin', PolarisSettings, true); // NOT 'polaris' — must match package.json name @@ -298,6 +306,7 @@ registerPluginSettings('headlamp-polaris-plugin', PolarisSettings, true); ## Dark Mode Issues ### Symptoms + - Drawer background remains white in dark mode - Text is hard to read in dark mode - Theme colors don't match Headlamp UI @@ -308,6 +317,7 @@ Upgrade to v0.3.5 or later for complete dark mode support. **Verify CSS Variables**: The plugin uses MUI CSS variables for theming: + - `--mui-palette-background-default` (drawer background) - `--mui-palette-text-primary` (text color) - `--mui-palette-primary-main` (links, buttons) @@ -317,6 +327,7 @@ These automatically adapt to Headlamp's theme (light/dark/system). **Hard Refresh Required**: After upgrading from v0.3.4 or earlier, hard refresh your browser: + - macOS: Cmd+Shift+R - Linux/Windows: Ctrl+Shift+R @@ -328,6 +339,7 @@ If hard refresh doesn't help, clear cache for Headlamp domain. ## Data Not Loading / Infinite Spinner ### Symptoms + - Plugin shows "Loading Polaris audit data..." forever - No error message in UI - Data never appears @@ -339,6 +351,7 @@ If hard refresh doesn't help, clear cache for Headlamp domain. Open DevTools (F12) → Console tab. Look for: + - Network errors (CORS, timeouts, 5xx responses) - JavaScript errors - Failed API requests @@ -348,6 +361,7 @@ Look for: Open DevTools → Network tab → Filter by "results.json" Expected request: + ``` GET /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json Status: 200 @@ -355,6 +369,7 @@ Response: JSON data ``` Common issues: + - **Status 0 / Failed**: Network policy blocking request - **Status 403**: RBAC issue (see [403 Forbidden Error](#403-forbidden-error)) - **Status 404**: Service not found (see [404 Not Found Error](#404-not-found-error)) @@ -378,6 +393,7 @@ curl http://localhost:8080/results.json **4. Check Network Policies** If your cluster uses NetworkPolicies: + ```bash kubectl get networkpolicy -n polaris ``` @@ -385,6 +401,7 @@ kubectl get networkpolicy -n polaris Ensure API server (or Headlamp pod) can reach Polaris dashboard. **Example fix**: + ```yaml apiVersion: networking.k8s.io/v1 kind: NetworkPolicy @@ -399,7 +416,7 @@ spec: - Ingress ingress: - from: - - namespaceSelector: {} # Allow from all namespaces (API server) + - namespaceSelector: {} # Allow from all namespaces (API server) ports: - protocol: TCP port: 8080 @@ -408,6 +425,7 @@ spec: **5. Increase Timeout / Disable Auto-Refresh** If Polaris responds slowly: + - Open Settings → Polaris - Increase refresh interval to 10+ minutes - Or set to "Manual only" to disable auto-refresh @@ -423,6 +441,7 @@ If Polaris responds slowly: **Cause**: Network request failed (CORS, network policy, timeout) **Solution**: + 1. Check Network tab for actual HTTP status 2. Verify network policies allow API server → Polaris 3. Check Polaris pod is running @@ -434,6 +453,7 @@ If Polaris responds slowly: **Cause**: API returned HTML (error page) instead of JSON **Solution**: + 1. Check Network tab response body (likely 404 or 500 error page) 2. Verify Polaris service exists and is healthy 3. Check service proxy URL is correct @@ -453,6 +473,7 @@ If Polaris responds slowly: **Cause**: Polaris returned empty or malformed response **Solution**: + 1. Check Polaris logs for errors 2. Verify Polaris is scanning the cluster (check audit timestamp) 3. Test `/results.json` endpoint directly @@ -538,11 +559,13 @@ kubectl logs -n kube-system kube-apiserver-* | grep polaris-dashboard ### Sidecar Fails to Install Plugin **Symptoms**: + - Plugin sidecar logs show download errors - Plugin directory is empty - Settings → Plugins shows nothing **Check sidecar logs**: + ```bash kubectl logs -n kube-system deployment/headlamp -c headlamp-plugin ``` @@ -566,11 +589,13 @@ Error: 404 Not Found ``` **Solution**: Verify `archive-url` in plugin config matches GitHub release: + ```bash kubectl get configmap headlamp-plugin-config -n kube-system -o yaml ``` Expected format: + ``` https://github.com/privilegedescalation/headlamp-polaris-plugin/releases/download/v0.3.10/polaris-0.3.10.tar.gz ``` @@ -580,6 +605,7 @@ https://github.com/privilegedescalation/headlamp-polaris-plugin/releases/downloa **3. Permission denied writing to /headlamp/plugins** **Solution**: Ensure volume mount is writable: + ```yaml volumeMounts: - name: plugins @@ -591,17 +617,18 @@ volumeMounts: ### Plugin Manager Not Working **Symptoms**: + - Headlamp → Settings → Plugins shows "Catalog" tab but plugins don't install - "Install" button does nothing **Root Cause**: Plugin manager requires `config.pluginsDir` to be set. **Solution**: Configure Headlamp for plugin manager: + ```yaml # HelmRelease values config: pluginsDir: /headlamp/plugins - watchPlugins: false # CRITICAL for v0.39.0+ ``` --- @@ -609,25 +636,30 @@ config: ## ArtifactHub Sync Delays ### Symptoms + - New version released on GitHub but not showing in ArtifactHub - Headlamp plugin catalog shows old version ### Root Cause + ArtifactHub pulls metadata every 30 minutes. There is no webhook or push mechanism. ### Solution **Wait 30 minutes** after pushing a GitHub release, then check: + ``` https://artifacthub.io/packages/headlamp/headlamp-polaris-plugin/headlamp-polaris-plugin ``` **Verify metadata**: + 1. Check `artifacthub-pkg.yml` is in repository root 2. Check `headlamp/plugin/archive-url` points to GitHub release 3. Check `headlamp/plugin/archive-checksum` matches tarball SHA256 **Force sync** (ArtifactHub UI): + - Log in to ArtifactHub as package maintainer - Go to package settings - Click "Reindex now" @@ -643,23 +675,28 @@ If none of these solutions work, gather debugging information and open an issue: ### Required Information 1. **Version Information**: + ```bash kubectl get pods -n kube-system -l app.kubernetes.io/name=headlamp -o yaml | grep image: ``` 2. **Plugin Version**: + - Check Settings → Plugins in Headlamp UI - Or: `kubectl exec -n kube-system deployment/headlamp -c headlamp -- cat /headlamp/plugins/headlamp-polaris-plugin/package.json` 3. **Browser Console Output**: + - Open DevTools (F12) → Console - Screenshot or copy errors 4. **Network Tab**: + - Open DevTools → Network - Screenshot failed requests to `results.json` 5. **Pod Logs**: + ```bash kubectl logs -n kube-system deployment/headlamp -c headlamp --tail=100 kubectl logs -n polaris deployment/polaris-dashboard --tail=100 diff --git a/docs/architecture/adr/001-react-context-for-state.md b/docs/architecture/adr/001-react-context-for-state.md index b53ab2e..f9e346c 100644 --- a/docs/architecture/adr/001-react-context-for-state.md +++ b/docs/architecture/adr/001-react-context-for-state.md @@ -7,6 +7,7 @@ ## Context The Headlamp Polaris Plugin needs to fetch Polaris audit data once and share it across multiple components: + - Dashboard view (cluster overview) - Namespaces list view - Namespace detail view (drawer) @@ -16,12 +17,14 @@ The Headlamp Polaris Plugin needs to fetch Polaris audit data once and share it Multiple state management approaches are available: Redux, Zustand, Jotai, Recoil, React Context (built-in), or component props with prop drilling. **Constraints:** + - Headlamp plugin environment does not allow adding external dependencies (peer dependencies only) - Redux, Zustand, Jotai, Recoil are not available in the plugin runtime - Plugin must work with Headlamp's existing React context (React 17+) - Bundle size should remain small (<50 KB) **Requirements:** + - Share `AuditData` object across all views without duplicate API calls - Support auto-refresh on user-configurable interval (1-30 minutes) - Handle loading and error states consistently @@ -32,6 +35,7 @@ Multiple state management approaches are available: Redux, Zustand, Jotai, Recoi Use **React Context API** (built-in, no dependencies) for shared state management. **Implementation:** + - `PolarisDataProvider` wraps all plugin routes - `usePolarisDataContext()` hook provides `{ data, loading, error, refresh }` to consumers - Single fetch shared across all views @@ -67,12 +71,14 @@ Use **React Context API** (built-in, no dependencies) for shared state managemen ### Option 1: Redux **Pros:** + - Powerful state management with middleware - Excellent DevTools for debugging - Time-travel debugging - Well-established patterns **Cons:** + - Redux is not available as a peer dependency in Headlamp plugins - Massive overkill for single AuditData object - Adds significant bundle size (~10-15 KB) @@ -83,11 +89,13 @@ Use **React Context API** (built-in, no dependencies) for shared state managemen ### Option 2: Zustand **Pros:** + - Lightweight (~1 KB) - Simple API similar to `useState` - No provider boilerplate **Cons:** + - External peer dependency (not available in plugin runtime) - Still adds bundle size - Unnecessary for read-only state @@ -97,11 +105,13 @@ Use **React Context API** (built-in, no dependencies) for shared state managemen ### Option 3: Component Props (Prop Drilling) **Pros:** + - No dependencies - Explicit data flow - TypeScript tracks prop types **Cons:** + - Prop drilling through 5+ component layers (index.tsx → route → view → subcomponent) - Duplicate fetches if not carefully managed - Refactoring nightmare if component tree changes @@ -112,10 +122,12 @@ Use **React Context API** (built-in, no dependencies) for shared state managemen ### Option 4: Global Variable / Module State **Pros:** + - Simple to implement - No React dependencies **Cons:** + - No reactivity (components don't re-render on data change) - No built-in loading/error handling - Breaks React's declarative model @@ -126,6 +138,7 @@ Use **React Context API** (built-in, no dependencies) for shared state managemen ## Implementation Details **Context Definition:** + ```typescript interface PolarisDataContextValue { data: AuditData | null; @@ -138,6 +151,7 @@ const PolarisDataContext = React.createContext(null); @@ -163,6 +177,7 @@ export function PolarisDataProvider({ children }: { children: React.ReactNode }) ``` **Consumer Hook:** + ```typescript export function usePolarisDataContext() { const context = useContext(PolarisDataContext); @@ -176,6 +191,7 @@ export function usePolarisDataContext() { ## Validation Criteria **Success Metrics:** + - ✅ All views share single fetch (verified via network tab - one request per refresh) - ✅ No duplicate API calls (verified via Kubernetes audit logs) - ✅ Auto-refresh works correctly (5-30 minute intervals) @@ -184,6 +200,7 @@ export function usePolarisDataContext() { - ✅ Bundle size remains <50 KB (currently ~27 KB) **Tested Scenarios:** + - ✅ Initial load with loading spinner - ✅ Error handling (403, 404, network errors) - ✅ Manual refresh via button @@ -200,6 +217,6 @@ export function usePolarisDataContext() { ## Revision History -| Date | Author | Change | -|------|--------|--------| +| Date | Author | Change | +| ---------- | ----------- | ---------------- | | 2026-02-12 | Plugin Team | Initial decision | diff --git a/docs/architecture/adr/README.md b/docs/architecture/adr/README.md index 8a07d5b..f88533c 100644 --- a/docs/architecture/adr/README.md +++ b/docs/architecture/adr/README.md @@ -41,17 +41,21 @@ What is the change that we're proposing and/or doing? What becomes easier or more difficult to do because of this change? ### Positive + - ... ### Negative + - ... ### Neutral + - ... ## Alternatives Considered ### Option 1: Name + **Pros**: ... **Cons**: ... **Decision**: Not chosen because... @@ -63,8 +67,8 @@ What becomes easier or more difficult to do because of this change? ## ADR Index -| ADR | Title | Status | Date | -|-----|-------|--------|------| +| ADR | Title | Status | Date | +| ------------------------------------- | -------------------------------------- | -------- | ---------- | | [001](001-react-context-for-state.md) | Use React Context for State Management | Accepted | 2026-02-12 | **Note:** Additional ADRs documenting other significant decisions (service proxy approach, drawer navigation, MUI import restrictions) can be created following the template above. diff --git a/docs/architecture/data-flow.md b/docs/architecture/data-flow.md index b2ccc7d..7c88faf 100644 --- a/docs/architecture/data-flow.md +++ b/docs/architecture/data-flow.md @@ -212,46 +212,46 @@ If no match found: ```typescript interface AuditData { - PolarisOutputVersion: string; // "1.0" - AuditTime: string; // ISO 8601 timestamp - SourceType: string; // "Cluster" - SourceName: string; // Cluster identifier - DisplayName: string; // Human-readable name + PolarisOutputVersion: string; // "1.0" + AuditTime: string; // ISO 8601 timestamp + SourceType: string; // "Cluster" + SourceName: string; // Cluster identifier + DisplayName: string; // Human-readable name ClusterInfo: { - Version: string; // K8s version + Version: string; // K8s version Nodes: number; Pods: number; Namespaces: number; Controllers: number; }; - Results: Result[]; // Array of resource audit results + Results: Result[]; // Array of resource audit results } interface Result { - Name: string; // Resource name - Namespace: string; // Kubernetes namespace - Kind: string; // "Deployment", "StatefulSet", etc. - Results: ResultSet; // Resource-level checks + Name: string; // Resource name + Namespace: string; // Kubernetes namespace + Kind: string; // "Deployment", "StatefulSet", etc. + Results: ResultSet; // Resource-level checks PodResult?: { Name: string; - Results: ResultSet; // Pod-level checks + Results: ResultSet; // Pod-level checks ContainerResults: { Name: string; - Results: ResultSet; // Container-level checks + Results: ResultSet; // Container-level checks }[]; }; - CreatedTime: string; // ISO 8601 timestamp + CreatedTime: string; // ISO 8601 timestamp } type ResultSet = Record; interface ResultMessage { - ID: string; // Check ID (e.g., "cpuLimitsMissing") - Message: string; // Human-readable message - Details: string[]; // Additional context - Success: boolean; // true = passed, false = failed - Severity: "ignore" | "warning" | "danger"; - Category: string; // "Security", "Efficiency", etc. + ID: string; // Check ID (e.g., "cpuLimitsMissing") + Message: string; // Human-readable message + Details: string[]; // Additional context + Success: boolean; // true = passed, false = failed + Severity: 'ignore' | 'warning' | 'danger'; + Category: string; // "Security", "Efficiency", etc. } ``` @@ -259,11 +259,11 @@ interface ResultMessage { ```typescript interface ResultCounts { - total: number; // Total checks performed - pass: number; // Checks that passed (Success: true) - warning: number; // Failed checks with Severity: "warning" - danger: number; // Failed checks with Severity: "danger" - skipped: number; // Failed checks with Severity: "ignore" + total: number; // Total checks performed + pass: number; // Checks that passed (Success: true) + warning: number; // Failed checks with Severity: "warning" + danger: number; // Failed checks with Severity: "danger" + skipped: number; // Failed checks with Severity: "ignore" } ``` @@ -361,21 +361,25 @@ function getNamespaces(data: AuditData): string[] { ## Caching Strategy **Current Implementation:** + - Data fetched once and stored in React Context - Shared across all plugin views (no duplicate fetches) - Cached until manual refresh or auto-refresh interval **Cache Invalidation:** + - Manual refresh button click - Auto-refresh interval elapses - Settings change (dashboard URL) **No Persistence:** + - Data NOT persisted to localStorage - Each browser session fetches fresh data - No offline mode **Future Enhancement:** + - IndexedDB caching for offline access - Incremental updates (fetch only changed namespaces) - Service Worker for background refresh diff --git a/docs/architecture/design-decisions.md b/docs/architecture/design-decisions.md index 4ef0528..337ffde 100644 --- a/docs/architecture/design-decisions.md +++ b/docs/architecture/design-decisions.md @@ -7,14 +7,16 @@ Key architectural choices and their rationale for the Headlamp Polaris Plugin. **Decision:** Use Kubernetes service proxy, not direct ClusterIP access **Context:** + - Plugin needs to access Polaris dashboard API - Two options: Direct ClusterIP access or Kubernetes service proxy --Headlamp already has K8s API credentials + -Headlamp already has K8s API credentials **Decision:** Use service proxy path: `/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json` **Rationale:** + - Headlamp already has K8s API credentials (service account or user token) - Service proxy leverages existing RBAC (no new credentials needed) - Works with Headlamp's token auth and OIDC @@ -22,10 +24,12 @@ Use service proxy path: `/api/v1/namespaces/polaris/services/polaris-dashboard:8 - Consistent with Headlamp's architecture (all API calls go through K8s API) **Trade-offs:** + - ✅ **Pros:** Simpler RBAC, works with user tokens, no new credentials - ❌ **Cons:** Longer URL path, requires `services/proxy` permission **Alternatives Considered:** + - Direct ClusterIP access → Rejected (requires new credentials, network policies) - External Polaris URL → Supported as optional feature (custom URL setting) @@ -34,6 +38,7 @@ Use service proxy path: `/api/v1/namespaces/polaris/services/polaris-dashboard:8 **Decision:** Use React Context for state management **Context:** + - Plugin needs to share Polaris audit data across multiple views - Options: React Context, Redux, Zustand, or component props @@ -41,16 +46,19 @@ Use service proxy path: `/api/v1/namespaces/polaris/services/polaris-dashboard:8 Use React Context with `PolarisDataProvider` **Rationale:** + 1. **Simple state:** Single AuditData object, no complex mutations 2. **Read-only:** No transactions, undo/redo, or optimistic updates 3. **Headlamp constraints:** Cannot add external dependencies (Redux not bundled) 4. **Performance:** Data changes infrequently (5-30 minute refresh interval) **Trade-offs:** + - ✅ **Pros:** No dependencies, simple API, built-in React feature - ❌ **Cons:** All consumers re-render on data change (acceptable for infrequent updates) **Alternatives Considered:** + - Redux → Rejected (not available in plugin environment) - Zustand → Rejected (requires external dependency) - Component props → Rejected (prop drilling, duplicate fetches) @@ -60,6 +68,7 @@ Use React Context with `PolarisDataProvider` **Decision:** Use drawer for namespace detail, not dedicated route **Context:** + - Namespaces list needs drill-down to per-namespace detail - Options: Dedicated route (`/polaris/ns/:namespace`) or drawer overlay @@ -67,16 +76,19 @@ Use React Context with `PolarisDataProvider` Use drawer with URL hash (`/polaris/namespaces#kube-system`) **Rationale:** + - **Better UX:** Drawer overlays table, preserves scroll position and context - **URL hash:** Preserves navigation state, supports browser back/forward - **Keyboard shortcuts:** Escape key to close drawer - **Sidebar limitation:** Headlamp sidebar doesn't support 3-level nesting **Trade-offs:** + - ✅ **Pros:** Better UX, preserves context, keyboard navigation - ❌ **Cons:** Hash-based routing (not "true" route), drawer accessibility considerations **Alternatives Considered:** + - Dedicated route → Rejected (loses table context, requires back navigation) - Modal → Rejected (less natural for drill-down, no URL state) @@ -85,6 +97,7 @@ Use drawer with URL hash (`/polaris/namespaces#kube-system`) **Decision:** Never import from `@mui/material` or `@mui/icons-material` **Context:** + - Plugin needs UI components (buttons, icons, etc.) - Headlamp uses MUI but doesn't expose full library to plugins @@ -92,20 +105,24 @@ Use drawer with URL hash (`/polaris/namespaces#kube-system`) Use only Headlamp CommonComponents or HTML elements with inline styles **Rationale:** + - Importing MUI causes `createSvgIcon undefined` runtime error - Headlamp plugin environment provides limited MUI exports - CommonComponents cover 90% of use cases **Implementation:** + - Use `StatusLabel`, `SectionBox`, `SimpleTable` from CommonComponents - Use standard HTML elements (`