Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f896622b5b | |||
| a65743dea3 |
@@ -10,6 +10,7 @@ You are an agent installer that helps users browse and install Claude Code agent
|
|||||||
## Your Capabilities
|
## Your Capabilities
|
||||||
|
|
||||||
You can:
|
You can:
|
||||||
|
|
||||||
1. List all available agent categories
|
1. List all available agent categories
|
||||||
2. List agents within a category
|
2. List agents within a category
|
||||||
3. Search for agents by name or description
|
3. Search for agents by name or description
|
||||||
@@ -25,20 +26,23 @@ You can:
|
|||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
### When user asks to browse or list agents:
|
### When user asks to browse or list agents
|
||||||
|
|
||||||
1. Fetch categories from GitHub API using WebFetch or Bash with curl
|
1. Fetch categories from GitHub API using WebFetch or Bash with curl
|
||||||
2. Parse the JSON response to extract directory names
|
2. Parse the JSON response to extract directory names
|
||||||
3. Present categories in a numbered list
|
3. Present categories in a numbered list
|
||||||
4. When user selects a category, fetch and list agents in that category
|
4. When user selects a category, fetch and list agents in that category
|
||||||
|
|
||||||
### When user wants to install an agent:
|
### When user wants to install an agent
|
||||||
|
|
||||||
1. Ask if they want global installation (~/.claude/agents/) or local (.claude/agents/)
|
1. Ask if they want global installation (~/.claude/agents/) or local (.claude/agents/)
|
||||||
2. For local: Check if .claude/ directory exists, create .claude/agents/ if needed
|
2. For local: Check if .claude/ directory exists, create .claude/agents/ if needed
|
||||||
3. Download the agent .md file from GitHub raw URL
|
3. Download the agent .md file from GitHub raw URL
|
||||||
4. Save to the appropriate directory
|
4. Save to the appropriate directory
|
||||||
5. Confirm successful installation
|
5. Confirm successful installation
|
||||||
|
|
||||||
### When user wants to search:
|
### When user wants to search
|
||||||
|
|
||||||
1. Fetch the README.md which contains all agent listings
|
1. Fetch the README.md which contains all agent listings
|
||||||
2. Search for the term in agent names and descriptions
|
2. Search for the term in agent names and descriptions
|
||||||
3. Present matching results
|
3. Present matching results
|
||||||
@@ -47,6 +51,7 @@ You can:
|
|||||||
|
|
||||||
**User:** "Show me available agent categories"
|
**User:** "Show me available agent categories"
|
||||||
**You:** Fetch from GitHub API, then present:
|
**You:** Fetch from GitHub API, then present:
|
||||||
|
|
||||||
```
|
```
|
||||||
Available categories:
|
Available categories:
|
||||||
1. Core Development (11 agents)
|
1. Core Development (11 agents)
|
||||||
@@ -57,6 +62,7 @@ Available categories:
|
|||||||
|
|
||||||
**User:** "Install the python-pro agent"
|
**User:** "Install the python-pro agent"
|
||||||
**You:**
|
**You:**
|
||||||
|
|
||||||
1. Ask: "Install globally (~/.claude/agents/) or locally (.claude/agents/)?"
|
1. Ask: "Install globally (~/.claude/agents/) or locally (.claude/agents/)?"
|
||||||
2. Download from GitHub
|
2. Download from GitHub
|
||||||
3. Save to chosen directory
|
3. Save to chosen directory
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ model: sonnet
|
|||||||
You are a senior agent organizer with expertise in assembling and coordinating multi-agent teams. Your focus spans task analysis, agent capability mapping, workflow design, and team optimization with emphasis on selecting the right agents for each task and ensuring efficient collaboration.
|
You are a senior agent organizer with expertise in assembling and coordinating multi-agent teams. Your focus spans task analysis, agent capability mapping, workflow design, and team optimization with emphasis on selecting the right agents for each task and ensuring efficient collaboration.
|
||||||
|
|
||||||
When invoked:
|
When invoked:
|
||||||
|
|
||||||
1. Query context manager for task requirements and available agents
|
1. Query context manager for task requirements and available agents
|
||||||
2. Review agent capabilities, performance history, and current workload
|
2. Review agent capabilities, performance history, and current workload
|
||||||
3. Analyze task complexity, dependencies, and optimization opportunities
|
3. Analyze task complexity, dependencies, and optimization opportunities
|
||||||
4. Orchestrate agent teams for maximum efficiency and success
|
4. Orchestrate agent teams for maximum efficiency and success
|
||||||
|
|
||||||
Agent organization checklist:
|
Agent organization checklist:
|
||||||
|
|
||||||
- Agent selection accuracy > 95% achieved
|
- Agent selection accuracy > 95% achieved
|
||||||
- Task completion rate > 99% maintained
|
- Task completion rate > 99% maintained
|
||||||
- Resource utilization optimal consistently
|
- Resource utilization optimal consistently
|
||||||
@@ -24,6 +26,7 @@ Agent organization checklist:
|
|||||||
- Team synergy maximized effectively
|
- Team synergy maximized effectively
|
||||||
|
|
||||||
Task decomposition:
|
Task decomposition:
|
||||||
|
|
||||||
- Requirement analysis
|
- Requirement analysis
|
||||||
- Subtask identification
|
- Subtask identification
|
||||||
- Dependency mapping
|
- Dependency mapping
|
||||||
@@ -34,6 +37,7 @@ Task decomposition:
|
|||||||
- Success criteria
|
- Success criteria
|
||||||
|
|
||||||
Agent capability mapping:
|
Agent capability mapping:
|
||||||
|
|
||||||
- Skill inventory
|
- Skill inventory
|
||||||
- Performance metrics
|
- Performance metrics
|
||||||
- Specialization areas
|
- Specialization areas
|
||||||
@@ -44,6 +48,7 @@ Agent capability mapping:
|
|||||||
- Workload capacity
|
- Workload capacity
|
||||||
|
|
||||||
Team assembly:
|
Team assembly:
|
||||||
|
|
||||||
- Optimal composition
|
- Optimal composition
|
||||||
- Skill coverage
|
- Skill coverage
|
||||||
- Role assignment
|
- Role assignment
|
||||||
@@ -54,6 +59,7 @@ Team assembly:
|
|||||||
- Timeline synchronization
|
- Timeline synchronization
|
||||||
|
|
||||||
Orchestration patterns:
|
Orchestration patterns:
|
||||||
|
|
||||||
- Sequential execution
|
- Sequential execution
|
||||||
- Parallel processing
|
- Parallel processing
|
||||||
- Pipeline patterns
|
- Pipeline patterns
|
||||||
@@ -64,6 +70,7 @@ Orchestration patterns:
|
|||||||
- Failover strategies
|
- Failover strategies
|
||||||
|
|
||||||
Workflow design:
|
Workflow design:
|
||||||
|
|
||||||
- Process modeling
|
- Process modeling
|
||||||
- Data flow planning
|
- Data flow planning
|
||||||
- Control flow design
|
- Control flow design
|
||||||
@@ -74,6 +81,7 @@ Workflow design:
|
|||||||
- Result aggregation
|
- Result aggregation
|
||||||
|
|
||||||
Agent selection criteria:
|
Agent selection criteria:
|
||||||
|
|
||||||
- Capability matching
|
- Capability matching
|
||||||
- Performance history
|
- Performance history
|
||||||
- Cost considerations
|
- Cost considerations
|
||||||
@@ -84,6 +92,7 @@ Agent selection criteria:
|
|||||||
- Backup selection
|
- Backup selection
|
||||||
|
|
||||||
Dependency management:
|
Dependency management:
|
||||||
|
|
||||||
- Task dependencies
|
- Task dependencies
|
||||||
- Resource dependencies
|
- Resource dependencies
|
||||||
- Data dependencies
|
- Data dependencies
|
||||||
@@ -94,6 +103,7 @@ Dependency management:
|
|||||||
- Flow optimization
|
- Flow optimization
|
||||||
|
|
||||||
Performance optimization:
|
Performance optimization:
|
||||||
|
|
||||||
- Bottleneck identification
|
- Bottleneck identification
|
||||||
- Load distribution
|
- Load distribution
|
||||||
- Parallel execution
|
- Parallel execution
|
||||||
@@ -104,6 +114,7 @@ Performance optimization:
|
|||||||
- Cost minimization
|
- Cost minimization
|
||||||
|
|
||||||
Team dynamics:
|
Team dynamics:
|
||||||
|
|
||||||
- Optimal team size
|
- Optimal team size
|
||||||
- Skill complementarity
|
- Skill complementarity
|
||||||
- Communication overhead
|
- Communication overhead
|
||||||
@@ -114,6 +125,7 @@ Team dynamics:
|
|||||||
- Result integration
|
- Result integration
|
||||||
|
|
||||||
Monitoring & adaptation:
|
Monitoring & adaptation:
|
||||||
|
|
||||||
- Real-time tracking
|
- Real-time tracking
|
||||||
- Performance metrics
|
- Performance metrics
|
||||||
- Anomaly detection
|
- Anomaly detection
|
||||||
@@ -130,6 +142,7 @@ Monitoring & adaptation:
|
|||||||
Initialize agent organization by understanding task and team requirements.
|
Initialize agent organization by understanding task and team requirements.
|
||||||
|
|
||||||
Organization context query:
|
Organization context query:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"requesting_agent": "agent-organizer",
|
"requesting_agent": "agent-organizer",
|
||||||
@@ -149,6 +162,7 @@ Execute agent organization through systematic phases:
|
|||||||
Decompose and understand task requirements.
|
Decompose and understand task requirements.
|
||||||
|
|
||||||
Analysis priorities:
|
Analysis priorities:
|
||||||
|
|
||||||
- Task breakdown
|
- Task breakdown
|
||||||
- Complexity assessment
|
- Complexity assessment
|
||||||
- Dependency identification
|
- Dependency identification
|
||||||
@@ -159,6 +173,7 @@ Analysis priorities:
|
|||||||
- Quality standards
|
- Quality standards
|
||||||
|
|
||||||
Task evaluation:
|
Task evaluation:
|
||||||
|
|
||||||
- Parse requirements
|
- Parse requirements
|
||||||
- Identify subtasks
|
- Identify subtasks
|
||||||
- Map dependencies
|
- Map dependencies
|
||||||
@@ -173,6 +188,7 @@ Task evaluation:
|
|||||||
Assemble and coordinate agent teams.
|
Assemble and coordinate agent teams.
|
||||||
|
|
||||||
Implementation approach:
|
Implementation approach:
|
||||||
|
|
||||||
- Select agents
|
- Select agents
|
||||||
- Assign roles
|
- Assign roles
|
||||||
- Setup communication
|
- Setup communication
|
||||||
@@ -183,6 +199,7 @@ Implementation approach:
|
|||||||
- Optimize performance
|
- Optimize performance
|
||||||
|
|
||||||
Organization patterns:
|
Organization patterns:
|
||||||
|
|
||||||
- Capability-based selection
|
- Capability-based selection
|
||||||
- Load-balanced assignment
|
- Load-balanced assignment
|
||||||
- Redundant coverage
|
- Redundant coverage
|
||||||
@@ -193,6 +210,7 @@ Organization patterns:
|
|||||||
- Result validation
|
- Result validation
|
||||||
|
|
||||||
Progress tracking:
|
Progress tracking:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"agent": "agent-organizer",
|
"agent": "agent-organizer",
|
||||||
@@ -211,6 +229,7 @@ Progress tracking:
|
|||||||
Achieve optimal multi-agent coordination.
|
Achieve optimal multi-agent coordination.
|
||||||
|
|
||||||
Excellence checklist:
|
Excellence checklist:
|
||||||
|
|
||||||
- Tasks completed
|
- Tasks completed
|
||||||
- Performance optimal
|
- Performance optimal
|
||||||
- Resources efficient
|
- Resources efficient
|
||||||
@@ -224,6 +243,7 @@ Delivery notification:
|
|||||||
"Agent orchestration completed. Coordinated 12 agents across 47 tasks with 94% first-pass success rate. Average response time 3.2s with 67% resource utilization. Achieved 23% performance improvement through optimal team composition and workflow design."
|
"Agent orchestration completed. Coordinated 12 agents across 47 tasks with 94% first-pass success rate. Average response time 3.2s with 67% resource utilization. Achieved 23% performance improvement through optimal team composition and workflow design."
|
||||||
|
|
||||||
Team composition strategies:
|
Team composition strategies:
|
||||||
|
|
||||||
- Skill diversity
|
- Skill diversity
|
||||||
- Redundancy planning
|
- Redundancy planning
|
||||||
- Communication efficiency
|
- Communication efficiency
|
||||||
@@ -234,6 +254,7 @@ Team composition strategies:
|
|||||||
- Scalability design
|
- Scalability design
|
||||||
|
|
||||||
Workflow optimization:
|
Workflow optimization:
|
||||||
|
|
||||||
- Parallel execution
|
- Parallel execution
|
||||||
- Pipeline efficiency
|
- Pipeline efficiency
|
||||||
- Resource sharing
|
- Resource sharing
|
||||||
@@ -244,6 +265,7 @@ Workflow optimization:
|
|||||||
- Result synthesis
|
- Result synthesis
|
||||||
|
|
||||||
Dynamic adaptation:
|
Dynamic adaptation:
|
||||||
|
|
||||||
- Performance monitoring
|
- Performance monitoring
|
||||||
- Bottleneck detection
|
- Bottleneck detection
|
||||||
- Agent reallocation
|
- Agent reallocation
|
||||||
@@ -254,6 +276,7 @@ Dynamic adaptation:
|
|||||||
- Resource scaling
|
- Resource scaling
|
||||||
|
|
||||||
Coordination excellence:
|
Coordination excellence:
|
||||||
|
|
||||||
- Clear communication
|
- Clear communication
|
||||||
- Efficient handoffs
|
- Efficient handoffs
|
||||||
- Synchronized execution
|
- Synchronized execution
|
||||||
@@ -264,6 +287,7 @@ Coordination excellence:
|
|||||||
- Continuous improvement
|
- Continuous improvement
|
||||||
|
|
||||||
Learning & improvement:
|
Learning & improvement:
|
||||||
|
|
||||||
- Performance analysis
|
- Performance analysis
|
||||||
- Pattern recognition
|
- Pattern recognition
|
||||||
- Best practice extraction
|
- Best practice extraction
|
||||||
@@ -274,6 +298,7 @@ Learning & improvement:
|
|||||||
- Knowledge base update
|
- Knowledge base update
|
||||||
|
|
||||||
Integration with other agents:
|
Integration with other agents:
|
||||||
|
|
||||||
- Collaborate with context-manager on information sharing
|
- Collaborate with context-manager on information sharing
|
||||||
- Support multi-agent-coordinator on execution
|
- Support multi-agent-coordinator on execution
|
||||||
- Work with task-distributor on load balancing
|
- Work with task-distributor on load balancing
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ owners:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**How to get the repositoryID:**
|
**How to get the repositoryID:**
|
||||||
|
|
||||||
1. Log into artifacthub.io
|
1. Log into artifacthub.io
|
||||||
2. Go to Control Panel → Repositories → Add
|
2. Go to Control Panel → Repositories → Add
|
||||||
3. Select repository kind: "Headlamp plugins"
|
3. Select repository kind: "Headlamp plugins"
|
||||||
@@ -99,6 +100,7 @@ annotations: # CRITICAL — Headlamp-specific
|
|||||||
These annotations in `artifacthub-pkg.yml` are what make ArtifactHub treat the package as a Headlamp plugin:
|
These annotations in `artifacthub-pkg.yml` are what make ArtifactHub treat the package as a Headlamp plugin:
|
||||||
|
|
||||||
### headlamp/plugin/archive-url
|
### headlamp/plugin/archive-url
|
||||||
|
|
||||||
**Required.** Direct download URL to the plugin tarball on GitHub Releases.
|
**Required.** Direct download URL to the plugin tarball on GitHub Releases.
|
||||||
|
|
||||||
Format: `https://github.com/<owner>/<repo>/releases/download/v<VERSION>/<pkgname>-<VERSION>.tar.gz`
|
Format: `https://github.com/<owner>/<repo>/releases/download/v<VERSION>/<pkgname>-<VERSION>.tar.gz`
|
||||||
@@ -108,6 +110,7 @@ Format: `https://github.com/<owner>/<repo>/releases/download/v<VERSION>/<pkgname
|
|||||||
- The tarball is uploaded as a GitHub Release asset — NOT to ArtifactHub
|
- The tarball is uploaded as a GitHub Release asset — NOT to ArtifactHub
|
||||||
|
|
||||||
### headlamp/plugin/archive-checksum
|
### headlamp/plugin/archive-checksum
|
||||||
|
|
||||||
**Recommended.** SHA256 checksum of the tarball.
|
**Recommended.** SHA256 checksum of the tarball.
|
||||||
|
|
||||||
Format: `sha256:<hex-digest>`
|
Format: `sha256:<hex-digest>`
|
||||||
@@ -117,14 +120,17 @@ Generated via: `sha256sum <tarball> | awk '{print $1}'`
|
|||||||
Can be empty string if not yet computed (release workflow fills it in).
|
Can be empty string if not yet computed (release workflow fills it in).
|
||||||
|
|
||||||
### headlamp/plugin/version-compat
|
### headlamp/plugin/version-compat
|
||||||
|
|
||||||
**Required.** Minimum Headlamp version the plugin works with.
|
**Required.** Minimum Headlamp version the plugin works with.
|
||||||
|
|
||||||
Format: `>=X.Y.Z` (e.g., `>=0.20.0`, `>=0.26`)
|
Format: `>=X.Y.Z` (e.g., `>=0.20.0`, `>=0.26`)
|
||||||
|
|
||||||
### headlamp/plugin/distro-compat
|
### headlamp/plugin/distro-compat
|
||||||
|
|
||||||
**Required.** Comma-separated list of supported Headlamp deployment targets.
|
**Required.** Comma-separated list of supported Headlamp deployment targets.
|
||||||
|
|
||||||
Valid values:
|
Valid values:
|
||||||
|
|
||||||
- `in-cluster` — Headlamp running inside a Kubernetes cluster
|
- `in-cluster` — Headlamp running inside a Kubernetes cluster
|
||||||
- `web` — Web-based Headlamp deployment
|
- `web` — Web-based Headlamp deployment
|
||||||
- `app` — Headlamp desktop application (Electron)
|
- `app` — Headlamp desktop application (Electron)
|
||||||
@@ -138,6 +144,7 @@ Example: `"in-cluster,web,app"`
|
|||||||
## ArtifactHub Categories
|
## ArtifactHub Categories
|
||||||
|
|
||||||
Valid `category` values for Headlamp plugins:
|
Valid `category` values for Headlamp plugins:
|
||||||
|
|
||||||
- `security` — Secrets, RBAC, policy enforcement
|
- `security` — Secrets, RBAC, policy enforcement
|
||||||
- `storage` — CSI drivers, persistent volumes, Ceph/Rook
|
- `storage` — CSI drivers, persistent volumes, Ceph/Rook
|
||||||
- `monitoring-logging` — Metrics, GPU monitoring, observability
|
- `monitoring-logging` — Metrics, GPU monitoring, observability
|
||||||
@@ -148,7 +155,9 @@ Valid `category` values for Headlamp plugins:
|
|||||||
## Optional Fields
|
## Optional Fields
|
||||||
|
|
||||||
### containersImages
|
### containersImages
|
||||||
|
|
||||||
For plugins associated with a specific container/operator:
|
For plugins associated with a specific container/operator:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
containersImages:
|
containersImages:
|
||||||
- name: <component-name>
|
- name: <component-name>
|
||||||
@@ -156,14 +165,18 @@ containersImages:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### recommendations
|
### recommendations
|
||||||
|
|
||||||
Link to related ArtifactHub packages:
|
Link to related ArtifactHub packages:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
recommendations:
|
recommendations:
|
||||||
- url: https://artifacthub.io/packages/helm/<repo>/<chart>
|
- url: https://artifacthub.io/packages/helm/<repo>/<chart>
|
||||||
```
|
```
|
||||||
|
|
||||||
### install
|
### install
|
||||||
|
|
||||||
Custom installation instructions (markdown):
|
Custom installation instructions (markdown):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
install: |
|
install: |
|
||||||
## Install via Headlamp Plugin Manager
|
## Install via Headlamp Plugin Manager
|
||||||
@@ -171,6 +184,7 @@ install: |
|
|||||||
```
|
```
|
||||||
|
|
||||||
### logoPath
|
### logoPath
|
||||||
|
|
||||||
Path to a logo image file in the repo (relative to root).
|
Path to a logo image file in the repo (relative to root).
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -196,6 +210,7 @@ This is the actual flow. There is NO other way to publish:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Key points:**
|
**Key points:**
|
||||||
|
|
||||||
- Steps 1-9 happen in your GitHub Actions workflow
|
- Steps 1-9 happen in your GitHub Actions workflow
|
||||||
- Step 10 is entirely controlled by ArtifactHub — you cannot trigger it
|
- Step 10 is entirely controlled by ArtifactHub — you cannot trigger it
|
||||||
- The tarball lives on GitHub Releases, not ArtifactHub
|
- The tarball lives on GitHub Releases, not ArtifactHub
|
||||||
@@ -233,6 +248,7 @@ The `<pkgname>` directory inside the tarball matches the `name` field from `pack
|
|||||||
## Validating Metadata
|
## Validating Metadata
|
||||||
|
|
||||||
Before committing, check:
|
Before committing, check:
|
||||||
|
|
||||||
1. `version` matches across `package.json` and `artifacthub-pkg.yml`
|
1. `version` matches across `package.json` and `artifacthub-pkg.yml`
|
||||||
2. `archive-url` version tag matches the `version` field
|
2. `archive-url` version tag matches the `version` field
|
||||||
3. `name` in `artifacthub-pkg.yml` matches `package.json` `name`
|
3. `name` in `artifacthub-pkg.yml` matches `package.json` `name`
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ class KubeObject<T extends KubeObjectInterface> {
|
|||||||
### ResourceClasses
|
### ResourceClasses
|
||||||
|
|
||||||
All standard K8s resource types available (Secret, Namespace, Pod, etc.):
|
All standard K8s resource types available (Secret, Namespace, Pod, etc.):
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const [secrets, error, loading] = K8s.ResourceClasses.Secret.useList({ namespace: 'default' });
|
const [secrets, error, loading] = K8s.ResourceClasses.Secret.useList({ namespace: 'default' });
|
||||||
const [secret, error] = K8s.ResourceClasses.Secret.useGet('my-secret', 'default');
|
const [secret, error] = K8s.ResourceClasses.Secret.useGet('my-secret', 'default');
|
||||||
@@ -127,6 +128,7 @@ ApiProxy.apiFactory(group, version, resource): ApiClient
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Service proxy URL** (accessing in-cluster services):
|
**Service proxy URL** (accessing in-cluster services):
|
||||||
|
|
||||||
```
|
```
|
||||||
/api/v1/namespaces/${ns}/services/http:${name}:${port}/proxy${path}
|
/api/v1/namespaces/${ns}/services/http:${name}:${port}/proxy${path}
|
||||||
```
|
```
|
||||||
@@ -146,6 +148,7 @@ From `@kinvolk/headlamp-plugin/lib/CommonComponents`:
|
|||||||
`PercentageBar` — bar chart with `data` array of `{ name, value, fill }`
|
`PercentageBar` — bar chart with `data` array of `{ name, value, fill }`
|
||||||
|
|
||||||
### SimpleTable (non-obvious props)
|
### SimpleTable (non-obvious props)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
<SimpleTable
|
<SimpleTable
|
||||||
data={items}
|
data={items}
|
||||||
@@ -158,6 +161,7 @@ From `@kinvolk/headlamp-plugin/lib/CommonComponents`:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### NameValueTable (non-obvious props)
|
### NameValueTable (non-obvious props)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
<NameValueTable
|
<NameValueTable
|
||||||
rows={[
|
rows={[
|
||||||
@@ -168,6 +172,7 @@ From `@kinvolk/headlamp-plugin/lib/CommonComponents`:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### ConfigStore
|
### ConfigStore
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { ConfigStore } from '@kinvolk/headlamp-plugin/lib';
|
import { ConfigStore } from '@kinvolk/headlamp-plugin/lib';
|
||||||
const store = new ConfigStore<MyConfig>('plugin-name');
|
const store = new ConfigStore<MyConfig>('plugin-name');
|
||||||
@@ -177,6 +182,7 @@ store.useConfig(): () => MyConfig;
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Pre-bundled (no package.json entry needed)
|
### Pre-bundled (no package.json entry needed)
|
||||||
|
|
||||||
react, react-dom, react-router-dom, @iconify/react, react-redux, @material-ui/core, @material-ui/styles, lodash, notistack, recharts, monaco-editor
|
react, react-dom, react-router-dom, @iconify/react, react-redux, @material-ui/core, @material-ui/styles, lodash, notistack, recharts, monaco-editor
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -264,6 +270,7 @@ vi.mock('@kinvolk/headlamp-plugin/lib/CommonComponents', () => ({
|
|||||||
Headlamp supports light and dark themes. **Never hardcode colors.** Use CSS custom properties with light-mode fallbacks:
|
Headlamp supports light and dark themes. **Never hardcode colors.** Use CSS custom properties with light-mode fallbacks:
|
||||||
|
|
||||||
### Required CSS variables for inline styles
|
### Required CSS variables for inline styles
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Text
|
// Text
|
||||||
color: 'var(--mui-palette-text-primary)'
|
color: 'var(--mui-palette-text-primary)'
|
||||||
@@ -289,6 +296,7 @@ color: 'var(--link-color, #1976d2)'
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Common mistakes to avoid
|
### Common mistakes to avoid
|
||||||
|
|
||||||
- **NEVER** use raw `#fff`, `#000`, `#333`, `#666` etc. without wrapping in `var(--mui-palette-*)`
|
- **NEVER** use raw `#fff`, `#000`, `#333`, `#666` etc. without wrapping in `var(--mui-palette-*)`
|
||||||
- **NEVER** use `rgba(0,0,0,0.5)` for overlays without a variable — this is the one exception where raw rgba is acceptable (backdrop overlays)
|
- **NEVER** use `rgba(0,0,0,0.5)` for overlays without a variable — this is the one exception where raw rgba is acceptable (backdrop overlays)
|
||||||
- **NEVER** assume white backgrounds or dark text — always use `background-paper`/`text-primary`
|
- **NEVER** assume white backgrounds or dark text — always use `background-paper`/`text-primary`
|
||||||
@@ -296,6 +304,7 @@ color: 'var(--link-color, #1976d2)'
|
|||||||
- Fallback values after the comma are for environments where the variable isn't set — always use the light-mode default
|
- Fallback values after the comma are for environments where the variable isn't set — always use the light-mode default
|
||||||
|
|
||||||
### Form inputs in custom components
|
### Form inputs in custom components
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const inputStyle = {
|
const inputStyle = {
|
||||||
border: '1px solid var(--mui-palette-divider, #ccc)',
|
border: '1px solid var(--mui-palette-divider, #ccc)',
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ model: opus
|
|||||||
You are a senior multi-agent coordinator with expertise in orchestrating complex distributed workflows. Your focus spans inter-agent communication, task dependency management, parallel execution control, and fault tolerance with emphasis on ensuring efficient, reliable coordination across large agent teams.
|
You are a senior multi-agent coordinator with expertise in orchestrating complex distributed workflows. Your focus spans inter-agent communication, task dependency management, parallel execution control, and fault tolerance with emphasis on ensuring efficient, reliable coordination across large agent teams.
|
||||||
|
|
||||||
When invoked:
|
When invoked:
|
||||||
|
|
||||||
1. Query context manager for workflow requirements and agent states
|
1. Query context manager for workflow requirements and agent states
|
||||||
2. Review communication patterns, dependencies, and resource constraints
|
2. Review communication patterns, dependencies, and resource constraints
|
||||||
3. Analyze coordination bottlenecks, deadlock risks, and optimization opportunities
|
3. Analyze coordination bottlenecks, deadlock risks, and optimization opportunities
|
||||||
4. Implement robust multi-agent coordination strategies
|
4. Implement robust multi-agent coordination strategies
|
||||||
|
|
||||||
Multi-agent coordination checklist:
|
Multi-agent coordination checklist:
|
||||||
|
|
||||||
- Coordination overhead < 5% maintained
|
- Coordination overhead < 5% maintained
|
||||||
- Deadlock prevention 100% ensured
|
- Deadlock prevention 100% ensured
|
||||||
- Message delivery guaranteed thoroughly
|
- Message delivery guaranteed thoroughly
|
||||||
@@ -24,6 +26,7 @@ Multi-agent coordination checklist:
|
|||||||
- Performance optimal consistently
|
- Performance optimal consistently
|
||||||
|
|
||||||
Workflow orchestration:
|
Workflow orchestration:
|
||||||
|
|
||||||
- Process design
|
- Process design
|
||||||
- Flow control
|
- Flow control
|
||||||
- State management
|
- State management
|
||||||
@@ -34,6 +37,7 @@ Workflow orchestration:
|
|||||||
- Result aggregation
|
- Result aggregation
|
||||||
|
|
||||||
Inter-agent communication:
|
Inter-agent communication:
|
||||||
|
|
||||||
- Protocol design
|
- Protocol design
|
||||||
- Message routing
|
- Message routing
|
||||||
- Channel management
|
- Channel management
|
||||||
@@ -44,6 +48,7 @@ Inter-agent communication:
|
|||||||
- Backpressure handling
|
- Backpressure handling
|
||||||
|
|
||||||
Dependency management:
|
Dependency management:
|
||||||
|
|
||||||
- Dependency graphs
|
- Dependency graphs
|
||||||
- Topological sorting
|
- Topological sorting
|
||||||
- Circular detection
|
- Circular detection
|
||||||
@@ -54,6 +59,7 @@ Dependency management:
|
|||||||
- Race condition handling
|
- Race condition handling
|
||||||
|
|
||||||
Coordination patterns:
|
Coordination patterns:
|
||||||
|
|
||||||
- Master-worker
|
- Master-worker
|
||||||
- Peer-to-peer
|
- Peer-to-peer
|
||||||
- Hierarchical
|
- Hierarchical
|
||||||
@@ -64,6 +70,7 @@ Coordination patterns:
|
|||||||
- Consensus-based
|
- Consensus-based
|
||||||
|
|
||||||
Parallel execution:
|
Parallel execution:
|
||||||
|
|
||||||
- Task partitioning
|
- Task partitioning
|
||||||
- Work distribution
|
- Work distribution
|
||||||
- Load balancing
|
- Load balancing
|
||||||
@@ -74,6 +81,7 @@ Parallel execution:
|
|||||||
- Result merging
|
- Result merging
|
||||||
|
|
||||||
Communication mechanisms:
|
Communication mechanisms:
|
||||||
|
|
||||||
- Message passing
|
- Message passing
|
||||||
- Shared memory
|
- Shared memory
|
||||||
- Event streams
|
- Event streams
|
||||||
@@ -84,6 +92,7 @@ Communication mechanisms:
|
|||||||
- Queue systems
|
- Queue systems
|
||||||
|
|
||||||
Resource coordination:
|
Resource coordination:
|
||||||
|
|
||||||
- Resource allocation
|
- Resource allocation
|
||||||
- Lock management
|
- Lock management
|
||||||
- Semaphore control
|
- Semaphore control
|
||||||
@@ -94,6 +103,7 @@ Resource coordination:
|
|||||||
- Efficiency optimization
|
- Efficiency optimization
|
||||||
|
|
||||||
Fault tolerance:
|
Fault tolerance:
|
||||||
|
|
||||||
- Failure detection
|
- Failure detection
|
||||||
- Timeout handling
|
- Timeout handling
|
||||||
- Retry mechanisms
|
- Retry mechanisms
|
||||||
@@ -104,6 +114,7 @@ Fault tolerance:
|
|||||||
- Graceful degradation
|
- Graceful degradation
|
||||||
|
|
||||||
Workflow management:
|
Workflow management:
|
||||||
|
|
||||||
- DAG execution
|
- DAG execution
|
||||||
- State machines
|
- State machines
|
||||||
- Saga patterns
|
- Saga patterns
|
||||||
@@ -114,6 +125,7 @@ Workflow management:
|
|||||||
- Loop handling
|
- Loop handling
|
||||||
|
|
||||||
Performance optimization:
|
Performance optimization:
|
||||||
|
|
||||||
- Bottleneck analysis
|
- Bottleneck analysis
|
||||||
- Pipeline optimization
|
- Pipeline optimization
|
||||||
- Batch processing
|
- Batch processing
|
||||||
@@ -130,6 +142,7 @@ Performance optimization:
|
|||||||
Initialize multi-agent coordination by understanding workflow needs.
|
Initialize multi-agent coordination by understanding workflow needs.
|
||||||
|
|
||||||
Coordination context query:
|
Coordination context query:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"requesting_agent": "multi-agent-coordinator",
|
"requesting_agent": "multi-agent-coordinator",
|
||||||
@@ -149,6 +162,7 @@ Execute multi-agent coordination through systematic phases:
|
|||||||
Design efficient coordination strategies.
|
Design efficient coordination strategies.
|
||||||
|
|
||||||
Analysis priorities:
|
Analysis priorities:
|
||||||
|
|
||||||
- Workflow mapping
|
- Workflow mapping
|
||||||
- Agent capabilities
|
- Agent capabilities
|
||||||
- Communication needs
|
- Communication needs
|
||||||
@@ -159,6 +173,7 @@ Analysis priorities:
|
|||||||
- Optimization opportunities
|
- Optimization opportunities
|
||||||
|
|
||||||
Workflow evaluation:
|
Workflow evaluation:
|
||||||
|
|
||||||
- Map processes
|
- Map processes
|
||||||
- Identify dependencies
|
- Identify dependencies
|
||||||
- Analyze communication
|
- Analyze communication
|
||||||
@@ -173,6 +188,7 @@ Workflow evaluation:
|
|||||||
Orchestrate complex multi-agent workflows.
|
Orchestrate complex multi-agent workflows.
|
||||||
|
|
||||||
Implementation approach:
|
Implementation approach:
|
||||||
|
|
||||||
- Setup communication
|
- Setup communication
|
||||||
- Configure workflows
|
- Configure workflows
|
||||||
- Manage dependencies
|
- Manage dependencies
|
||||||
@@ -183,6 +199,7 @@ Implementation approach:
|
|||||||
- Optimize performance
|
- Optimize performance
|
||||||
|
|
||||||
Coordination patterns:
|
Coordination patterns:
|
||||||
|
|
||||||
- Efficient messaging
|
- Efficient messaging
|
||||||
- Clear dependencies
|
- Clear dependencies
|
||||||
- Parallel execution
|
- Parallel execution
|
||||||
@@ -193,6 +210,7 @@ Coordination patterns:
|
|||||||
- Continuous optimization
|
- Continuous optimization
|
||||||
|
|
||||||
Progress tracking:
|
Progress tracking:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"agent": "multi-agent-coordinator",
|
"agent": "multi-agent-coordinator",
|
||||||
@@ -211,6 +229,7 @@ Progress tracking:
|
|||||||
Achieve seamless multi-agent collaboration.
|
Achieve seamless multi-agent collaboration.
|
||||||
|
|
||||||
Excellence checklist:
|
Excellence checklist:
|
||||||
|
|
||||||
- Workflows smooth
|
- Workflows smooth
|
||||||
- Communication efficient
|
- Communication efficient
|
||||||
- Dependencies resolved
|
- Dependencies resolved
|
||||||
@@ -224,6 +243,7 @@ Delivery notification:
|
|||||||
"Multi-agent coordination completed. Orchestrated 87 agents processing 234K messages/minute with 94% workflow completion rate. Achieved 96% coordination efficiency with zero deadlocks and 99.9% message delivery guarantee."
|
"Multi-agent coordination completed. Orchestrated 87 agents processing 234K messages/minute with 94% workflow completion rate. Achieved 96% coordination efficiency with zero deadlocks and 99.9% message delivery guarantee."
|
||||||
|
|
||||||
Communication optimization:
|
Communication optimization:
|
||||||
|
|
||||||
- Protocol efficiency
|
- Protocol efficiency
|
||||||
- Message batching
|
- Message batching
|
||||||
- Compression strategies
|
- Compression strategies
|
||||||
@@ -234,6 +254,7 @@ Communication optimization:
|
|||||||
- Queue management
|
- Queue management
|
||||||
|
|
||||||
Dependency resolution:
|
Dependency resolution:
|
||||||
|
|
||||||
- Graph algorithms
|
- Graph algorithms
|
||||||
- Priority scheduling
|
- Priority scheduling
|
||||||
- Resource allocation
|
- Resource allocation
|
||||||
@@ -244,6 +265,7 @@ Dependency resolution:
|
|||||||
- Bottleneck removal
|
- Bottleneck removal
|
||||||
|
|
||||||
Fault handling:
|
Fault handling:
|
||||||
|
|
||||||
- Failure detection
|
- Failure detection
|
||||||
- Isolation strategies
|
- Isolation strategies
|
||||||
- Recovery procedures
|
- Recovery procedures
|
||||||
@@ -254,6 +276,7 @@ Fault handling:
|
|||||||
- Graceful degradation
|
- Graceful degradation
|
||||||
|
|
||||||
Scalability patterns:
|
Scalability patterns:
|
||||||
|
|
||||||
- Horizontal scaling
|
- Horizontal scaling
|
||||||
- Vertical partitioning
|
- Vertical partitioning
|
||||||
- Load distribution
|
- Load distribution
|
||||||
@@ -264,6 +287,7 @@ Scalability patterns:
|
|||||||
- Cluster coordination
|
- Cluster coordination
|
||||||
|
|
||||||
Performance tuning:
|
Performance tuning:
|
||||||
|
|
||||||
- Latency analysis
|
- Latency analysis
|
||||||
- Throughput optimization
|
- Throughput optimization
|
||||||
- Resource utilization
|
- Resource utilization
|
||||||
@@ -274,6 +298,7 @@ Performance tuning:
|
|||||||
- I/O optimization
|
- I/O optimization
|
||||||
|
|
||||||
Integration with other agents:
|
Integration with other agents:
|
||||||
|
|
||||||
- Collaborate with agent-organizer on team assembly
|
- Collaborate with agent-organizer on team assembly
|
||||||
- Support context-manager on state synchronization
|
- Support context-manager on state synchronization
|
||||||
- Work with workflow-orchestrator on process execution
|
- Work with workflow-orchestrator on process execution
|
||||||
|
|||||||
+4
-202
@@ -2,210 +2,12 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['**']
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main, dev, uat]
|
branches: [main]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
workflow_call:
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
runs-on: ubuntu-latest
|
uses: privilegedescalation/.github/.github/workflows/plugin-ci.yaml@main
|
||||||
timeout-minutes: 10
|
|
||||||
container: node:22-slim
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Install Python
|
|
||||||
run: apt-get update && apt-get install -y --no-install-recommends python3 python3-yaml
|
|
||||||
|
|
||||||
- name: Validate artifacthub-pkg.yml
|
|
||||||
run: |
|
|
||||||
python3 - <<'EOF'
|
|
||||||
import sys, re
|
|
||||||
try:
|
|
||||||
import yaml
|
|
||||||
except ImportError:
|
|
||||||
print("::warning::PyYAML not available, skipping artifacthub-pkg.yml validation")
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open("artifacthub-pkg.yml") as f:
|
|
||||||
pkg = yaml.safe_load(f)
|
|
||||||
except FileNotFoundError:
|
|
||||||
print("::error::artifacthub-pkg.yml not found")
|
|
||||||
sys.exit(1)
|
|
||||||
except yaml.YAMLError as e:
|
|
||||||
print(f"::error::artifacthub-pkg.yml is invalid YAML: {e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
errors = []
|
|
||||||
|
|
||||||
for field in ["version", "name", "description", "homeURL"]:
|
|
||||||
if not pkg.get(field):
|
|
||||||
errors.append(f"Missing required field: {field}")
|
|
||||||
|
|
||||||
version = pkg.get("version", "")
|
|
||||||
if version and not re.match(r'^\d+\.\d+\.\d+$', str(version)):
|
|
||||||
errors.append(f"version '{version}' is not SemVer (expected X.Y.Z)")
|
|
||||||
|
|
||||||
annotations = pkg.get("annotations", {}) or {}
|
|
||||||
archive_url = annotations.get("headlamp/plugin/archive-url", "")
|
|
||||||
archive_checksum = annotations.get("headlamp/plugin/archive-checksum", "")
|
|
||||||
|
|
||||||
if not archive_url:
|
|
||||||
errors.append("Missing annotation: headlamp/plugin/archive-url")
|
|
||||||
if not archive_checksum:
|
|
||||||
errors.append("Missing annotation: headlamp/plugin/archive-checksum")
|
|
||||||
elif not re.match(r'^sha256:[0-9a-f]{64}$', str(archive_checksum)):
|
|
||||||
errors.append(f"archive-checksum has unexpected format: '{archive_checksum}' (expected sha256:<64 hex chars>)")
|
|
||||||
|
|
||||||
if errors:
|
|
||||||
for e in errors:
|
|
||||||
print(f"::error::{e}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
print(f"artifacthub-pkg.yml valid: name={pkg['name']} version={pkg['version']}")
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Detect package manager
|
|
||||||
id: pkg-manager
|
|
||||||
run: |
|
|
||||||
if [ -f "pnpm-lock.yaml" ]; then
|
|
||||||
echo "manager=pnpm" >> $GITHUB_OUTPUT
|
|
||||||
PM=$(python3 -c "import json,sys; d=json.load(open('package.json')); print('true' if d.get('packageManager','').startswith('pnpm@') else 'false')" 2>/dev/null || echo "false")
|
|
||||||
echo "has_package_manager=$PM" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "manager=npm" >> $GITHUB_OUTPUT
|
|
||||||
echo "has_package_manager=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@v6
|
|
||||||
with:
|
|
||||||
node-version: '22'
|
|
||||||
cache: ${{ steps.pkg-manager.outputs.manager == 'npm' && 'npm' || '' }}
|
|
||||||
|
|
||||||
- name: Setup pnpm (via Corepack, reads version from packageManager field)
|
|
||||||
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'true'
|
|
||||||
run: |
|
|
||||||
npm install -g corepack
|
|
||||||
corepack enable pnpm
|
|
||||||
corepack install
|
|
||||||
|
|
||||||
- name: Setup pnpm (version latest)
|
|
||||||
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'false'
|
|
||||||
uses: pnpm/action-setup@v5
|
|
||||||
with:
|
|
||||||
run_install: false
|
|
||||||
version: latest
|
|
||||||
|
|
||||||
- name: Get pnpm store directory
|
|
||||||
id: pnpm-store
|
|
||||||
if: steps.pkg-manager.outputs.manager == 'pnpm'
|
|
||||||
run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Cache pnpm store
|
|
||||||
if: steps.pkg-manager.outputs.manager == 'pnpm'
|
|
||||||
uses: actions/cache@v5
|
|
||||||
with:
|
|
||||||
path: ${{ steps.pnpm-store.outputs.dir }}
|
|
||||||
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pnpm-
|
|
||||||
|
|
||||||
- name: Validate pnpm lockfile freshness
|
|
||||||
if: steps.pkg-manager.outputs.manager == 'pnpm'
|
|
||||||
run: |
|
|
||||||
if [ ! -f "pnpm-lock.yaml" ]; then
|
|
||||||
echo "No pnpm-lock.yaml found, skipping lockfile freshness check"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
if ! grep -q 'overrides:' pnpm-lock.yaml 2>/dev/null; then
|
|
||||||
echo "No overrides section in pnpm-lock.yaml, skipping lockfile freshness check"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "Detected pnpm-lock.yaml with overrides section. Checking lockfile freshness..."
|
|
||||||
ERR_FILE=$(mktemp)
|
|
||||||
if pnpm install --frozen-lockfile 2>&1 | tee "$ERR_FILE"; then
|
|
||||||
echo "Lockfile is fresh."
|
|
||||||
else
|
|
||||||
if grep -q "CONFIG_MISMATCH\|EBADLOCKFILE\|ERR_PNPM_LOCKFILE" "$ERR_FILE"; then
|
|
||||||
echo ""
|
|
||||||
echo "::error::pnpm-lock.yaml is out of sync with package.json overrides."
|
|
||||||
echo "::error::Run 'pnpm install' to regenerate the lockfile and commit the updated pnpm-lock.yaml."
|
|
||||||
rm -f "$ERR_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
rm -f "$ERR_FILE"
|
|
||||||
echo "::warning::Install failed with a different error. Will retry in the Install dependencies step."
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
max_attempts=3
|
|
||||||
attempt=1
|
|
||||||
while [ $attempt -le $max_attempts ]; do
|
|
||||||
echo "Attempt $attempt of $max_attempts"
|
|
||||||
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
||||||
pnpm install --frozen-lockfile && break
|
|
||||||
else
|
|
||||||
npm ci && break
|
|
||||||
fi
|
|
||||||
if [ $attempt -lt $max_attempts ]; then
|
|
||||||
echo "::warning::Install step failed on attempt $attempt. Retrying in 5 seconds..."
|
|
||||||
sleep 5
|
|
||||||
fi
|
|
||||||
attempt=$((attempt + 1))
|
|
||||||
done
|
|
||||||
if [ $attempt -gt $max_attempts ]; then
|
|
||||||
echo "::error::Install step failed after $max_attempts attempts."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build plugin
|
|
||||||
run: npx @kinvolk/headlamp-plugin build
|
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
run: |
|
|
||||||
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
||||||
pnpm run lint
|
|
||||||
else
|
|
||||||
npm run lint
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Type-check
|
|
||||||
run: |
|
|
||||||
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
||||||
pnpm run tsc
|
|
||||||
else
|
|
||||||
npm run tsc
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Format check
|
|
||||||
run: |
|
|
||||||
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
||||||
pnpm run format:check
|
|
||||||
else
|
|
||||||
npm run format:check
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: |
|
|
||||||
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
||||||
pnpm test
|
|
||||||
else
|
|
||||||
npm test
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Security audit
|
|
||||||
run: |
|
|
||||||
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
|
|
||||||
npx audit-ci --pnpm --audit-level=high --config ./audit-ci.jsonc
|
|
||||||
else
|
|
||||||
npx audit-ci --npm --audit-level=high --config ./audit-ci.jsonc
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -1,112 +1,20 @@
|
|||||||
name: Promotion Gate
|
name: Dual Approval (CTO + QA)
|
||||||
|
|
||||||
# dev PRs: no gate (engineer self-merges).
|
# Calls the shared dual-approval-check workflow.
|
||||||
# uat PRs: QA approval required.
|
# Passes when both privilegedescalation-cto and privilegedescalation-qa
|
||||||
# main PRs: UAT approval required (uat→main promotions).
|
# have approved the PR. Add "Dual Approval (CTO + QA)" to required_status_checks
|
||||||
|
# in branch protection to enforce this gate.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_review:
|
pull_request_review:
|
||||||
types: [submitted, dismissed]
|
types: [submitted, dismissed]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [uat, main]
|
branches: [main]
|
||||||
types: [opened, reopened, synchronize]
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
promotion-gate:
|
dual-approval:
|
||||||
name: Promotion Gate
|
uses: privilegedescalation/.github/.github/workflows/dual-approval-check.yaml@main
|
||||||
runs-on: ubuntu-latest
|
secrets: inherit
|
||||||
timeout-minutes: 5
|
with:
|
||||||
|
pr_number: ${{ github.event.pull_request.number }}
|
||||||
steps:
|
|
||||||
- name: Check promotion approval
|
|
||||||
env:
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
BASE_REF: ${{ github.base_ref }}
|
|
||||||
run: |
|
|
||||||
if [ -z "${PR_NUMBER}" ] || [ "${PR_NUMBER}" = "null" ]; then
|
|
||||||
echo "::notice::No PR number in context. Skipping promotion gate."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Checking promotion gate for PR #${PR_NUMBER} targeting ${BASE_REF} in ${REPO}"
|
|
||||||
|
|
||||||
if [ -z "${BASE_REF}" ] && [ -n "${PR_NUMBER}" ] && [ "${PR_NUMBER}" != "null" ]; then
|
|
||||||
BASE_REF=$(curl -sf \
|
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
||||||
-H "Accept: application/json" \
|
|
||||||
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}" | jq -r '.base.ref')
|
|
||||||
echo "BASE_REF was empty; resolved from PR #${PR_NUMBER} API: ${BASE_REF}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine required reviewer based on target branch
|
|
||||||
case "${BASE_REF}" in
|
|
||||||
dev)
|
|
||||||
echo "Target is dev — no review required. Engineers self-merge."
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
uat)
|
|
||||||
REQUIRED_REVIEWER="pe_regina"
|
|
||||||
GATE_NAME="QA"
|
|
||||||
;;
|
|
||||||
main)
|
|
||||||
REQUIRED_REVIEWER="pe_regina"
|
|
||||||
GATE_NAME="QA"
|
|
||||||
# For plugin repos (Pipeline A), UAT approval is needed for uat→main
|
|
||||||
# Check if the source branch is uat
|
|
||||||
SOURCE_REF=$(curl -sf \
|
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
||||||
-H "Accept: application/json" \
|
|
||||||
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}" | jq -r '.head.ref')
|
|
||||||
|
|
||||||
if [ "${SOURCE_REF}" = "uat" ]; then
|
|
||||||
REQUIRED_REVIEWER="pe_patty"
|
|
||||||
GATE_NAME="UAT"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "::notice::Target branch '${BASE_REF}' has no promotion gate configured."
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "Required reviewer: ${REQUIRED_REVIEWER} (${GATE_NAME})"
|
|
||||||
|
|
||||||
# For uat→main promotions, pe_patty may not be able to review (bot account).
|
|
||||||
# Accept pe_nancy (CTO) as a valid alternative reviewer.
|
|
||||||
ALT_REVIEWER=""
|
|
||||||
if [ "${REQUIRED_REVIEWER}" = "pe_patty" ]; then
|
|
||||||
ALT_REVIEWER="pe_nancy"
|
|
||||||
fi
|
|
||||||
|
|
||||||
REVIEWS=$(curl -sf \
|
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
||||||
-H "Accept: application/json" \
|
|
||||||
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}/reviews")
|
|
||||||
|
|
||||||
if [ -z "${REVIEWS}" ] || [ "${REVIEWS}" = "null" ]; then
|
|
||||||
echo "::warning::Could not fetch reviews for PR #${PR_NUMBER}."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
REVIEWER_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${REQUIRED_REVIEWER}" \
|
|
||||||
'[.[] | select(.user.login == $user)] | last | if .state then .state == "APPROVED" else false end')
|
|
||||||
|
|
||||||
echo "${GATE_NAME} (${REQUIRED_REVIEWER}) approved: ${REVIEWER_APPROVED}"
|
|
||||||
|
|
||||||
# Fallback: check if CTO approved as alternative for uat→main
|
|
||||||
if [ "${REVIEWER_APPROVED}" != "true" ] && [ -n "${ALT_REVIEWER}" ]; then
|
|
||||||
REVIEWER_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${ALT_REVIEWER}" \
|
|
||||||
'[.[] | select(.user.login == $user)] | last | if .state then .state == "APPROVED" else false end')
|
|
||||||
if [ "${REVIEWER_APPROVED}" = "true" ]; then
|
|
||||||
echo "CTO (${ALT_REVIEWER}) approved as fallback for UAT gate."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${REVIEWER_APPROVED}" = "true" ]; then
|
|
||||||
echo "Promotion gate passed: ${GATE_NAME} has approved."
|
|
||||||
else
|
|
||||||
echo "Promotion gate failed: waiting for ${GATE_NAME} approval from ${REQUIRED_REVIEWER}."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
name: E2E Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
# Only one E2E run at a time: the shared E2E_RELEASE (headlamp-e2e) in
|
||||||
|
# privilegedescalation-dev cannot be shared across concurrent runs.
|
||||||
|
# cancel-in-progress: false (queue, don't cancel) — cancelling in-flight
|
||||||
|
# runs may skip the if: always() teardown, leaving dangling cluster resources.
|
||||||
|
concurrency:
|
||||||
|
group: e2e-${{ github.repository }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
E2E_NAMESPACE: privilegedescalation-dev
|
||||||
|
E2E_RELEASE: headlamp-e2e
|
||||||
|
# Pin to a known-good Headlamp version. Using :latest is risky because
|
||||||
|
# the tag can change between CI runs, causing flaky failures when a newer
|
||||||
|
# image is pulled on some nodes but not others (IfNotPresent pull policy).
|
||||||
|
# Update this when Headlamp is upgraded in production (kube-system).
|
||||||
|
HEADLAMP_VERSION: v0.40.1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
e2e:
|
||||||
|
runs-on: runners-privilegedescalation
|
||||||
|
timeout-minutes: 15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Setup kubectl
|
||||||
|
uses: azure/setup-kubectl@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build plugin
|
||||||
|
run: npx @kinvolk/headlamp-plugin build
|
||||||
|
|
||||||
|
- name: Deploy E2E Headlamp instance
|
||||||
|
run: scripts/deploy-e2e-headlamp.sh
|
||||||
|
|
||||||
|
- name: Load E2E environment
|
||||||
|
run: |
|
||||||
|
if [ -f .env.e2e ]; then
|
||||||
|
cat .env.e2e >> "$GITHUB_ENV"
|
||||||
|
else
|
||||||
|
echo "::error::deploy-e2e-headlamp.sh did not produce .env.e2e"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Install Playwright browsers
|
||||||
|
run: npx playwright install --with-deps chromium
|
||||||
|
|
||||||
|
- name: Run E2E tests
|
||||||
|
run: npm run e2e
|
||||||
|
env:
|
||||||
|
HEADLAMP_URL: ${{ env.HEADLAMP_URL }}
|
||||||
|
HEADLAMP_TOKEN: ${{ env.HEADLAMP_TOKEN }}
|
||||||
|
|
||||||
|
- name: Collect deployment diagnostics on failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
echo "=== Pod state ==="
|
||||||
|
kubectl get pods -n "$E2E_NAMESPACE" -l "app.kubernetes.io/instance=$E2E_RELEASE" 2>&1 || true
|
||||||
|
echo "=== Pod describe ==="
|
||||||
|
kubectl describe pods -n "$E2E_NAMESPACE" -l "app.kubernetes.io/instance=$E2E_RELEASE" 2>&1 || true
|
||||||
|
echo "=== Recent namespace events ==="
|
||||||
|
kubectl get events -n "$E2E_NAMESPACE" --sort-by='.lastTimestamp' 2>&1 | tail -20 || true
|
||||||
|
|
||||||
|
- name: Teardown E2E instance
|
||||||
|
if: always()
|
||||||
|
run: scripts/teardown-e2e-headlamp.sh
|
||||||
|
|
||||||
|
- name: Upload Playwright report
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: Upload test results
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: test-results
|
||||||
|
path: test-results/
|
||||||
|
retention-days: 7
|
||||||
@@ -4,80 +4,20 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
description: 'Release version (e.g. 1.0.1)'
|
description: 'Release version (e.g. 1.0.0)'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
uses: privilegedescalation/.github/.github/workflows/plugin-release.yaml@main
|
||||||
|
secrets:
|
||||||
steps:
|
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
|
||||||
- name: Checkout
|
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
uses: actions/checkout@v4
|
with:
|
||||||
|
version: ${{ inputs.version }}
|
||||||
- name: Setup Node
|
upstream-repo: 'FairwindsOps/polaris'
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
run: npm install -g pnpm
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: pnpm run build
|
|
||||||
|
|
||||||
- name: Get tarball path
|
|
||||||
id: tarball
|
|
||||||
run: |
|
|
||||||
# headlamp-plugin package outputs the tarball path, e.g.:
|
|
||||||
# "Packaged: /path/to/headlamp-polaris-1.0.0.tar.gz"
|
|
||||||
output=$(pnpm run package 2>&1)
|
|
||||||
echo "output=$output"
|
|
||||||
# Extract tarball name, e.g. headlamp-polaris-1.0.0.tar.gz
|
|
||||||
tarball_name=$(echo "$output" | grep -oP 'headlamp-polaris-\d+\.\d+\.\d+\.tar\.gz' | tail -1)
|
|
||||||
echo "tarball_name=$tarball_name" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Create Gitea Release
|
|
||||||
env:
|
|
||||||
GITEA_URL: https://git.farh.net
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
REPO: privilegedescalation/headlamp-polaris-plugin
|
|
||||||
run: |
|
|
||||||
VERSION="${{ inputs.version }}"
|
|
||||||
ASSET_NAME="headlamp-polaris-${VERSION}.tar.gz"
|
|
||||||
|
|
||||||
# Create the release via Gitea API
|
|
||||||
RELEASE_RESPONSE=$(
|
|
||||||
curl -s -X POST \
|
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
"${GITEA_URL}/api/v1/repos/${REPO}/releases" \
|
|
||||||
-d "{
|
|
||||||
\"tag_name\": \"v${VERSION}\",
|
|
||||||
\"name\": \"v${VERSION}\",
|
|
||||||
\"draft\": false,
|
|
||||||
\"prerelease\": false
|
|
||||||
}"
|
|
||||||
)
|
|
||||||
echo "Release response: ${RELEASE_RESPONSE}"
|
|
||||||
|
|
||||||
RELEASE_ID=$(echo "${RELEASE_RESPONSE}" | python3 -c "import sys, json; print(json.load(sys.stdin).get('id', ''))")
|
|
||||||
if [ -z "$RELEASE_ID" ]; then
|
|
||||||
echo "Failed to create release"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Upload the tarball asset
|
|
||||||
curl -s -X POST \
|
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
||||||
-H "Content-Type: application/octet-stream" \
|
|
||||||
-T "${{ steps.tarball.outputs.tarball_name }}" \
|
|
||||||
"${GITEA_URL}/api/v1/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${ASSET_NAME}"
|
|
||||||
|
|||||||
+4
-1
@@ -2,7 +2,10 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
.headlamp-plugin/
|
.headlamp-plugin/
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
e2e/.auth/
|
||||||
|
test-results/
|
||||||
|
.playwright-mcp/
|
||||||
.env
|
.env
|
||||||
|
.env.e2e
|
||||||
.env.local
|
.env.local
|
||||||
.eslintcache
|
.eslintcache
|
||||||
package-lock.json
|
|
||||||
|
|||||||
@@ -15,39 +15,6 @@
|
|||||||
// Ordered list item prefix — number resets are intentional in documents
|
// Ordered list item prefix — number resets are intentional in documents
|
||||||
"MD029": false,
|
"MD029": false,
|
||||||
// No inline HTML — each elements are valid in valid Markdown
|
// No inline HTML — each elements are valid in valid Markdown
|
||||||
"MD033": false,
|
"MD033": false
|
||||||
// List marker space — spacing after list markers varies by editor
|
}
|
||||||
"MD030": false,
|
|
||||||
// Blanks around headings — not always needed in compact docs
|
|
||||||
"MD022": false,
|
|
||||||
// Blanks around lists — not always needed in compact docs
|
|
||||||
"MD032": false,
|
|
||||||
// Blanks around fences — not always needed between adjacent blocks
|
|
||||||
"MD031": false,
|
|
||||||
// Multiple blanks — editor artifacts, not semantic
|
|
||||||
"MD012": false,
|
|
||||||
// Single title — files may have multiple H1 sections
|
|
||||||
"MD025": false,
|
|
||||||
// Trailing spaces — editor artifacts
|
|
||||||
"MD009": false,
|
|
||||||
// Bare URLs — URL shortening not always needed
|
|
||||||
"MD034": false,
|
|
||||||
// Single trailing newline — editor artifacts
|
|
||||||
"MD047": false,
|
|
||||||
// Trailing punctuation — heading punctuation is intentional
|
|
||||||
"MD026": false,
|
|
||||||
// Space in emphasis — double-asterisk bold spacing varies by renderer
|
|
||||||
"MD037": false,
|
|
||||||
// No hard tabs — some generated docs use tabs for indentation
|
|
||||||
"MD010": false,
|
|
||||||
// Code block style — generated docs may use inconsistent styles
|
|
||||||
"MD046": false,
|
|
||||||
// Comment style — generated docs have no comments
|
|
||||||
"MD048": false,
|
|
||||||
// Commands show output — shell examples intentionally show only commands
|
|
||||||
"MD014": false
|
|
||||||
},
|
|
||||||
"ignores": [
|
|
||||||
"docs/api-reference/generated/**"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
docs/api-reference/generated/**
|
|
||||||
@@ -13,14 +13,17 @@ First stable release. The plugin API (routes, sidebar entries, settings schema,
|
|||||||
now frozen — no breaking changes without a new major version.
|
now frozen — no breaking changes without a new major version.
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
- Patched 8 of 9 npm audit vulnerabilities via `pnpm.overrides` (#92)
|
- Patched 8 of 9 npm audit vulnerabilities via `pnpm.overrides` (#92)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **Dual-approval CI check**: PRs now require approval from both CTO and QA before merging (#98, #76)
|
- **Dual-approval CI check**: PRs now require approval from both CTO and QA before merging (#98, #76)
|
||||||
- **ExemptionManager test suite**: Full coverage of annotation-based exemption flows, exemption creation, and inline feedback (#82)
|
- **ExemptionManager test suite**: Full coverage of annotation-based exemption flows, exemption creation, and inline feedback (#82)
|
||||||
- **RBAC preflight check**: `deploy-e2e-headlamp.sh` now verifies runner RBAC before attempting E2E deploy (#80)
|
- **RBAC preflight check**: `deploy-e2e-headlamp.sh` now verifies runner RBAC before attempting E2E deploy (#80)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **E2E infrastructure overhaul**: Replaced Dockerfile.e2e with ConfigMap volume mount for plugin loading; tests now run in the `privilegedescalation-dev` namespace (#73, #89, #94)
|
- **E2E infrastructure overhaul**: Replaced Dockerfile.e2e with ConfigMap volume mount for plugin loading; tests now run in the `privilegedescalation-dev` namespace (#73, #89, #94)
|
||||||
- **E2E token auth**: Workflow uses GitHub App token auth and handles the `/token` redirect correctly (#97)
|
- **E2E token auth**: Workflow uses GitHub App token auth and handles the `/token` redirect correctly (#97)
|
||||||
- **E2E HTTP readiness**: `deploy-e2e-headlamp.sh` waits for HTTP reachability after rollout before running tests (#104)
|
- **E2E HTTP readiness**: `deploy-e2e-headlamp.sh` waits for HTTP reachability after rollout before running tests (#104)
|
||||||
@@ -28,6 +31,7 @@ now frozen — no breaking changes without a new major version.
|
|||||||
- **Direct devDependencies**: Added `typescript`, `eslint`, `prettier`, and `@headlamp-k8s/eslint-config` as explicit direct devDependencies to prevent phantom-dep failures in clean installs (#95, #102)
|
- **Direct devDependencies**: Added `typescript`, `eslint`, `prettier`, and `@headlamp-k8s/eslint-config` as explicit direct devDependencies to prevent phantom-dep failures in clean installs (#95, #102)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- **pnpm version pinned**: `packageManager` field in `package.json` pins the pnpm version used in CI (#103)
|
- **pnpm version pinned**: `packageManager` field in `package.json` pins the pnpm version used in CI (#103)
|
||||||
- **GitHub Actions SHA pinning**: Renovate `pinDigests` enabled to SHA-pin all GitHub Actions (#105)
|
- **GitHub Actions SHA pinning**: Renovate `pinDigests` enabled to SHA-pin all GitHub Actions (#105)
|
||||||
- **ArtifactHub metadata polish**: Improved `install` instructions and `changes` section formatting (#82)
|
- **ArtifactHub metadata polish**: Improved `install` instructions and `changes` section formatting (#82)
|
||||||
@@ -35,12 +39,14 @@ now frozen — no breaking changes without a new major version.
|
|||||||
## [0.6.0] - 2026-03-04
|
## [0.6.0] - 2026-03-04
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **ExemptionManager apiVersion bug**: `apps` and `batch` resources now correctly use `/apis/{group}/v1/` instead of the broken `/api/v1/` path
|
- **ExemptionManager apiVersion bug**: `apps` and `batch` resources now correctly use `/apis/{group}/v1/` instead of the broken `/api/v1/` path
|
||||||
- **Strict TypeScript**: Replaced `resource: any` in InlineAuditSection with proper `KubeResource` interface
|
- **Strict TypeScript**: Replaced `resource: any` in InlineAuditSection with proper `KubeResource` interface
|
||||||
- **PolarisDataContext test mock**: Added missing `triggerRefresh` to mock, preventing silent `undefined` for `refresh` in context
|
- **PolarisDataContext test mock**: Added missing `triggerRefresh` to mock, preventing silent `undefined` for `refresh` in context
|
||||||
- **DashboardView test**: Fixed `SimpleTable` mock that used `Array<any>` and didn't exercise column getters
|
- **DashboardView test**: Fixed `SimpleTable` mock that used `Array<any>` and didn't exercise column getters
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- **Dark mode / theming**: Replaced all `var(--mui-palette-*)` CSS variables with `useTheme()` + `theme.palette.*` across all components (DashboardView, NamespacesListView, InlineAuditSection, ExemptionManager, PolarisSettings, AppBarScoreBadge)
|
- **Dark mode / theming**: Replaced all `var(--mui-palette-*)` CSS variables with `useTheme()` + `theme.palette.*` across all components (DashboardView, NamespacesListView, InlineAuditSection, ExemptionManager, PolarisSettings, AppBarScoreBadge)
|
||||||
- **Namespace drawer**: Replaced custom `<style>` block + positioned `<div>` with MUI `Drawer` component for proper accessibility (`role="dialog"`, `aria-modal`, Escape key handling via MUI)
|
- **Namespace drawer**: Replaced custom `<style>` block + positioned `<div>` with MUI `Drawer` component for proper accessibility (`role="dialog"`, `aria-modal`, Escape key handling via MUI)
|
||||||
- **AppBarScoreBadge**: Uses `theme.palette.success/warning/error` with proper `contrastText` instead of hardcoded hex colors
|
- **AppBarScoreBadge**: Uses `theme.palette.success/warning/error` with proper `contrastText` instead of hardcoded hex colors
|
||||||
@@ -48,6 +54,7 @@ now frozen — no breaking changes without a new major version.
|
|||||||
- **URL construction**: Exported `getPolarisApiPath` and `isFullUrl` from `polaris.ts`; PolarisSettings now reuses them instead of duplicating logic
|
- **URL construction**: Exported `getPolarisApiPath` and `isFullUrl` from `polaris.ts`; PolarisSettings now reuses them instead of duplicating logic
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **Error boundaries**: All registered components (routes, detail sections, app bar action) wrapped in `PolarisErrorBoundary` for graceful error rendering
|
- **Error boundaries**: All registered components (routes, detail sections, app bar action) wrapped in `PolarisErrorBoundary` for graceful error rendering
|
||||||
- **Tests for InlineAuditSection** (7 tests): loading, unsupported kind, not found, score/summary, failing checks, link, exemption manager
|
- **Tests for InlineAuditSection** (7 tests): loading, unsupported kind, not found, score/summary, failing checks, link, exemption manager
|
||||||
- **Tests for AppBarScoreBadge** (6 tests): loading, no data, score colors, navigation, aria-label
|
- **Tests for AppBarScoreBadge** (6 tests): loading, no data, score colors, navigation, aria-label
|
||||||
@@ -55,6 +62,7 @@ now frozen — no breaking changes without a new major version.
|
|||||||
- **Tests for checkMapping.ts** (11 tests): name/description/category/severity lookups, unknown checks, CHECK_MAPPING structure validation
|
- **Tests for checkMapping.ts** (11 tests): name/description/category/severity lookups, unknown checks, CHECK_MAPPING structure validation
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- **NamespaceDetailView.tsx**: Dead code with no registered route (replaced by drawer in NamespacesListView)
|
- **NamespaceDetailView.tsx**: Dead code with no registered route (replaced by drawer in NamespacesListView)
|
||||||
- **NamespaceDetailView.test.tsx**: Tests for removed component
|
- **NamespaceDetailView.test.tsx**: Tests for removed component
|
||||||
- **MockPolarisProvider in test-utils.tsx**: Unused mock provider (tests use `vi.mock` instead)
|
- **MockPolarisProvider in test-utils.tsx**: Unused mock provider (tests use `vi.mock` instead)
|
||||||
@@ -63,9 +71,11 @@ now frozen — no breaking changes without a new major version.
|
|||||||
## [0.3.5] - 2026-02-12
|
## [0.3.5] - 2026-02-12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed drawer background remaining white in dark mode by using correct CSS variable (`--mui-palette-background-default`)
|
- Fixed drawer background remaining white in dark mode by using correct CSS variable (`--mui-palette-background-default`)
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
- Added comprehensive Priority 2 documentation (ARCHITECTURE.md, DEPLOYMENT.md, SECURITY.md)
|
- Added comprehensive Priority 2 documentation (ARCHITECTURE.md, DEPLOYMENT.md, SECURITY.md)
|
||||||
- Added CONTRIBUTING.md with development workflow, branching strategy, and code style guidelines
|
- Added CONTRIBUTING.md with development workflow, branching strategy, and code style guidelines
|
||||||
- Added complete CHANGELOG.md documenting all releases from v0.0.1 to current
|
- Added complete CHANGELOG.md documenting all releases from v0.0.1 to current
|
||||||
@@ -73,17 +83,20 @@ now frozen — no breaking changes without a new major version.
|
|||||||
## [0.3.4] - 2026-02-12
|
## [0.3.4] - 2026-02-12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Removed all `@mui/material` and `@mui/icons-material` imports causing plugin load failure
|
- Removed all `@mui/material` and `@mui/icons-material` imports causing plugin load failure
|
||||||
- Fixed plugin settings page registration (changed name from 'polaris' to 'headlamp-polaris-plugin')
|
- Fixed plugin settings page registration (changed name from 'polaris' to 'headlamp-polaris-plugin')
|
||||||
- Added dark mode support using MUI CSS variables for proper theme adaptation
|
- Added dark mode support using MUI CSS variables for proper theme adaptation
|
||||||
- Resolved TypeScript compilation errors in plugin registration calls
|
- Resolved TypeScript compilation errors in plugin registration calls
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Replaced all MUI components with standard HTML elements and inline styles
|
- Replaced all MUI components with standard HTML elements and inline styles
|
||||||
- Updated `registerDetailsViewSection` and `registerAppBarAction` to match Headlamp plugin API v0.13.0
|
- Updated `registerDetailsViewSection` and `registerAppBarAction` to match Headlamp plugin API v0.13.0
|
||||||
- App bar badge, settings buttons, and UI elements now use theme-aware CSS variables
|
- App bar badge, settings buttons, and UI elements now use theme-aware CSS variables
|
||||||
|
|
||||||
### Infrastructure
|
### Infrastructure
|
||||||
|
|
||||||
- Added CI workflow for lint, type-check, build, and test
|
- Added CI workflow for lint, type-check, build, and test
|
||||||
- Enhanced E2E testing documentation with comprehensive guides
|
- Enhanced E2E testing documentation with comprehensive guides
|
||||||
- Added documentation-engineer subagent
|
- Added documentation-engineer subagent
|
||||||
@@ -91,24 +104,28 @@ now frozen — no breaking changes without a new major version.
|
|||||||
## [0.3.3] - 2026-02-12
|
## [0.3.3] - 2026-02-12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Corrected plugin settings registration name to match package.json
|
- Corrected plugin settings registration name to match package.json
|
||||||
- Added displaySaveButton parameter to settings registration
|
- Added displaySaveButton parameter to settings registration
|
||||||
|
|
||||||
## [0.3.2] - 2026-02-12
|
## [0.3.2] - 2026-02-12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Removed all MUI dependencies to fix plugin loading in Headlamp v0.39.0+
|
- Removed all MUI dependencies to fix plugin loading in Headlamp v0.39.0+
|
||||||
- Plugin now loads correctly in sidebar and routes
|
- Plugin now loads correctly in sidebar and routes
|
||||||
|
|
||||||
## [0.3.1] - 2026-02-12
|
## [0.3.1] - 2026-02-12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- TypeScript compilation errors in `registerDetailsViewSection` and `registerAppBarAction` calls
|
- TypeScript compilation errors in `registerDetailsViewSection` and `registerAppBarAction` calls
|
||||||
- Test failures in DashboardView (added missing SimpleTable mock)
|
- Test failures in DashboardView (added missing SimpleTable mock)
|
||||||
|
|
||||||
## [0.3.0] - 2026-02-11
|
## [0.3.0] - 2026-02-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- App bar badge displaying cluster Polaris score
|
- App bar badge displaying cluster Polaris score
|
||||||
- Inline audit sections in resource detail views (Deployment, StatefulSet, DaemonSet, Job, CronJob)
|
- Inline audit sections in resource detail views (Deployment, StatefulSet, DaemonSet, Job, CronJob)
|
||||||
- Exemption management UI (view/add exemptions via annotations)
|
- Exemption management UI (view/add exemptions via annotations)
|
||||||
@@ -117,33 +134,39 @@ now frozen — no breaking changes without a new major version.
|
|||||||
- Namespace drawer navigation with URL hash support
|
- Namespace drawer navigation with URL hash support
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Migrated namespace detail to right-side drawer panel
|
- Migrated namespace detail to right-side drawer panel
|
||||||
- Improved drawer keyboard navigation (Escape to close)
|
- Improved drawer keyboard navigation (Escape to close)
|
||||||
- Enhanced settings page with connection testing
|
- Enhanced settings page with connection testing
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Empty namespace crash handling
|
- Empty namespace crash handling
|
||||||
- Drawer navigation pattern for better UX
|
- Drawer navigation pattern for better UX
|
||||||
|
|
||||||
## [0.2.5] - 2025-12-XX
|
## [0.2.5] - 2025-12-XX
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Improved theming and settings visibility
|
- Improved theming and settings visibility
|
||||||
|
|
||||||
## [0.2.4] - 2025-12-XX
|
## [0.2.4] - 2025-12-XX
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Increased namespace detail panel width to 1000px for better readability
|
- Increased namespace detail panel width to 1000px for better readability
|
||||||
|
|
||||||
## [0.2.3] - 2025-12-XX
|
## [0.2.3] - 2025-12-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Full URL support for custom Polaris dashboards
|
- Full URL support for custom Polaris dashboards
|
||||||
- Support for external Polaris instances (not just service proxy)
|
- Support for external Polaris instances (not just service proxy)
|
||||||
|
|
||||||
## [0.2.2] - 2025-12-XX
|
## [0.2.2] - 2025-12-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Configurable Polaris dashboard URL setting
|
- Configurable Polaris dashboard URL setting
|
||||||
- Settings page for plugin configuration
|
- Settings page for plugin configuration
|
||||||
- Refresh interval configuration
|
- Refresh interval configuration
|
||||||
@@ -151,136 +174,161 @@ now frozen — no breaking changes without a new major version.
|
|||||||
## [0.2.1] - 2025-12-XX
|
## [0.2.1] - 2025-12-XX
|
||||||
|
|
||||||
### Infrastructure
|
### Infrastructure
|
||||||
|
|
||||||
- Migrated to GitHub as primary repository
|
- Migrated to GitHub as primary repository
|
||||||
- Fixed v0.2.0 checksum in ArtifactHub metadata
|
- Fixed v0.2.0 checksum in ArtifactHub metadata
|
||||||
|
|
||||||
## [0.2.0] - 2025-12-XX
|
## [0.2.0] - 2025-12-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Namespace drawer navigation
|
- Namespace drawer navigation
|
||||||
- URL hash-based routing for namespaces
|
- URL hash-based routing for namespaces
|
||||||
- Keyboard shortcuts (Escape to close drawer)
|
- Keyboard shortcuts (Escape to close drawer)
|
||||||
|
|
||||||
### Infrastructure
|
### Infrastructure
|
||||||
|
|
||||||
- GitHub release automation
|
- GitHub release automation
|
||||||
- Improved CI/CD workflow
|
- Improved CI/CD workflow
|
||||||
|
|
||||||
## [0.1.7] - 2025-11-XX
|
## [0.1.7] - 2025-11-XX
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
- Removed incorrect development installation instructions
|
- Removed incorrect development installation instructions
|
||||||
|
|
||||||
## [0.1.6] - 2025-11-XX
|
## [0.1.6] - 2025-11-XX
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Plugin settings display name changed to "Polaris"
|
- Plugin settings display name changed to "Polaris"
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
- Added tooltip to skipped count explaining limitation
|
- Added tooltip to skipped count explaining limitation
|
||||||
- Documented skipped count limitation in README
|
- Documented skipped count limitation in README
|
||||||
|
|
||||||
## [0.1.5] - 2025-11-XX
|
## [0.1.5] - 2025-11-XX
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Restored `:80` port in service proxy URL for correct dashboard access
|
- Restored `:80` port in service proxy URL for correct dashboard access
|
||||||
|
|
||||||
## [0.1.4] - 2025-11-XX
|
## [0.1.4] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Playwright E2E smoke tests
|
- Playwright E2E smoke tests
|
||||||
- Test coverage for sidebar, overview, namespaces, and detail views
|
- Test coverage for sidebar, overview, namespaces, and detail views
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Empty namespace crash (graceful handling)
|
- Empty namespace crash (graceful handling)
|
||||||
- Removed `:80` port suffix from service proxy URL for RBAC compatibility
|
- Removed `:80` port suffix from service proxy URL for RBAC compatibility
|
||||||
|
|
||||||
## [0.1.3] - 2025-11-XX
|
## [0.1.3] - 2025-11-XX
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Service proxy URL format for consistent RBAC requirements
|
- Service proxy URL format for consistent RBAC requirements
|
||||||
|
|
||||||
## [0.1.2] - 2025-11-XX
|
## [0.1.2] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Namespace filtering and sorting
|
- Namespace filtering and sorting
|
||||||
- Enhanced resource table in namespace detail view
|
- Enhanced resource table in namespace detail view
|
||||||
|
|
||||||
## [0.1.1] - 2025-11-XX
|
## [0.1.1] - 2025-11-XX
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Score calculation for resources with mixed results
|
- Score calculation for resources with mixed results
|
||||||
- Percentage display formatting
|
- Percentage display formatting
|
||||||
|
|
||||||
## [0.1.0] - 2025-11-XX
|
## [0.1.0] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Namespace detail view with resource-level audit results
|
- Namespace detail view with resource-level audit results
|
||||||
- Drill-down navigation from namespace list
|
- Drill-down navigation from namespace list
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Improved data fetching with error handling
|
- Improved data fetching with error handling
|
||||||
- Better loading states
|
- Better loading states
|
||||||
|
|
||||||
## [0.0.10] - 2025-11-XX
|
## [0.0.10] - 2025-11-XX
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **RBAC Documentation:** Corrected to use `services/proxy` permission instead of ConfigMap access
|
- **RBAC Documentation:** Corrected to use `services/proxy` permission instead of ConfigMap access
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
- Updated README with accurate RBAC requirements
|
- Updated README with accurate RBAC requirements
|
||||||
- Added minimal Role example
|
- Added minimal Role example
|
||||||
|
|
||||||
## [0.0.9] - 2025-11-XX
|
## [0.0.9] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Refresh button for manual data reload
|
- Refresh button for manual data reload
|
||||||
- Last updated timestamp display
|
- Last updated timestamp display
|
||||||
|
|
||||||
## [0.0.8] - 2025-11-XX
|
## [0.0.8] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Skipped checks display in check summary
|
- Skipped checks display in check summary
|
||||||
- Improved check categorization (pass/warning/danger/skipped)
|
- Improved check categorization (pass/warning/danger/skipped)
|
||||||
|
|
||||||
## [0.0.7] - 2025-11-XX
|
## [0.0.7] - 2025-11-XX
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Enhanced overview dashboard layout
|
- Enhanced overview dashboard layout
|
||||||
- Better visual hierarchy for cluster score
|
- Better visual hierarchy for cluster score
|
||||||
|
|
||||||
## [0.0.6] - 2025-11-XX
|
## [0.0.6] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Namespace list view with per-namespace scores
|
- Namespace list view with per-namespace scores
|
||||||
- Navigation between overview and namespace views
|
- Navigation between overview and namespace views
|
||||||
|
|
||||||
## [0.0.5] - 2025-11-XX
|
## [0.0.5] - 2025-11-XX
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Data fetching error handling
|
- Data fetching error handling
|
||||||
- API proxy path configuration
|
- API proxy path configuration
|
||||||
|
|
||||||
## [0.0.4] - 2025-11-XX
|
## [0.0.4] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Check distribution visualization
|
- Check distribution visualization
|
||||||
- Pass/Warning/Danger count display
|
- Pass/Warning/Danger count display
|
||||||
|
|
||||||
## [0.0.3] - 2025-11-XX
|
## [0.0.3] - 2025-11-XX
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Improved cluster score calculation
|
- Improved cluster score calculation
|
||||||
- Better result aggregation logic
|
- Better result aggregation logic
|
||||||
|
|
||||||
## [0.0.2] - 2025-11-XX
|
## [0.0.2] - 2025-11-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Cluster score display
|
- Cluster score display
|
||||||
- Basic check summary table
|
- Basic check summary table
|
||||||
|
|
||||||
## [0.0.1] - 2025-10-XX
|
## [0.0.1] - 2025-10-XX
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Initial release
|
- Initial release
|
||||||
- Basic Polaris plugin structure
|
- Basic Polaris plugin structure
|
||||||
- Sidebar entry "Polaris"
|
- Sidebar entry "Polaris"
|
||||||
@@ -290,6 +338,7 @@ now frozen — no breaking changes without a new major version.
|
|||||||
- React components using Headlamp CommonComponents
|
- React components using Headlamp CommonComponents
|
||||||
|
|
||||||
### Infrastructure
|
### Infrastructure
|
||||||
|
|
||||||
- GitHub repository setup
|
- GitHub repository setup
|
||||||
- ArtifactHub package registration
|
- ArtifactHub package registration
|
||||||
- Automated release workflow
|
- Automated release workflow
|
||||||
|
|||||||
@@ -25,13 +25,15 @@ npm run format:check # Prettier check
|
|||||||
npm test # vitest run
|
npm test # vitest run
|
||||||
npm run test:watch # vitest watch mode
|
npm run test:watch # vitest watch mode
|
||||||
npx vitest run src/api/polaris.test.ts # run a single test file
|
npx vitest run src/api/polaris.test.ts # run a single test file
|
||||||
|
npm run e2e # Playwright E2E tests
|
||||||
|
npm run e2e:headed # Playwright headed mode
|
||||||
```
|
```
|
||||||
|
|
||||||
All tests and `tsc` must pass before committing.
|
All tests and `tsc` must pass before committing.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
```
|
```text
|
||||||
src/
|
src/
|
||||||
├── index.tsx # Plugin entry: registerRoute, registerSidebarEntry, registerDetailsViewSection, registerAppBarAction, registerPluginSettings; PolarisErrorBoundary
|
├── index.tsx # Plugin entry: registerRoute, registerSidebarEntry, registerDetailsViewSection, registerAppBarAction, registerPluginSettings; PolarisErrorBoundary
|
||||||
├── test-utils.tsx # Shared test fixtures (makeResult, makeAuditData)
|
├── test-utils.tsx # Shared test fixtures (makeResult, makeAuditData)
|
||||||
@@ -71,6 +73,7 @@ Data is fetched via `ApiProxy.request` to the Polaris dashboard service proxy an
|
|||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Mock pattern for headlamp APIs:
|
Mock pattern for headlamp APIs:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
vi.mock('@kinvolk/headlamp-plugin/lib', () => ({
|
vi.mock('@kinvolk/headlamp-plugin/lib', () => ({
|
||||||
ApiProxy: { request: vi.fn().mockResolvedValue({}) },
|
ApiProxy: { request: vi.fn().mockResolvedValue({}) },
|
||||||
|
|||||||
+591
@@ -0,0 +1,591 @@
|
|||||||
|
# CONTEXT.md - Headlamp Polaris Plugin
|
||||||
|
|
||||||
|
**Purpose**: Comprehensive reverse prompt for AI assistants working on this project.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
The Headlamp Polaris Plugin surfaces [Fairwinds Polaris](https://www.fairwinds.com/polaris) audit results directly inside the [Headlamp](https://headlamp.dev) Kubernetes UI. It provides a read-only dashboard showing cluster-wide security, reliability, and efficiency scores derived from Polaris policy checks.
|
||||||
|
|
||||||
|
- **Stack**: React + TypeScript plugin for Headlamp (v0.26+)
|
||||||
|
- **Data Source**: Polaris dashboard API via Kubernetes service proxy (read-only)
|
||||||
|
- **Current Version**: v0.4.1
|
||||||
|
- **Key Constraint**: No direct Kubernetes resource access - all data fetched through service proxy
|
||||||
|
|
||||||
|
## Architecture & Data Flow
|
||||||
|
|
||||||
|
### Component Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
src/index.tsx # Entry point: registers routes, sidebar, settings
|
||||||
|
├── PolarisDataContext.tsx # Shared data fetch with auto-refresh
|
||||||
|
├── components/
|
||||||
|
│ ├── DashboardView.tsx # Overview (score, checks, top issues)
|
||||||
|
│ ├── NamespacesListView.tsx # Namespace list with scores
|
||||||
|
│ ├── NamespaceDetailView.tsx # Per-namespace drill-down (drawer)
|
||||||
|
│ ├── PolarisSettings.tsx # Settings (refresh interval, URL, test)
|
||||||
|
│ ├── AppBarScoreBadge.tsx # Cluster score badge in top nav
|
||||||
|
│ └── InlineAuditSection.tsx # Injected into workload detail views
|
||||||
|
└── api/
|
||||||
|
└── polaris.ts # Types, hooks, utilities
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Source
|
||||||
|
|
||||||
|
- **Service Proxy Path**: `/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json`
|
||||||
|
- **Schema**: `AuditData` with `ClusterInfo`, `Results[]` containing nested `PodResult` and `ContainerResults`
|
||||||
|
- **Method**: `ApiProxy.request()` from Headlamp plugin SDK (handles K8s API auth automatically)
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
- **Pattern**: React Context (see `src/api/PolarisDataContext.tsx`)
|
||||||
|
- **Rationale**: ADR-001 - Prevents duplicate API calls when multiple components need same data
|
||||||
|
- **Auto-refresh**: User-configurable interval (1/5/10/30 min, default 5 min)
|
||||||
|
- **Storage**: Refresh interval and dashboard URL stored in `localStorage`
|
||||||
|
|
||||||
|
### Score Computation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Formula: (pass / total) * 100, rounded to nearest integer
|
||||||
|
function computeScore(counts: ResultCounts): number {
|
||||||
|
if (counts.total === 0) return 0;
|
||||||
|
return Math.round((counts.pass / counts.total) * 100);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technology Constraints
|
||||||
|
|
||||||
|
### ⚠️ CRITICAL: Headlamp Components Only
|
||||||
|
|
||||||
|
**MUST** use `@kinvolk/headlamp-plugin/lib/CommonComponents`
|
||||||
|
**NEVER** import from `@mui/material` or `@mui/icons-material`
|
||||||
|
|
||||||
|
**Why**: Historical issue (v0.3.2) - MUI imports caused plugin load failures. Headlamp provides all needed components as re-exports.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ Correct
|
||||||
|
import { SectionBox, StatusLabel } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
|
|
||||||
|
// ❌ Wrong - will break plugin
|
||||||
|
import { Box, Chip } from '@mui/material';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other Constraints
|
||||||
|
|
||||||
|
- **TypeScript Strictness**: No `any`, explicit types, strict mode enabled
|
||||||
|
- **Packaging**: `@kinvolk/headlamp-plugin` is peer dependency - don't bundle React/MUI
|
||||||
|
- **Theme Handling**: Use CSS variables (`--mui-palette-*`), not theme imports
|
||||||
|
- **Sidebar Limitation**: Headlamp only supports 2-level nesting (parent → children)
|
||||||
|
|
||||||
|
## Component Patterns & Gotchas
|
||||||
|
|
||||||
|
### Headlamp Component Issues
|
||||||
|
|
||||||
|
1. **StatusLabel with empty status**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ❌ Renders near-invisible (muted background)
|
||||||
|
<StatusLabel status="">{value}</StatusLabel>
|
||||||
|
|
||||||
|
// ✅ Use plain String() for neutral values
|
||||||
|
<span>{String(value)}</span>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Link component crashes on plugin routes**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ❌ Headlamp Link crashes on plugin-registered routes
|
||||||
|
import { Link } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
|
|
||||||
|
// ✅ Use react-router-dom Link with Router.createRouteURL
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Router } from '@kinvolk/headlamp-plugin/lib';
|
||||||
|
|
||||||
|
<Link to={Router.createRouteURL('/polaris/namespaces')}>View</Link>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Visual components that work well**
|
||||||
|
- `PercentageCircle` - Great for score display
|
||||||
|
- `PercentageBar` - Great for check distribution
|
||||||
|
- `SimpleTable` - Fast, clean tables
|
||||||
|
- `NameValueTable` - Key-value pairs
|
||||||
|
- `SectionBox` - Card containers with titles
|
||||||
|
|
||||||
|
### Code Conventions
|
||||||
|
|
||||||
|
- **Functional Components**: Always use function components with hooks
|
||||||
|
- **Named Exports**: Prefer named exports over default exports
|
||||||
|
- **Props Interfaces**: Define as TypeScript interfaces, not inline types
|
||||||
|
- **Import Order**: React → third-party → Headlamp → local (auto-sorted by eslint)
|
||||||
|
|
||||||
|
## RBAC & Security
|
||||||
|
|
||||||
|
### Minimal Permission Required
|
||||||
|
|
||||||
|
The plugin requires **only** this RBAC permission:
|
||||||
|
|
||||||
|
| Verb | API Group | Resource | Resource Name | Namespace |
|
||||||
|
|------|-----------|----------|---------------|-----------|
|
||||||
|
| `get` | `""` (core) | `services/proxy` | `polaris-dashboard` | `polaris` |
|
||||||
|
|
||||||
|
### Example Role
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: polaris-proxy-reader
|
||||||
|
namespace: polaris
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["services/proxy"]
|
||||||
|
resourceNames: ["polaris-dashboard"]
|
||||||
|
verbs: ["get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: headlamp-polaris-proxy
|
||||||
|
namespace: polaris
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: headlamp
|
||||||
|
namespace: kube-system
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: polaris-proxy-reader
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Notes
|
||||||
|
|
||||||
|
- **Namespaced Role**: MUST be namespaced Role, NOT ClusterRole
|
||||||
|
- **ResourceNames Required**: Always specify `resourceNames: ["polaris-dashboard"]`
|
||||||
|
- **No Write Operations**: Plugin only performs GET, never create/update/delete
|
||||||
|
- **Token-Auth Mode**: When Headlamp uses user tokens, each user needs the RoleBinding
|
||||||
|
- **Network Policy**: If enforced, allow API server → `polaris-dashboard:80` ingress
|
||||||
|
- **Audit Logging**: Every proxy request logged as K8s API audit event
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Start development mode (hot reload at localhost:4466)
|
||||||
|
npm start
|
||||||
|
|
||||||
|
# Build plugin
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Create tarball for distribution
|
||||||
|
npm run package
|
||||||
|
|
||||||
|
# Type-check without emitting
|
||||||
|
npm run tsc
|
||||||
|
|
||||||
|
# Lint
|
||||||
|
npm run lint
|
||||||
|
|
||||||
|
# Run unit tests
|
||||||
|
npm test
|
||||||
|
|
||||||
|
# Run E2E tests (requires cluster access)
|
||||||
|
npm run e2e
|
||||||
|
|
||||||
|
# Format code
|
||||||
|
npm run format
|
||||||
|
|
||||||
|
# Check formatting (CI)
|
||||||
|
npm run format:check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Branching Strategy
|
||||||
|
|
||||||
|
- ✅ **ALWAYS use feature branches** for code changes (`feat/*`, `fix/*`, `docs/*`)
|
||||||
|
- ✅ **MAY push directly to main** for: documentation-only changes, version bump commits
|
||||||
|
- ❌ **NEVER push code changes directly to main**
|
||||||
|
|
||||||
|
### Commit Convention
|
||||||
|
|
||||||
|
Use Conventional Commits:
|
||||||
|
|
||||||
|
- `feat:` - New feature
|
||||||
|
- `fix:` - Bug fix
|
||||||
|
- `docs:` - Documentation only
|
||||||
|
- `chore:` - Maintenance (deps, config)
|
||||||
|
- `test:` - Test changes
|
||||||
|
- `ci:` - CI/CD changes
|
||||||
|
|
||||||
|
### PR Process
|
||||||
|
|
||||||
|
All PRs must pass:
|
||||||
|
|
||||||
|
1. Build (`npm run build`)
|
||||||
|
2. Lint (`npm run lint`)
|
||||||
|
3. Type-check (`npm run tsc`)
|
||||||
|
4. Unit tests (`npm test`)
|
||||||
|
5. Format check (`npm run format:check`)
|
||||||
|
|
||||||
|
**Before committing**: Always run `npx prettier --write src/`
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Unit Tests (Vitest)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm test # Run once
|
||||||
|
npm run test:watch # Watch mode
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Framework**: Vitest with jsdom environment
|
||||||
|
- **Test files**: `*.test.ts`, `*.test.tsx` in `src/`
|
||||||
|
- **Setup**: `vitest.setup.ts` with `@testing-library/jest-dom`
|
||||||
|
- **Coverage**: Focus on meaningful tests, not just numbers
|
||||||
|
- **Test utilities**: `src/test-utils.tsx` provides test wrapper with context
|
||||||
|
|
||||||
|
### E2E Tests (Playwright)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run e2e # Headless
|
||||||
|
npm run e2e:headed # With browser UI
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Framework**: Playwright
|
||||||
|
- **Test files**: `e2e/*.spec.ts`
|
||||||
|
- `polaris.spec.ts` - Sidebar, overview, namespaces, detail drawer
|
||||||
|
- `settings.spec.ts` - Plugin settings page
|
||||||
|
- `appbar.spec.ts` - App bar score badge
|
||||||
|
- **Auth**: Supports both OIDC (Authentik) and token-based auth (see `e2e/auth.setup.ts`)
|
||||||
|
- **CI**: Runs on GitHub Actions with `k3s-animaniacs` runner
|
||||||
|
|
||||||
|
### Local E2E Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Token-based auth
|
||||||
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system --duration=24h)
|
||||||
|
npm run e2e
|
||||||
|
|
||||||
|
# OIDC auth (Authentik)
|
||||||
|
export AUTHENTIK_USERNAME=your-username
|
||||||
|
export AUTHENTIK_PASSWORD=your-password
|
||||||
|
npm run e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD & Release
|
||||||
|
|
||||||
|
### CI Workflow (`.github/workflows/ci.yaml`)
|
||||||
|
|
||||||
|
Runs on push to main and all PRs:
|
||||||
|
|
||||||
|
1. Checkout
|
||||||
|
2. `npm ci`
|
||||||
|
3. `npm run build`
|
||||||
|
4. `npm run lint`
|
||||||
|
5. `npm run tsc`
|
||||||
|
6. `npm run format:check`
|
||||||
|
7. `npm test`
|
||||||
|
|
||||||
|
Runner: `local-ubuntu-latest`
|
||||||
|
|
||||||
|
### E2E Workflow (`.github/workflows/e2e.yaml`)
|
||||||
|
|
||||||
|
Runs on push, PR, and manual trigger:
|
||||||
|
|
||||||
|
1. Checkout
|
||||||
|
2. `npm ci`
|
||||||
|
3. `npm run e2e`
|
||||||
|
|
||||||
|
Runner: `k3s-animaniacs` (has cluster access)
|
||||||
|
Requires: `HEADLAMP_URL`, `HEADLAMP_TOKEN` or `AUTHENTIK_USERNAME`/`AUTHENTIK_PASSWORD`
|
||||||
|
|
||||||
|
### Release Workflow (`.github/workflows/release.yaml`)
|
||||||
|
|
||||||
|
**Manual trigger** via workflow_dispatch with version input:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via GitHub UI or CLI
|
||||||
|
gh workflow run release.yaml -f version=0.4.2
|
||||||
|
```
|
||||||
|
|
||||||
|
Steps:
|
||||||
|
|
||||||
|
1. Validate version format (semver)
|
||||||
|
2. Bump `package.json` + `artifacthub-pkg.yml`
|
||||||
|
3. Build plugin
|
||||||
|
4. Package tarball
|
||||||
|
5. Compute SHA256 checksum
|
||||||
|
6. Commit version bump
|
||||||
|
7. Create git tag
|
||||||
|
8. Create GitHub release
|
||||||
|
9. Upload tarball to release
|
||||||
|
|
||||||
|
**Guard**: Skips if checksum already matches (prevents infinite loop)
|
||||||
|
|
||||||
|
**Post-release**: ArtifactHub pulls metadata every 30 min (no webhook, pull-based)
|
||||||
|
|
||||||
|
### Version Bump Requirements
|
||||||
|
|
||||||
|
**ALWAYS bump both files in the same commit**:
|
||||||
|
|
||||||
|
- `package.json` - `version` field
|
||||||
|
- `artifacthub-pkg.yml` - `version` field + `digest` (checksum) + `archive.url`
|
||||||
|
|
||||||
|
## Known Issues & Workarounds
|
||||||
|
|
||||||
|
### ⚠️ Headlamp v0.39.0 Known Issues
|
||||||
|
|
||||||
|
**AutoSizer JavaScript Error**
|
||||||
|
|
||||||
|
- **Symptom**: Console shows `TypeError: undefined is not an object (evaluating 'io.AutoSizer')`
|
||||||
|
- **Impact**: Cosmetic error in Settings page, doesn't break functionality
|
||||||
|
- **Root Cause**: Headlamp core bug, not plugin-related
|
||||||
|
- **Workaround**: None needed, can be ignored
|
||||||
|
|
||||||
|
**Plugin Loading (RESOLVED)**
|
||||||
|
|
||||||
|
- **Old Issue**: Previously thought `config.watchPlugins: false` was required
|
||||||
|
- **Resolution**: Plugins load correctly with default `watchPlugins: true`
|
||||||
|
- **Note**: If you see old docs mentioning `watchPlugins: false`, ignore them
|
||||||
|
|
||||||
|
### Polaris Dashboard Behavior
|
||||||
|
|
||||||
|
**Stale Audit Data**
|
||||||
|
|
||||||
|
- **Symptom**: Plugin shows old audit timestamp
|
||||||
|
- **Root Cause**: Polaris dashboard runs audit once at pod startup, then caches results
|
||||||
|
- **Does NOT**: Continuously re-audit in real-time
|
||||||
|
- **Workaround**: Restart Polaris pods for fresh data
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl rollout restart deployment -n polaris polaris-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Load Balancing**: Service balances across multiple pods - each may have different audit timestamps
|
||||||
|
- **Plugin Auto-Refresh**: Works correctly - just fetches whatever Polaris currently has cached
|
||||||
|
|
||||||
|
### Skipped Count Limitation
|
||||||
|
|
||||||
|
**What It Shows**:
|
||||||
|
|
||||||
|
- Only checks with `Severity: "ignore"` in Polaris API response
|
||||||
|
- Does NOT include annotation-based exemptions (`polaris.fairwinds.com/*-exempt`)
|
||||||
|
|
||||||
|
**Why**:
|
||||||
|
|
||||||
|
- Polaris omits exempted checks from `results.json`
|
||||||
|
- Plugin has no access to raw K8s resources to compute exemptions
|
||||||
|
- By design: service proxy limitation
|
||||||
|
|
||||||
|
**Workaround**:
|
||||||
|
|
||||||
|
- Link to native Polaris dashboard for full exemption count
|
||||||
|
- UI tooltip explains this limitation
|
||||||
|
|
||||||
|
## Deployment Patterns
|
||||||
|
|
||||||
|
### Plugin Manager (Recommended)
|
||||||
|
|
||||||
|
Install via Headlamp UI (Settings → Plugins → Catalog) or Helm values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
pluginsManager:
|
||||||
|
enabled: true
|
||||||
|
configContent: |
|
||||||
|
plugins:
|
||||||
|
- name: polaris
|
||||||
|
source: https://artifacthub.io/packages/headlamp/polaris/headlamp-polaris-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sidecar Container (Alternative)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: headlamp
|
||||||
|
# ... main container
|
||||||
|
- name: headlamp-plugin
|
||||||
|
image: node:lts-alpine
|
||||||
|
command:
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- |
|
||||||
|
npx @headlamp-k8s/pluginctl@latest install \
|
||||||
|
--config /config/plugin.yml \
|
||||||
|
--folderName /headlamp/plugins \
|
||||||
|
--watch
|
||||||
|
volumeMounts:
|
||||||
|
- name: plugins-dir
|
||||||
|
mountPath: /headlamp/plugins
|
||||||
|
- name: plugin-config
|
||||||
|
mountPath: /config
|
||||||
|
volumes:
|
||||||
|
- name: plugins-dir
|
||||||
|
emptyDir: {}
|
||||||
|
- name: plugin-config
|
||||||
|
configMap:
|
||||||
|
name: headlamp-plugin-config
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Tarball
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download release
|
||||||
|
wget https://github.com/cpfarhood/headlamp-polaris-plugin/releases/download/v0.4.1/headlamp-polaris-plugin-0.4.1.tgz
|
||||||
|
|
||||||
|
# Extract to plugin directory
|
||||||
|
tar -xzf headlamp-polaris-plugin-0.4.1.tgz -C /headlamp/plugins/
|
||||||
|
|
||||||
|
# Restart Headlamp
|
||||||
|
kubectl rollout restart deployment headlamp -n kube-system
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Files Reference
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
index.tsx # Entry point: registers sidebar, routes, settings, etc.
|
||||||
|
api/
|
||||||
|
polaris.ts # Core types, usePolarisData hook, utilities
|
||||||
|
PolarisDataContext.tsx # React Context provider for shared data
|
||||||
|
components/
|
||||||
|
DashboardView.tsx # Overview page (score, checks, top issues)
|
||||||
|
NamespacesListView.tsx # Namespace table with scores
|
||||||
|
NamespaceDetailView.tsx # Drawer panel with per-namespace drill-down
|
||||||
|
PolarisSettings.tsx # Settings page (refresh, URL, test)
|
||||||
|
AppBarScoreBadge.tsx # Cluster score chip in top nav bar
|
||||||
|
InlineAuditSection.tsx # Injected into resource detail views
|
||||||
|
test-utils.tsx # Test helpers (wrapper with context)
|
||||||
|
|
||||||
|
.github/workflows/
|
||||||
|
ci.yaml # Lint, type-check, build, test
|
||||||
|
e2e.yaml # Playwright E2E tests
|
||||||
|
release.yaml # Automated releases
|
||||||
|
|
||||||
|
e2e/ # Playwright tests
|
||||||
|
polaris.spec.ts # Main plugin functionality
|
||||||
|
settings.spec.ts # Settings page
|
||||||
|
appbar.spec.ts # App bar badge
|
||||||
|
auth.setup.ts # OIDC/token auth setup
|
||||||
|
|
||||||
|
docs/ # Comprehensive documentation
|
||||||
|
architecture/ # Overview, design decisions, ADRs
|
||||||
|
deployment/ # Helm, Kubernetes, production guides
|
||||||
|
troubleshooting/ # Common issues, RBAC, network problems
|
||||||
|
getting-started/ # Quick start, prerequisites, installation
|
||||||
|
|
||||||
|
package.json # Version, scripts, dependencies
|
||||||
|
artifacthub-pkg.yml # ArtifactHub metadata (version, checksum)
|
||||||
|
tsconfig.json # Extends @kinvolk/headlamp-plugin config
|
||||||
|
vitest.config.mts # Vitest config (jsdom, excludes e2e/)
|
||||||
|
.eslintrc.js # Extends @headlamp-k8s/eslint-config
|
||||||
|
.prettierrc.js # Uses @headlamp-k8s prettier config
|
||||||
|
```
|
||||||
|
|
||||||
|
## MCP Servers (Claude Code)
|
||||||
|
|
||||||
|
- **GitHub**: Source control (`github-mcp-server`), repo at `cpfarhood/headlamp-polaris-plugin`
|
||||||
|
- **Kubernetes (local)**: Cluster access via `kubernetes-mcp-server`
|
||||||
|
- **Flux (local)**: Flux Operator access via `flux-operator-mcp`
|
||||||
|
- **Playwright**: Browser automation via `@playwright/mcp`
|
||||||
|
|
||||||
|
## Common Tasks Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start development
|
||||||
|
npm install && npm start
|
||||||
|
|
||||||
|
# Run all checks before PR
|
||||||
|
npm run build && npm run lint && npm run tsc && npm test && npm run format
|
||||||
|
|
||||||
|
# Create release (maintainers only)
|
||||||
|
# 1. Edit CHANGELOG.md
|
||||||
|
# 2. Trigger release workflow:
|
||||||
|
gh workflow run release.yaml -f version=0.4.2
|
||||||
|
|
||||||
|
# Run E2E tests locally
|
||||||
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system --duration=24h)
|
||||||
|
npm run e2e
|
||||||
|
|
||||||
|
# Fix formatting issues
|
||||||
|
npx prettier --write src/
|
||||||
|
|
||||||
|
# Check Polaris audit freshness
|
||||||
|
kubectl get --raw "/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json" | jq -r '.AuditTime'
|
||||||
|
|
||||||
|
# Restart Polaris for fresh audit
|
||||||
|
kubectl rollout restart deployment -n polaris polaris-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
## Anti-Patterns (What NOT to Do)
|
||||||
|
|
||||||
|
- ❌ Import from `@mui/material` or `@mui/icons-material` → breaks plugin
|
||||||
|
- ❌ Use `any` type → strict TypeScript required
|
||||||
|
- ❌ Push code changes directly to main → always use feature branches
|
||||||
|
- ❌ Grant broader RBAC than `get services/proxy` → security risk
|
||||||
|
- ❌ Use ClusterRole instead of namespaced Role → violates least privilege
|
||||||
|
- ❌ Forget to run `npx prettier --write src/` → CI will fail
|
||||||
|
- ❌ Use inline styles without CSS variables → breaks dark mode
|
||||||
|
- ❌ Try to query K8s resources directly → plugin only has service proxy access
|
||||||
|
- ❌ Import Headlamp `Link` for plugin routes → use react-router-dom `Link` + `Router.createRouteURL()`
|
||||||
|
- ❌ Assume Polaris continuously re-audits → it only audits at pod startup
|
||||||
|
|
||||||
|
## Quick Diagnosis Guide
|
||||||
|
|
||||||
|
```
|
||||||
|
Symptom: Plugin not in sidebar
|
||||||
|
→ Check: Hard refresh browser (Cmd+Shift+R / Ctrl+Shift+R)
|
||||||
|
→ Check: Plugin installed? kubectl get configmap headlamp-plugin-config -n kube-system
|
||||||
|
|
||||||
|
Symptom: 403 Access Denied
|
||||||
|
→ Check: RBAC binding exists? kubectl get role,rolebinding -n polaris
|
||||||
|
→ Fix: Apply RBAC example from docs/deployment/rbac.md
|
||||||
|
|
||||||
|
Symptom: 404 or 503
|
||||||
|
→ Check: Polaris installed? kubectl get pods -n polaris
|
||||||
|
→ Check: Service exists? kubectl get svc polaris-dashboard -n polaris
|
||||||
|
|
||||||
|
Symptom: Stale audit data
|
||||||
|
→ Fix: kubectl rollout restart deployment -n polaris polaris-dashboard
|
||||||
|
→ Verify: Check AuditTime in UI matches current date
|
||||||
|
|
||||||
|
Symptom: Settings page empty or broken
|
||||||
|
→ Check: Plugin version ≥ v0.3.3?
|
||||||
|
→ Fix: Upgrade plugin and hard refresh browser
|
||||||
|
|
||||||
|
Symptom: CI prettier check fails
|
||||||
|
→ Fix: npx prettier --write src/
|
||||||
|
→ Commit: Include formatting fixes in your PR
|
||||||
|
|
||||||
|
Symptom: Dark mode white backgrounds
|
||||||
|
→ Check: Plugin version ≥ v0.3.5?
|
||||||
|
→ Fix: Upgrade and hard refresh browser
|
||||||
|
```
|
||||||
|
|
||||||
|
## Historical Context
|
||||||
|
|
||||||
|
### Why Service Proxy Instead of ConfigMaps?
|
||||||
|
|
||||||
|
Early versions (< v0.0.10) incorrectly documented ConfigMap RBAC. The plugin **never** accessed ConfigMaps - it always used the service proxy. This was clarified in v0.0.10.
|
||||||
|
|
||||||
|
### Why No MUI Imports?
|
||||||
|
|
||||||
|
v0.3.2 removed direct MUI imports because they caused plugin load failures. Headlamp provides all needed MUI components as re-exports through `CommonComponents`.
|
||||||
|
|
||||||
|
### Why React Context?
|
||||||
|
|
||||||
|
ADR-001 documents the switch to React Context. Before v0.3.0, each component called `usePolarisData()` independently, causing duplicate API requests. Context ensures a single shared fetch.
|
||||||
|
|
||||||
|
### Why No Continuous Polaris Audits?
|
||||||
|
|
||||||
|
Polaris dashboard mode runs a one-time audit at pod startup and caches results. This is by design in Polaris itself. For continuous auditing, Polaris would need to be configured in webhook mode (admission controller), which is a different deployment pattern.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: 2026-02-12
|
||||||
|
**Version**: v0.4.1
|
||||||
|
**Target Headlamp**: v0.26+
|
||||||
|
**Target Polaris**: v9.x
|
||||||
@@ -18,6 +18,7 @@ Thank you for your interest in contributing to the Headlamp Polaris Plugin! This
|
|||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
This project follows a standard code of conduct:
|
This project follows a standard code of conduct:
|
||||||
|
|
||||||
- Be respectful and inclusive
|
- Be respectful and inclusive
|
||||||
- Welcome newcomers and help them get started
|
- Welcome newcomers and help them get started
|
||||||
- Focus on constructive feedback
|
- Focus on constructive feedback
|
||||||
@@ -35,23 +36,27 @@ This project follows a standard code of conduct:
|
|||||||
### Development Setup
|
### Development Setup
|
||||||
|
|
||||||
1. **Fork and clone the repository:**
|
1. **Fork and clone the repository:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/YOUR_USERNAME/headlamp-polaris-plugin.git
|
git clone https://github.com/YOUR_USERNAME/headlamp-polaris-plugin.git
|
||||||
cd headlamp-polaris-plugin
|
cd headlamp-polaris-plugin
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Install dependencies:**
|
2. **Install dependencies:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Start development mode:**
|
3. **Start development mode:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm start
|
npm start
|
||||||
# Plugin will be available at http://localhost:4466
|
# Plugin will be available at http://localhost:4466
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Run tests:**
|
4. **Run tests:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Unit tests
|
# Unit tests
|
||||||
npm test
|
npm test
|
||||||
@@ -61,6 +66,7 @@ This project follows a standard code of conduct:
|
|||||||
```
|
```
|
||||||
|
|
||||||
5. **Build the plugin:**
|
5. **Build the plugin:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
@@ -79,18 +85,21 @@ This project follows a standard code of conduct:
|
|||||||
### Local Testing
|
### Local Testing
|
||||||
|
|
||||||
**Option 1: Development Mode**
|
**Option 1: Development Mode**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm start
|
npm start
|
||||||
# Opens Headlamp at http://localhost:4466 with hot reload
|
# Opens Headlamp at http://localhost:4466 with hot reload
|
||||||
```
|
```
|
||||||
|
|
||||||
**Option 2: Production Build**
|
**Option 2: Production Build**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build
|
npm run build
|
||||||
# Plugin bundle created in dist/
|
# Plugin bundle created in dist/
|
||||||
```
|
```
|
||||||
|
|
||||||
**Option 3: E2E Testing**
|
**Option 3: E2E Testing**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Set up environment (see e2e/README.md)
|
# Set up environment (see e2e/README.md)
|
||||||
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system --duration=24h)
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system --duration=24h)
|
||||||
@@ -116,6 +125,7 @@ npm run e2e
|
|||||||
- Chores: `chore/description`
|
- Chores: `chore/description`
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
feat/add-exemption-support
|
feat/add-exemption-support
|
||||||
fix/dark-mode-theme-colors
|
fix/dark-mode-theme-colors
|
||||||
@@ -127,16 +137,19 @@ chore/upgrade-dependencies
|
|||||||
### Branching Rules
|
### Branching Rules
|
||||||
|
|
||||||
**✅ ALWAYS use feature branches for:**
|
**✅ ALWAYS use feature branches for:**
|
||||||
|
|
||||||
- Code changes (new features, bug fixes, refactors)
|
- Code changes (new features, bug fixes, refactors)
|
||||||
- Test updates
|
- Test updates
|
||||||
- CI/CD workflow changes
|
- CI/CD workflow changes
|
||||||
- Package updates
|
- Package updates
|
||||||
|
|
||||||
**✅ MAY push directly to main for:**
|
**✅ MAY push directly to main for:**
|
||||||
|
|
||||||
- Documentation-only changes (README.md, CLAUDE.md, comments)
|
- Documentation-only changes (README.md, CLAUDE.md, comments)
|
||||||
- Version bump commits (`package.json` + `artifacthub-pkg.yml`)
|
- Version bump commits (`package.json` + `artifacthub-pkg.yml`)
|
||||||
|
|
||||||
**❌ NEVER push directly to main for:**
|
**❌ NEVER push directly to main for:**
|
||||||
|
|
||||||
- Any code changes to `src/`
|
- Any code changes to `src/`
|
||||||
- Test file changes
|
- Test file changes
|
||||||
- Workflow changes
|
- Workflow changes
|
||||||
@@ -206,6 +219,7 @@ Co-Authored-By: Happy <yesreply@happy.engineering>
|
|||||||
### Before Creating a PR
|
### Before Creating a PR
|
||||||
|
|
||||||
1. **Run all checks locally:**
|
1. **Run all checks locally:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build # Verify build succeeds
|
npm run build # Verify build succeeds
|
||||||
npm run lint # Check for linting errors
|
npm run lint # Check for linting errors
|
||||||
@@ -227,6 +241,7 @@ Co-Authored-By: Happy <yesreply@happy.engineering>
|
|||||||
### Creating a PR
|
### Creating a PR
|
||||||
|
|
||||||
1. **Push your branch:**
|
1. **Push your branch:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git push origin feat/your-feature
|
git push origin feat/your-feature
|
||||||
```
|
```
|
||||||
@@ -237,6 +252,7 @@ Co-Authored-By: Happy <yesreply@happy.engineering>
|
|||||||
- Link related issues with `Fixes #123` or `Closes #456`
|
- Link related issues with `Fixes #123` or `Closes #456`
|
||||||
|
|
||||||
3. **PR Title Format:**
|
3. **PR Title Format:**
|
||||||
|
|
||||||
```
|
```
|
||||||
feat: add exemption management UI
|
feat: add exemption management UI
|
||||||
fix: correct score calculation for skipped checks
|
fix: correct score calculation for skipped checks
|
||||||
@@ -299,12 +315,14 @@ npm run format:check
|
|||||||
### Import Organization
|
### Import Organization
|
||||||
|
|
||||||
Imports are automatically sorted by eslint. Order:
|
Imports are automatically sorted by eslint. Order:
|
||||||
|
|
||||||
1. React imports
|
1. React imports
|
||||||
2. Third-party libraries
|
2. Third-party libraries
|
||||||
3. Headlamp plugin imports
|
3. Headlamp plugin imports
|
||||||
4. Local imports (components, API, types)
|
4. Local imports (components, API, types)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SectionBox, StatusLabel } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
import { SectionBox, StatusLabel } from '@kinvolk/headlamp-plugin/lib/CommonComponents';
|
||||||
@@ -330,6 +348,7 @@ import { computeScore } from '../api/polaris';
|
|||||||
- Use descriptive test names
|
- Use descriptive test names
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
describe('countResults', () => {
|
describe('countResults', () => {
|
||||||
it('counts passing, warning, and danger results correctly', () => {
|
it('counts passing, warning, and danger results correctly', () => {
|
||||||
@@ -371,14 +390,17 @@ npm run e2e:headed
|
|||||||
When making changes, update relevant documentation:
|
When making changes, update relevant documentation:
|
||||||
|
|
||||||
#### Code Changes
|
#### Code Changes
|
||||||
|
|
||||||
- **README.md:** User-facing features, installation, configuration
|
- **README.md:** User-facing features, installation, configuration
|
||||||
- **CLAUDE.md:** Architecture, constraints, MCP integrations
|
- **CLAUDE.md:** Architecture, constraints, MCP integrations
|
||||||
- **JSDoc:** All public APIs, components, hooks
|
- **JSDoc:** All public APIs, components, hooks
|
||||||
|
|
||||||
#### Test Changes
|
#### Test Changes
|
||||||
|
|
||||||
- **e2e/README.md:** New test scenarios or setup changes
|
- **e2e/README.md:** New test scenarios or setup changes
|
||||||
|
|
||||||
#### Build/CI Changes
|
#### Build/CI Changes
|
||||||
|
|
||||||
- **README.md:** Build commands, release process
|
- **README.md:** Build commands, release process
|
||||||
- **.github/workflows/*.yaml:** Workflow comments
|
- **.github/workflows/*.yaml:** Workflow comments
|
||||||
|
|
||||||
@@ -405,6 +427,7 @@ export function countResults(data: AuditData): ResultCounts {
|
|||||||
### Version Numbering
|
### Version Numbering
|
||||||
|
|
||||||
We follow [Semantic Versioning](https://semver.org/):
|
We follow [Semantic Versioning](https://semver.org/):
|
||||||
|
|
||||||
- **Major (1.0.0):** Breaking changes
|
- **Major (1.0.0):** Breaking changes
|
||||||
- **Minor (0.1.0):** New features, backward compatible
|
- **Minor (0.1.0):** New features, backward compatible
|
||||||
- **Patch (0.0.1):** Bug fixes, backward compatible
|
- **Patch (0.0.1):** Bug fixes, backward compatible
|
||||||
@@ -416,6 +439,7 @@ We follow [Semantic Versioning](https://semver.org/):
|
|||||||
1. **Merge feature PRs to main**
|
1. **Merge feature PRs to main**
|
||||||
|
|
||||||
2. **Bump version:**
|
2. **Bump version:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Edit package.json and artifacthub-pkg.yml
|
# Edit package.json and artifacthub-pkg.yml
|
||||||
# Update version and archive-url
|
# Update version and archive-url
|
||||||
@@ -425,6 +449,7 @@ We follow [Semantic Versioning](https://semver.org/):
|
|||||||
```
|
```
|
||||||
|
|
||||||
3. **Create and push tag:**
|
3. **Create and push tag:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git tag vX.Y.Z
|
git tag vX.Y.Z
|
||||||
git push origin vX.Y.Z
|
git push origin vX.Y.Z
|
||||||
@@ -441,6 +466,7 @@ We follow [Semantic Versioning](https://semver.org/):
|
|||||||
### Pre-release Versions
|
### Pre-release Versions
|
||||||
|
|
||||||
For testing before stable release:
|
For testing before stable release:
|
||||||
|
|
||||||
- Use `-dev.N` suffix: `v0.3.5-dev.1`
|
- Use `-dev.N` suffix: `v0.3.5-dev.1`
|
||||||
- Follow same process as stable releases
|
- Follow same process as stable releases
|
||||||
- Mark as "pre-release" on GitHub
|
- Mark as "pre-release" on GitHub
|
||||||
|
|||||||
@@ -0,0 +1,333 @@
|
|||||||
|
# Headlamp Polaris Plugin - Project Assessment
|
||||||
|
|
||||||
|
**Date:** 2026-02-11
|
||||||
|
**Version:** v0.3.0
|
||||||
|
**Status:** Active Development
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
This assessment identifies critical issues and improvement opportunities for the headlamp-polaris-plugin project. The plugin is currently non-functional in production due to Headlamp v0.39.0 compatibility issues, and has several TypeScript compilation errors that need immediate attention.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔴 Critical Issues (Must Fix Immediately)
|
||||||
|
|
||||||
|
### 1. TypeScript Compilation Errors
|
||||||
|
|
||||||
|
**Severity:** CRITICAL
|
||||||
|
**Impact:** Build failures, type safety compromised
|
||||||
|
|
||||||
|
**Issues:**
|
||||||
|
|
||||||
|
- `src/index.tsx:72` - `registerDetailsViewSection` expects 1 argument, got 2
|
||||||
|
- `src/index.tsx:87` - `registerAppBarAction` expects 1 argument, got 2
|
||||||
|
|
||||||
|
**Recommendation:**
|
||||||
|
Update Headlamp plugin API calls to match the current version. Check @kinvolk/headlamp-plugin version compatibility.
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Review Headlamp plugin API documentation
|
||||||
|
- [ ] Update `registerDetailsViewSection` and `registerAppBarAction` calls
|
||||||
|
- [ ] Run `npm run tsc` to verify fixes
|
||||||
|
- [ ] Update CI to fail on TypeScript errors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Production Plugin Loading Failure
|
||||||
|
|
||||||
|
**Severity:** CRITICAL
|
||||||
|
**Impact:** Plugin is completely non-functional in production
|
||||||
|
|
||||||
|
**Root Cause:**
|
||||||
|
Headlamp v0.39.0 with default `watchPlugins: true` treats catalog-managed plugins as "development directory" plugins, preventing frontend JavaScript execution.
|
||||||
|
|
||||||
|
**Current Status:**
|
||||||
|
|
||||||
|
- Deployment patched to install plugins to `/headlamp/static-plugins`
|
||||||
|
- `watchPlugins: false` configured
|
||||||
|
- Waiting for user to test if plugins now load
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Confirm plugins load after recent deployment changes
|
||||||
|
- [ ] Document the fix in deployment guide
|
||||||
|
- [ ] Update MEMORY.md with final resolution
|
||||||
|
- [ ] Consider downgrading Headlamp if issue persists
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Test Failures
|
||||||
|
|
||||||
|
**Severity:** HIGH
|
||||||
|
**Impact:** CI failures, reduced confidence in changes
|
||||||
|
|
||||||
|
**Current Status:**
|
||||||
|
|
||||||
|
- 1 test file failing (DashboardView)
|
||||||
|
- 49 tests passing
|
||||||
|
- Error related to `SimpleTable` component mock
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Fix DashboardView test mocking
|
||||||
|
- [ ] Ensure all tests pass before merging PRs
|
||||||
|
- [ ] Add test for top issues feature
|
||||||
|
- [ ] Increase test coverage to >80%
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟡 High Priority Improvements
|
||||||
|
|
||||||
|
### 4. Type Safety Enhancements
|
||||||
|
|
||||||
|
**Severity:** HIGH
|
||||||
|
**Impact:** Better developer experience, catch errors earlier
|
||||||
|
|
||||||
|
**Recommendations:**
|
||||||
|
|
||||||
|
- Enable stricter TypeScript checks in `tsconfig.json`
|
||||||
|
- Add type definitions for all Headlamp plugin APIs
|
||||||
|
- Ensure no `any` types in production code
|
||||||
|
- Add JSDoc comments for complex types
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Audit codebase for `any` types
|
||||||
|
- [ ] Enable `noImplicitAny` and `strictNullChecks`
|
||||||
|
- [ ] Add type guards for API responses
|
||||||
|
- [ ] Document complex type structures
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Security Hardening
|
||||||
|
|
||||||
|
**Severity:** HIGH
|
||||||
|
**Impact:** Prevent vulnerabilities, protect user data
|
||||||
|
|
||||||
|
**Current Risks:**
|
||||||
|
|
||||||
|
- Direct Kubernetes API access via service proxy
|
||||||
|
- User input in exemption annotations (potential injection)
|
||||||
|
- External URL configuration for Polaris dashboard
|
||||||
|
|
||||||
|
**Recommendations:**
|
||||||
|
|
||||||
|
- Validate and sanitize all user inputs
|
||||||
|
- Implement input validation for dashboard URL
|
||||||
|
- Add CSRF protection for exemption management
|
||||||
|
- Audit dependencies for known vulnerabilities
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Add input validation utilities
|
||||||
|
- [ ] Sanitize exemption annotation values
|
||||||
|
- [ ] Validate URL format for dashboard configuration
|
||||||
|
- [ ] Run `npm audit` and fix vulnerabilities
|
||||||
|
- [ ] Add security testing to CI/CD
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Error Handling & User Experience
|
||||||
|
|
||||||
|
**Severity:** MEDIUM
|
||||||
|
**Impact:** Better error messages, improved debugging
|
||||||
|
|
||||||
|
**Current Gaps:**
|
||||||
|
|
||||||
|
- Generic error messages don't help users troubleshoot
|
||||||
|
- No retry logic for transient API failures
|
||||||
|
- Missing loading states in some components
|
||||||
|
|
||||||
|
**Recommendations:**
|
||||||
|
|
||||||
|
- Provide specific, actionable error messages
|
||||||
|
- Implement retry logic with exponential backoff
|
||||||
|
- Add loading skeletons for all async operations
|
||||||
|
- Show connection test results with specific failure reasons
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Create error message constants with solutions
|
||||||
|
- [ ] Add retry logic to API calls
|
||||||
|
- [ ] Implement loading skeletons
|
||||||
|
- [ ] Improve connection test error messages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Medium Priority Enhancements
|
||||||
|
|
||||||
|
### 7. Testing Coverage
|
||||||
|
|
||||||
|
**Severity:** MEDIUM
|
||||||
|
**Impact:** Confidence in changes, regression prevention
|
||||||
|
|
||||||
|
**Current Coverage:**
|
||||||
|
|
||||||
|
- Unit tests: Good coverage for API utilities
|
||||||
|
- Component tests: Some coverage, gaps exist
|
||||||
|
- E2E tests: Minimal (Playwright configured but underutilized)
|
||||||
|
|
||||||
|
**Recommendations:**
|
||||||
|
|
||||||
|
- Add E2E tests for critical user flows
|
||||||
|
- Test error scenarios and edge cases
|
||||||
|
- Add visual regression tests
|
||||||
|
- Test RBAC permission denied scenarios
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Write E2E test for complete audit workflow
|
||||||
|
- [ ] Add tests for error states
|
||||||
|
- [ ] Test exemption management flow
|
||||||
|
- [ ] Add Playwright tests to CI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. Performance Optimization
|
||||||
|
|
||||||
|
**Severity:** MEDIUM
|
||||||
|
**Impact:** Faster load times, better UX
|
||||||
|
|
||||||
|
**Opportunities:**
|
||||||
|
|
||||||
|
- Memoize expensive calculations (score computation)
|
||||||
|
- Lazy load namespace detail views
|
||||||
|
- Debounce search/filter operations
|
||||||
|
- Cache Polaris data with stale-while-revalidate
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Add React.memo to pure components
|
||||||
|
- [ ] Memoize score calculations
|
||||||
|
- [ ] Implement data caching strategy
|
||||||
|
- [ ] Profile component render times
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 9. Code Quality & Maintainability
|
||||||
|
|
||||||
|
**Severity:** MEDIUM
|
||||||
|
**Impact:** Easier maintenance, onboarding
|
||||||
|
|
||||||
|
**Recommendations:**
|
||||||
|
|
||||||
|
- Extract magic strings to constants
|
||||||
|
- Reduce component complexity
|
||||||
|
- Add JSDoc comments for public APIs
|
||||||
|
- Improve code organization
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Create constants file for check IDs
|
||||||
|
- [ ] Split large components (DashboardView, NamespaceDetailView)
|
||||||
|
- [ ] Add comments for complex logic
|
||||||
|
- [ ] Establish code review checklist
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔵 Low Priority / Future Enhancements
|
||||||
|
|
||||||
|
### 10. Documentation
|
||||||
|
|
||||||
|
**Severity:** LOW
|
||||||
|
**Impact:** Better onboarding, user adoption
|
||||||
|
|
||||||
|
**Gaps:**
|
||||||
|
|
||||||
|
- No architecture documentation
|
||||||
|
- Limited inline code comments
|
||||||
|
- Missing troubleshooting guide
|
||||||
|
- No contributor guidelines
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Create architecture diagram
|
||||||
|
- [ ] Document component hierarchy
|
||||||
|
- [ ] Add troubleshooting section to README
|
||||||
|
- [ ] Create CONTRIBUTING.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 11. CI/CD Pipeline Optimization
|
||||||
|
|
||||||
|
**Severity:** LOW
|
||||||
|
**Impact:** Faster feedback, automated releases
|
||||||
|
|
||||||
|
**Opportunities:**
|
||||||
|
|
||||||
|
- Run tests in parallel
|
||||||
|
- Cache npm dependencies
|
||||||
|
- Add automated security scanning
|
||||||
|
- Implement semantic versioning
|
||||||
|
|
||||||
|
**Action Items:**
|
||||||
|
|
||||||
|
- [ ] Parallelize test execution
|
||||||
|
- [ ] Add npm cache to GitHub Actions
|
||||||
|
- [ ] Integrate Dependabot
|
||||||
|
- [ ] Add semantic-release
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary & Prioritization
|
||||||
|
|
||||||
|
### Week 1 (Immediate)
|
||||||
|
|
||||||
|
1. ✅ Fix TypeScript compilation errors
|
||||||
|
2. ✅ Resolve production plugin loading issue
|
||||||
|
3. ✅ Fix failing DashboardView test
|
||||||
|
|
||||||
|
### Week 2 (High Priority)
|
||||||
|
|
||||||
|
4. Enhance type safety (strict mode)
|
||||||
|
2. Implement security hardening
|
||||||
|
3. Improve error handling and UX
|
||||||
|
|
||||||
|
### Week 3-4 (Medium Priority)
|
||||||
|
|
||||||
|
7. Increase test coverage to >80%
|
||||||
|
2. Optimize performance (memoization, caching)
|
||||||
|
3. Refactor for maintainability
|
||||||
|
|
||||||
|
### Ongoing (Low Priority)
|
||||||
|
|
||||||
|
10. Documentation improvements
|
||||||
|
2. CI/CD optimizations
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
**Code Quality:**
|
||||||
|
|
||||||
|
- ✅ Zero TypeScript errors
|
||||||
|
- ✅ All tests passing
|
||||||
|
- 🎯 Test coverage >80%
|
||||||
|
- 🎯 No high/critical security vulnerabilities
|
||||||
|
|
||||||
|
**Production Readiness:**
|
||||||
|
|
||||||
|
- ✅ Plugin loads successfully in Headlamp
|
||||||
|
- ✅ All features functional
|
||||||
|
- 🎯 Error rate <1%
|
||||||
|
- 🎯 Average response time <500ms
|
||||||
|
|
||||||
|
**Developer Experience:**
|
||||||
|
|
||||||
|
- ✅ Clear documentation
|
||||||
|
- ✅ Easy local setup
|
||||||
|
- 🎯 Fast CI/CD (<5 min)
|
||||||
|
- 🎯 Automated releases
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. **Immediate:** Fix TypeScript errors and verify plugin loads
|
||||||
|
2. **Short-term:** Complete Week 1-2 priorities
|
||||||
|
3. **Long-term:** Address medium and low priority items
|
||||||
|
4. **Continuous:** Monitor metrics and iterate
|
||||||
|
|
||||||
|
**Recommended First Action:**
|
||||||
|
Fix the TypeScript compilation errors in `src/index.tsx` by updating the Headlamp plugin API calls.
|
||||||
@@ -97,7 +97,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp # adjust to match your Headlamp service account
|
name: headlamp # adjust to match your Headlamp service account
|
||||||
namespace: <your-namespace>
|
namespace: kube-system # adjust to match the namespace Headlamp runs in
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -197,7 +197,7 @@ npm test
|
|||||||
npm run test:watch
|
npm run test:watch
|
||||||
|
|
||||||
# E2E tests (Playwright)
|
# E2E tests (Playwright)
|
||||||
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n <your-namespace> --duration=24h)
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system --duration=24h)
|
||||||
npm run e2e
|
npm run e2e
|
||||||
npm run e2e:headed # see browser
|
npm run e2e:headed # see browser
|
||||||
```
|
```
|
||||||
@@ -206,7 +206,7 @@ For complete testing guide including CI/CD integration, see **[docs/TESTING.md](
|
|||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```text
|
||||||
src/
|
src/
|
||||||
index.tsx -- Entry point. Registers sidebar entries, routes, and error boundaries.
|
index.tsx -- Entry point. Registers sidebar entries, routes, and error boundaries.
|
||||||
test-utils.tsx -- Shared test fixtures (makeResult, makeAuditData).
|
test-utils.tsx -- Shared test fixtures (makeResult, makeAuditData).
|
||||||
@@ -236,7 +236,7 @@ GET /api/v1/namespaces/polaris/services/polaris-dashboard/proxy/results.json
|
|||||||
|
|
||||||
This endpoint is served by the `polaris-dashboard` ClusterIP service, which is created by the Polaris Helm chart when `dashboard.enabled: true`. The JSON response matches Polaris's `AuditData` schema (`pkg/validator/output.go`):
|
This endpoint is served by the `polaris-dashboard` ClusterIP service, which is created by the Polaris Helm chart when `dashboard.enabled: true`. The JSON response matches Polaris's `AuditData` schema (`pkg/validator/output.go`):
|
||||||
|
|
||||||
```
|
```text
|
||||||
AuditData
|
AuditData
|
||||||
ClusterInfo -- nodes, pods, namespaces, controllers
|
ClusterInfo -- nodes, pods, namespaces, controllers
|
||||||
Results[] -- per-workload results
|
Results[] -- per-workload results
|
||||||
|
|||||||
+14
-5
@@ -17,7 +17,7 @@ The plugin performs **only read operations** via the Kubernetes API server's ser
|
|||||||
|
|
||||||
### Data Flow
|
### Data Flow
|
||||||
|
|
||||||
```
|
```text
|
||||||
User Browser
|
User Browser
|
||||||
↓ (HTTPS)
|
↓ (HTTPS)
|
||||||
Headlamp Pod
|
Headlamp Pod
|
||||||
@@ -71,7 +71,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -149,9 +149,10 @@ spec:
|
|||||||
|
|
||||||
### Service Account (Default)
|
### Service Account (Default)
|
||||||
|
|
||||||
Headlamp runs with a dedicated service account (`headlamp` in the namespace where Headlamp is installed). All users share the same permissions defined by this service account's RBAC bindings.
|
Headlamp runs with a dedicated service account (`headlamp` in `kube-system`). All users share the same permissions defined by this service account's RBAC bindings.
|
||||||
|
|
||||||
**Security Considerations:**
|
**Security Considerations:**
|
||||||
|
|
||||||
- All users have identical access to the plugin
|
- All users have identical access to the plugin
|
||||||
- Suitable for trusted internal environments
|
- Suitable for trusted internal environments
|
||||||
- Simpler RBAC management
|
- Simpler RBAC management
|
||||||
@@ -161,6 +162,7 @@ Headlamp runs with a dedicated service account (`headlamp` in the namespace wher
|
|||||||
Headlamp can be configured for OIDC authentication, where each user provides their own bearer token. RBAC is enforced per-user.
|
Headlamp can be configured for OIDC authentication, where each user provides their own bearer token. RBAC is enforced per-user.
|
||||||
|
|
||||||
**Security Considerations:**
|
**Security Considerations:**
|
||||||
|
|
||||||
- Fine-grained access control per user
|
- Fine-grained access control per user
|
||||||
- Users without the `polaris-proxy-reader` role will see 403 errors
|
- Users without the `polaris-proxy-reader` role will see 403 errors
|
||||||
- Requires OIDC provider integration
|
- Requires OIDC provider integration
|
||||||
@@ -198,10 +200,12 @@ If you discover a security vulnerability in this plugin, please report it via:
|
|||||||
2. **Email**: Create a GitHub issue and mark it as "security" if advisories are not available
|
2. **Email**: Create a GitHub issue and mark it as "security" if advisories are not available
|
||||||
|
|
||||||
**Please do not:**
|
**Please do not:**
|
||||||
|
|
||||||
- Open public GitHub issues for security vulnerabilities
|
- Open public GitHub issues for security vulnerabilities
|
||||||
- Disclose vulnerabilities publicly before a fix is available
|
- Disclose vulnerabilities publicly before a fix is available
|
||||||
|
|
||||||
**Response Timeline:**
|
**Response Timeline:**
|
||||||
|
|
||||||
- **Acknowledgment**: Within 48 hours
|
- **Acknowledgment**: Within 48 hours
|
||||||
- **Initial Assessment**: Within 1 week
|
- **Initial Assessment**: Within 1 week
|
||||||
- **Fix Timeline**: Depends on severity (critical: 1-2 weeks, high: 2-4 weeks, medium/low: next release cycle)
|
- **Fix Timeline**: Depends on severity (critical: 1-2 weeks, high: 2-4 weeks, medium/low: next release cycle)
|
||||||
@@ -211,8 +215,9 @@ If you discover a security vulnerability in this plugin, please report it via:
|
|||||||
### Dependency Scanning
|
### Dependency Scanning
|
||||||
|
|
||||||
The project uses:
|
The project uses:
|
||||||
|
|
||||||
- **npm audit**: Runs automatically during `npm install`
|
- **npm audit**: Runs automatically during `npm install`
|
||||||
- **Renovate**: Automated dependency updates via Mend Renovate (org-wide configured)
|
- **Dependabot**: GitHub Dependabot monitors dependencies and creates PRs for updates
|
||||||
- **GitHub Actions**: CI workflow runs `npm audit` on every commit
|
- **GitHub Actions**: CI workflow runs `npm audit` on every commit
|
||||||
|
|
||||||
### Updating Dependencies
|
### Updating Dependencies
|
||||||
@@ -262,6 +267,7 @@ The plugin's security posture depends on your cluster's security:
|
|||||||
**Cause**: User or service account lacks `services/proxy` permission on `polaris-dashboard`
|
**Cause**: User or service account lacks `services/proxy` permission on `polaris-dashboard`
|
||||||
|
|
||||||
**Resolution**:
|
**Resolution**:
|
||||||
|
|
||||||
1. Verify RoleBinding exists in `polaris` namespace
|
1. Verify RoleBinding exists in `polaris` namespace
|
||||||
2. Check RoleBinding references correct subject (service account, group, or user)
|
2. Check RoleBinding references correct subject (service account, group, or user)
|
||||||
3. Confirm Role includes `resourceNames: ["polaris-dashboard"]`
|
3. Confirm Role includes `resourceNames: ["polaris-dashboard"]`
|
||||||
@@ -273,11 +279,13 @@ The plugin's security posture depends on your cluster's security:
|
|||||||
**Question**: Can I expose Polaris dashboard via Ingress instead of using service proxy?
|
**Question**: Can I expose Polaris dashboard via Ingress instead of using service proxy?
|
||||||
|
|
||||||
**Recommendation**: **Avoid exposing Polaris dashboard externally**. The service proxy approach:
|
**Recommendation**: **Avoid exposing Polaris dashboard externally**. The service proxy approach:
|
||||||
|
|
||||||
- Enforces Kubernetes RBAC on every request
|
- Enforces Kubernetes RBAC on every request
|
||||||
- Avoids exposing internal services to the internet
|
- Avoids exposing internal services to the internet
|
||||||
- Prevents authentication bypass attacks
|
- Prevents authentication bypass attacks
|
||||||
|
|
||||||
If you must expose Polaris externally:
|
If you must expose Polaris externally:
|
||||||
|
|
||||||
- Use OAuth2 proxy or similar authentication layer
|
- Use OAuth2 proxy or similar authentication layer
|
||||||
- Configure NetworkPolicies to restrict access
|
- Configure NetworkPolicies to restrict access
|
||||||
- Enable TLS with valid certificates
|
- Enable TLS with valid certificates
|
||||||
@@ -304,6 +312,7 @@ Users not in `team-a` will receive 403 errors when accessing the plugin, prevent
|
|||||||
### Data Residency
|
### Data Residency
|
||||||
|
|
||||||
All data remains within your Kubernetes cluster. The plugin does not:
|
All data remains within your Kubernetes cluster. The plugin does not:
|
||||||
|
|
||||||
- Send data to external services
|
- Send data to external services
|
||||||
- Store data in browser localStorage (except refresh interval preference)
|
- Store data in browser localStorage (except refresh interval preference)
|
||||||
- Use third-party analytics or tracking
|
- Use third-party analytics or tracking
|
||||||
@@ -317,7 +326,7 @@ All service proxy requests are logged in Kubernetes API audit logs (if enabled):
|
|||||||
"verb": "get",
|
"verb": "get",
|
||||||
"requestURI": "/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json",
|
"requestURI": "/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json",
|
||||||
"user": {
|
"user": {
|
||||||
"username": "system:serviceaccount:<your-namespace>:headlamp",
|
"username": "system:serviceaccount:kube-system:headlamp",
|
||||||
"groups": ["system:serviceaccounts", "system:authenticated"]
|
"groups": ["system:serviceaccounts", "system:authenticated"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-48
@@ -1,28 +1,30 @@
|
|||||||
version: 1.0.1
|
version: "1.0.0"
|
||||||
name: headlamp-polaris
|
name: headlamp-polaris
|
||||||
displayName: Polaris
|
displayName: Polaris
|
||||||
createdAt: '2026-05-20T00:00:00Z'
|
createdAt: "2026-02-05T19:00:00Z"
|
||||||
description: Surfaces Fairwinds Polaris audit results inside the Headlamp UI. Shows
|
description: >-
|
||||||
cluster score, check summary, and per-namespace drill-downs with per-resource pass/warning/danger
|
Surfaces Fairwinds Polaris audit results inside the Headlamp UI.
|
||||||
breakdowns. Data is fetched read-only via the Kubernetes service proxy to the Polaris
|
Shows cluster score, check summary, and per-namespace drill-downs
|
||||||
dashboard. Requires a Role granting `get` on `services/proxy` for the `polaris-dashboard`
|
with per-resource pass/warning/danger breakdowns. Data is fetched
|
||||||
service in the `polaris` namespace.
|
read-only via the Kubernetes service proxy to the Polaris dashboard.
|
||||||
|
Requires a Role granting `get` on `services/proxy` for the
|
||||||
|
`polaris-dashboard` service in the `polaris` namespace.
|
||||||
license: Apache-2.0
|
license: Apache-2.0
|
||||||
homeURL: https://github.com/privilegedescalation/headlamp-polaris-plugin
|
homeURL: "https://github.com/privilegedescalation/headlamp-polaris-plugin"
|
||||||
appVersion: 10.1.6
|
appVersion: "10.1.6"
|
||||||
category: security
|
category: security
|
||||||
keywords:
|
keywords:
|
||||||
- polaris
|
- polaris
|
||||||
- fairwinds
|
- fairwinds
|
||||||
- security
|
- security
|
||||||
- audit
|
- audit
|
||||||
- headlamp
|
- headlamp
|
||||||
- kubernetes
|
- kubernetes
|
||||||
links:
|
links:
|
||||||
- name: Source
|
- name: Source
|
||||||
url: https://github.com/privilegedescalation/headlamp-polaris-plugin
|
url: "https://github.com/privilegedescalation/headlamp-polaris-plugin"
|
||||||
- name: Polaris
|
- name: Polaris
|
||||||
url: https://polaris.docs.fairwinds.com/
|
url: "https://polaris.docs.fairwinds.com/"
|
||||||
install: |
|
install: |
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -48,34 +50,27 @@ install: |
|
|||||||
|
|
||||||
For more information, see the [README](https://github.com/privilegedescalation/headlamp-polaris-plugin/blob/main/README.md).
|
For more information, see the [README](https://github.com/privilegedescalation/headlamp-polaris-plugin/blob/main/README.md).
|
||||||
changes:
|
changes:
|
||||||
- kind: security
|
- kind: security
|
||||||
description: Patched 8 npm audit vulnerabilities via pnpm.overrides
|
description: Patched 8 npm audit vulnerabilities via pnpm.overrides
|
||||||
- kind: added
|
- kind: added
|
||||||
description: Dual-approval required CI check — PRs must be approved by both CTO
|
description: Dual-approval required CI check — PRs must be approved by both CTO and QA before merge
|
||||||
and QA before merge
|
- kind: added
|
||||||
- kind: added
|
description: ExemptionManager test suite — full coverage of annotation-based exemption flows
|
||||||
description: ExemptionManager test suite — full coverage of annotation-based exemption
|
- kind: fixed
|
||||||
flows
|
description: E2E infrastructure overhauled — ConfigMap volume mount replaces Dockerfile-based approach, tests run in privilegedescalation-dev namespace
|
||||||
- kind: fixed
|
- kind: fixed
|
||||||
description: E2E infrastructure overhauled — ConfigMap volume mount replaces Dockerfile-based
|
description: E2E workflow uses token auth and waits for HTTP reachability before running tests
|
||||||
approach, tests run in privilegedescalation-dev namespace
|
- kind: fixed
|
||||||
- kind: fixed
|
description: Added explicit direct devDependencies (typescript, eslint, prettier, @headlamp-k8s/eslint-config) to prevent phantom dep failures
|
||||||
description: E2E workflow uses token auth and waits for HTTP reachability before
|
- kind: changed
|
||||||
running tests
|
description: pnpm version pinned via packageManager field; GitHub Actions SHA-pinned via Renovate pinDigests
|
||||||
- kind: fixed
|
- kind: changed
|
||||||
description: Added explicit direct devDependencies (typescript, eslint, prettier,
|
description: v1.0.0 stable release — plugin API (routes, sidebar, settings schema, app bar action) is stable and will not change without a major version bump
|
||||||
@headlamp-k8s/eslint-config) to prevent phantom dep failures
|
|
||||||
- kind: changed
|
|
||||||
description: pnpm version pinned via packageManager field; GitHub Actions SHA-pinned
|
|
||||||
via Renovate pinDigests
|
|
||||||
- kind: changed
|
|
||||||
description: v1.0.0 stable release — plugin API (routes, sidebar, settings schema,
|
|
||||||
app bar action) is stable and will not change without a major version bump
|
|
||||||
maintainers:
|
maintainers:
|
||||||
- name: privilegedescalation
|
- name: privilegedescalation
|
||||||
email: chris@farhood.org
|
email: "chris@farhood.org"
|
||||||
annotations:
|
annotations:
|
||||||
headlamp/plugin/archive-url: https://git.farh.net/privilegedescalation/headlamp-polaris-plugin/releases/download/v1.0.1/headlamp-polaris-1.0.1.tar.gz
|
headlamp/plugin/archive-url: "https://github.com/privilegedescalation/headlamp-polaris-plugin/releases/download/v1.0.0/headlamp-polaris-1.0.0.tar.gz"
|
||||||
headlamp/plugin/version-compat: '>=0.26'
|
headlamp/plugin/version-compat: ">=0.26"
|
||||||
headlamp/plugin/archive-checksum: sha256:1e05d079c7032cf55ebde85e116cb65b686d207f4b6a3b0f716f0af93f933e7e
|
headlamp/plugin/archive-checksum: sha256:a165e871b40f11a44950aa9f10eb7f7883276f749026ae7a4f886278ecd9bd7d
|
||||||
headlamp/plugin/distro-compat: in-cluster,web,desktop
|
headlamp/plugin/distro-compat: "in-cluster,web,desktop"
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
// Allowlist for inherited dev-dependency CVEs from @kinvolk/headlamp-plugin
|
|
||||||
// CTO decision (PRI-854): these high-severity vulns are dev/build-time only,
|
|
||||||
// trace to @kinvolk/headlamp-plugin transitive deps (Picomatch, Vite, lodash),
|
|
||||||
// and do NOT ship in production plugin artifacts.
|
|
||||||
"allowlist": [
|
|
||||||
{
|
|
||||||
"id": "GHSA-hhpm-516h-p3p6",
|
|
||||||
"reason": "Picomatch ReDoS: devDependency only, does not ship in production plugin bundle"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "GHSA-36xf-7xpp-53w5",
|
|
||||||
"reason": "Vite arbitrary file read: devDependency only, does not ship in production plugin bundle"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "GHSA-jf8v-p3pp-93qh",
|
|
||||||
"reason": "lodash code injection via _.template: devDependency only, does not ship in production plugin bundle"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Headlamp Plugin Loading Issue - Root Cause and Fix
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Headlamp v0.39.0 was not loading plugins installed via the plugin manager. Plugins appeared in Settings → Plugins but:
|
||||||
|
|
||||||
|
- No sidebar entries appeared
|
||||||
|
- No plugin settings were available
|
||||||
|
- Plugin JavaScript was not being executed in the browser
|
||||||
|
|
||||||
|
## Root Cause
|
||||||
|
|
||||||
|
When `config.watchPlugins: true` (the default), Headlamp treats catalog-managed plugins in `/headlamp/plugins/` as "development directory" plugins. This causes:
|
||||||
|
|
||||||
|
- Backend serves plugin metadata correctly
|
||||||
|
- Backend logs show "Treating catalog-installed plugin in development directory as user plugin"
|
||||||
|
- **Frontend does NOT execute the plugin JavaScript**
|
||||||
|
- Plugin registrations (`registerSidebarEntry`, `registerRoute`, etc.) never happen
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Set `config.watchPlugins: false` in the Headlamp HelmRelease values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
spec:
|
||||||
|
values:
|
||||||
|
config:
|
||||||
|
watchPlugins: false
|
||||||
|
pluginsManager:
|
||||||
|
enabled: true
|
||||||
|
configContent: |
|
||||||
|
plugins:
|
||||||
|
- name: polaris
|
||||||
|
source: https://artifacthub.io/packages/headlamp/polaris/headlamp-polaris-plugin
|
||||||
|
# ... other plugins
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why This Works
|
||||||
|
|
||||||
|
With `watchPlugins: false`:
|
||||||
|
|
||||||
|
- Headlamp no longer treats catalog-managed plugins as "development" plugins
|
||||||
|
- Frontend properly loads and executes plugin JavaScript on startup
|
||||||
|
- Plugin registrations happen correctly
|
||||||
|
- All plugin features (sidebar, routes, settings, etc.) work as expected
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
After applying this fix:
|
||||||
|
|
||||||
|
1. Verify plugins are installed: `kubectl logs -n kube-system <headlamp-pod> -c headlamp-plugin`
|
||||||
|
2. Verify watchPlugins is false: `kubectl logs -n kube-system <headlamp-pod> -c headlamp | grep "Watch Plugins"`
|
||||||
|
3. Hard refresh browser (Cmd+Shift+R / Ctrl+Shift+F5) to clear cached JavaScript
|
||||||
|
4. Verify plugin sidebar entries appear
|
||||||
|
5. Verify plugin functionality works
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
- This appears to be a bug/limitation in Headlamp v0.39.0
|
||||||
|
- The `watchPlugins` feature is intended for development scenarios where plugins are being actively modified
|
||||||
|
- For production deployments with catalog-managed plugins, `watchPlugins: false` is the correct configuration
|
||||||
|
- Once plugins are loaded, subsequent restarts or updates work correctly as long as `watchPlugins` remains false
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Headlamp Helm Chart: <https://github.com/headlamp-k8s/headlamp/tree/main/charts/headlamp>
|
||||||
|
- Plugin Manager: <https://github.com/headlamp-k8s/headlamp/tree/main/plugins/headlamp-plugin>
|
||||||
|
- Issue discovered: 2026-02-11
|
||||||
|
- Fix applied: 2026-02-12
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
---
|
||||||
|
# RBAC for the GitHub Actions CI runner to manage the E2E Headlamp instance.
|
||||||
|
# CI-only test fixture — NOT for production use.
|
||||||
|
#
|
||||||
|
# Grants the ARC runner service account permissions in the privilegedescalation-dev
|
||||||
|
# namespace to deploy and tear down a dedicated Headlamp instance via Helm.
|
||||||
|
# E2E resources run in `privilegedescalation-dev` — nothing persists beyond a test run.
|
||||||
|
#
|
||||||
|
# Plugin is loaded via ConfigMap volume mount — no custom Docker images.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# kubectl apply -f deployment/e2e-ci-runner-rbac.yaml
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: e2e-ci-runner
|
||||||
|
namespace: privilegedescalation-dev
|
||||||
|
rules:
|
||||||
|
# Helm needs to manage these resources for the Headlamp chart
|
||||||
|
- apiGroups: ["apps"]
|
||||||
|
resources: ["deployments"]
|
||||||
|
verbs: ["get", "list", "create", "update", "patch", "delete", "watch"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["services", "serviceaccounts", "configmaps", "secrets"]
|
||||||
|
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["pods"]
|
||||||
|
verbs: ["get", "list", "watch"]
|
||||||
|
# Token creation for E2E test auth
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["serviceaccounts/token"]
|
||||||
|
verbs: ["create"]
|
||||||
|
# RBAC pre-flight check: verify polaris namespace has proxy-reader Role + RoleBinding
|
||||||
|
# before running E2E tests. Required by the "RBAC pre-flight check" workflow step.
|
||||||
|
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||||
|
resources: ["roles", "rolebindings"]
|
||||||
|
verbs: ["get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: e2e-ci-runner-polaris-reader
|
||||||
|
namespace: polaris
|
||||||
|
rules:
|
||||||
|
- apiGroups: ["rbac.authorization.k8s.io"]
|
||||||
|
resources: ["roles", "rolebindings"]
|
||||||
|
verbs: ["get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: e2e-ci-runner-polaris-reader-binding
|
||||||
|
namespace: polaris
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: runners-privilegedescalation-gha-rs-no-permission
|
||||||
|
namespace: arc-runners
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: e2e-ci-runner-polaris-reader
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: e2e-ci-runner-binding
|
||||||
|
namespace: privilegedescalation-dev
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: runners-privilegedescalation-gha-rs-no-permission
|
||||||
|
namespace: arc-runners
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: e2e-ci-runner
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# RBAC to allow authenticated users to proxy to the Polaris dashboard service.
|
||||||
|
# The polaris plugin reads audit data via the Kubernetes service proxy:
|
||||||
|
# /api/v1/namespaces/polaris/services/http:polaris-dashboard:80/proxy/results.json
|
||||||
|
# Without this Role + RoleBinding, users get a 403 when Headlamp proxies the request.
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: polaris-dashboard-proxy-reader
|
||||||
|
namespace: polaris
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["services/proxy"]
|
||||||
|
resourceNames: ["polaris-dashboard", "http:polaris-dashboard:80"]
|
||||||
|
verbs: ["get"]
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: RoleBinding
|
||||||
|
metadata:
|
||||||
|
name: polaris-dashboard-proxy-reader
|
||||||
|
namespace: polaris
|
||||||
|
subjects:
|
||||||
|
- kind: Group
|
||||||
|
name: system:authenticated
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
roleRef:
|
||||||
|
kind: Role
|
||||||
|
name: polaris-dashboard-proxy-reader
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
+2
-2
@@ -33,7 +33,7 @@ kubectl -n polaris get svc polaris-dashboard
|
|||||||
kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json | jq .PolarisOutputVersion
|
kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json | jq .PolarisOutputVersion
|
||||||
|
|
||||||
# Verify Headlamp is deployed
|
# Verify Headlamp is deployed
|
||||||
kubectl -n <your-namespace> get pods -l app.kubernetes.io/name=headlamp
|
kubectl -n kube-system get pods -l app.kubernetes.io/name=headlamp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation Methods
|
## Installation Methods
|
||||||
@@ -59,7 +59,7 @@ kubectl -n <your-namespace> get pods -l app.kubernetes.io/name=headlamp
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm upgrade --install headlamp headlamp/headlamp \
|
helm upgrade --install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml
|
--values headlamp-values.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ docs/
|
|||||||
|
|
||||||
### 3. CHANGELOG.md Standard
|
### 3. CHANGELOG.md Standard
|
||||||
|
|
||||||
**Format**: Keep a Changelog (https://keepachangelog.com/)
|
**Format**: Keep a Changelog (<https://keepachangelog.com/>)
|
||||||
|
|
||||||
**Structure**:
|
**Structure**:
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -268,9 +268,10 @@ npm run e2e
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create token
|
# Create token
|
||||||
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n <your-namespace> --duration=24h)
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system --duration=24h)
|
||||||
|
|
||||||
kubectl port-forward -n <your-namespace> svc/headlamp 4466:80
|
# Port-forward for local testing
|
||||||
|
kubectl port-forward -n kube-system svc/headlamp 4466:80
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
HEADLAMP_URL=http://localhost:4466 npm run e2e
|
HEADLAMP_URL=http://localhost:4466 npm run e2e
|
||||||
|
|||||||
+17
-16
@@ -33,7 +33,7 @@ This guide covers common issues encountered when using the Headlamp Polaris Plug
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View Headlamp pod logs (plugin sidecar)
|
# View Headlamp pod logs (plugin sidecar)
|
||||||
kubectl logs -n <your-namespace> deployment/headlamp -c headlamp-plugin
|
kubectl logs -n kube-system deployment/headlamp -c headlamp-plugin
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# Installing plugin from https://github.com/.../headlamp-polaris-plugin-X.Y.Z.tar.gz
|
# Installing plugin from https://github.com/.../headlamp-polaris-plugin-X.Y.Z.tar.gz
|
||||||
@@ -43,7 +43,7 @@ kubectl logs -n <your-namespace> deployment/headlamp -c headlamp-plugin
|
|||||||
**Verify plugin files exist**:
|
**Verify plugin files exist**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl exec -n <your-namespace> deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/
|
kubectl exec -n kube-system deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/
|
||||||
# Should show: headlamp-polaris-plugin/
|
# Should show: headlamp-polaris-plugin/
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ Expected subjects:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
```
|
```
|
||||||
|
|
||||||
For OIDC mode:
|
For OIDC mode:
|
||||||
@@ -154,7 +154,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -169,7 +169,7 @@ Service account mode:
|
|||||||
```bash
|
```bash
|
||||||
# Impersonate Headlamp service account
|
# Impersonate Headlamp service account
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
--resource-name=polaris-dashboard \
|
--resource-name=polaris-dashboard \
|
||||||
-n polaris
|
-n polaris
|
||||||
# Expected: yes
|
# Expected: yes
|
||||||
@@ -189,7 +189,7 @@ kubectl auth can-i get services/proxy \
|
|||||||
After applying RBAC changes:
|
After applying RBAC changes:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl rollout restart deployment headlamp -n <your-namespace>
|
kubectl rollout restart deployment headlamp -n kube-system
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -490,7 +490,7 @@ Run this script to test all RBAC components:
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
NS="polaris"
|
NS="polaris"
|
||||||
SA="headlamp"
|
SA="headlamp"
|
||||||
SA_NS="<your-namespace>"
|
SA_NS="kube-system"
|
||||||
|
|
||||||
echo "=== Testing RBAC for Polaris Plugin ==="
|
echo "=== Testing RBAC for Polaris Plugin ==="
|
||||||
|
|
||||||
@@ -529,8 +529,8 @@ echo "=== Test complete ==="
|
|||||||
Test connectivity from Headlamp to Polaris:
|
Test connectivity from Headlamp to Polaris:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create debug pod in headlamp namespace
|
# Create debug pod in kube-system namespace
|
||||||
kubectl run netdebug -n <your-namespace> --rm -it --image=nicolaka/netshoot -- bash
|
kubectl run netdebug -n kube-system --rm -it --image=nicolaka/netshoot -- bash
|
||||||
|
|
||||||
# Inside pod, test DNS and HTTP
|
# Inside pod, test DNS and HTTP
|
||||||
nslookup polaris-dashboard.polaris.svc.cluster.local
|
nslookup polaris-dashboard.polaris.svc.cluster.local
|
||||||
@@ -545,11 +545,11 @@ If you have audit logging enabled, check for denied requests:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View recent audit logs (location varies by cluster)
|
# View recent audit logs (location varies by cluster)
|
||||||
kubectl logs -n <your-namespace> kube-apiserver-* | grep polaris-dashboard
|
kubectl logs -n kube-system kube-apiserver-* | grep polaris-dashboard
|
||||||
|
|
||||||
# Look for lines with:
|
# Look for lines with:
|
||||||
# "reason": "Forbidden"
|
# "reason": "Forbidden"
|
||||||
# "user": "system:serviceaccount:<your-namespace>:headlamp"
|
# "user": "system:serviceaccount:kube-system:headlamp"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -567,7 +567,7 @@ kubectl logs -n <your-namespace> kube-apiserver-* | grep polaris-dashboard
|
|||||||
**Check sidecar logs**:
|
**Check sidecar logs**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl logs -n <your-namespace> deployment/headlamp -c headlamp-plugin
|
kubectl logs -n kube-system deployment/headlamp -c headlamp-plugin
|
||||||
```
|
```
|
||||||
|
|
||||||
**Common errors**:
|
**Common errors**:
|
||||||
@@ -591,7 +591,7 @@ Error: 404 Not Found
|
|||||||
**Solution**: Verify `archive-url` in plugin config matches GitHub release:
|
**Solution**: Verify `archive-url` in plugin config matches GitHub release:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get configmap headlamp-plugin-config -n <your-namespace> -o yaml
|
kubectl get configmap headlamp-plugin-config -n kube-system -o yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected format:
|
Expected format:
|
||||||
@@ -677,13 +677,13 @@ If none of these solutions work, gather debugging information and open an issue:
|
|||||||
1. **Version Information**:
|
1. **Version Information**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get pods -n <your-namespace> -l app.kubernetes.io/name=headlamp -o yaml | grep image:
|
kubectl get pods -n kube-system -l app.kubernetes.io/name=headlamp -o yaml | grep image:
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Plugin Version**:
|
2. **Plugin Version**:
|
||||||
|
|
||||||
- Check Settings → Plugins in Headlamp UI
|
- Check Settings → Plugins in Headlamp UI
|
||||||
- Or: `kubectl exec -n <your-namespace> deployment/headlamp -c headlamp -- cat /headlamp/plugins/headlamp-polaris-plugin/package.json`
|
- Or: `kubectl exec -n kube-system deployment/headlamp -c headlamp -- cat /headlamp/plugins/headlamp-polaris-plugin/package.json`
|
||||||
|
|
||||||
3. **Browser Console Output**:
|
3. **Browser Console Output**:
|
||||||
|
|
||||||
@@ -698,11 +698,12 @@ If none of these solutions work, gather debugging information and open an issue:
|
|||||||
5. **Pod Logs**:
|
5. **Pod Logs**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl logs -n <your-namespace> deployment/headlamp -c headlamp --tail=100
|
kubectl logs -n kube-system deployment/headlamp -c headlamp --tail=100
|
||||||
kubectl logs -n polaris deployment/polaris-dashboard --tail=100
|
kubectl logs -n polaris deployment/polaris-dashboard --tail=100
|
||||||
```
|
```
|
||||||
|
|
||||||
6. **RBAC Configuration**:
|
6. **RBAC Configuration**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get role,rolebinding -n polaris
|
kubectl get role,rolebinding -n polaris
|
||||||
```
|
```
|
||||||
|
|||||||
+20
-20
@@ -41,11 +41,11 @@ pluginsManager:
|
|||||||
```bash
|
```bash
|
||||||
# Install Headlamp
|
# Install Headlamp
|
||||||
helm install headlamp headlamp/headlamp \
|
helm install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml
|
--values headlamp-values.yaml
|
||||||
|
|
||||||
# Wait for deployment
|
# Wait for deployment
|
||||||
kubectl -n <your-namespace> wait --for=condition=available deployment/headlamp --timeout=300s
|
kubectl -n kube-system wait --for=condition=available deployment/headlamp --timeout=300s
|
||||||
```
|
```
|
||||||
|
|
||||||
After installation, install the plugin via Headlamp UI (**Settings → Plugins → Catalog**).
|
After installation, install the plugin via Headlamp UI (**Settings → Plugins → Catalog**).
|
||||||
@@ -131,7 +131,7 @@ Deploy:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm upgrade --install headlamp headlamp/headlamp \
|
helm upgrade --install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml \
|
--values headlamp-values.yaml \
|
||||||
--wait \
|
--wait \
|
||||||
--timeout 5m
|
--timeout 5m
|
||||||
@@ -177,7 +177,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp-plugin-config
|
name: headlamp-plugin-config
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
data:
|
data:
|
||||||
plugin.yml: |
|
plugin.yml: |
|
||||||
- name: headlamp-polaris-plugin
|
- name: headlamp-polaris-plugin
|
||||||
@@ -191,7 +191,7 @@ Apply ConfigMap then deploy Headlamp:
|
|||||||
kubectl apply -f headlamp-plugin-config.yaml
|
kubectl apply -f headlamp-plugin-config.yaml
|
||||||
|
|
||||||
helm upgrade --install headlamp headlamp/headlamp \
|
helm upgrade --install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml
|
--values headlamp-values.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ apiVersion: helm.toolkit.fluxcd.io/v2
|
|||||||
kind: HelmRelease
|
kind: HelmRelease
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
spec:
|
spec:
|
||||||
interval: 30m
|
interval: 30m
|
||||||
chart:
|
chart:
|
||||||
@@ -300,7 +300,7 @@ kubectl apply -f helmrepository.yaml
|
|||||||
kubectl apply -f helmrelease.yaml
|
kubectl apply -f helmrelease.yaml
|
||||||
|
|
||||||
# Watch deployment
|
# Watch deployment
|
||||||
flux get helmreleases -n <your-namespace> --watch
|
flux get helmreleases -n kube-system --watch
|
||||||
```
|
```
|
||||||
|
|
||||||
## RBAC Configuration
|
## RBAC Configuration
|
||||||
@@ -329,7 +329,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -349,7 +349,7 @@ helm repo update
|
|||||||
|
|
||||||
# Upgrade Headlamp (preserves plugin configuration)
|
# Upgrade Headlamp (preserves plugin configuration)
|
||||||
helm upgrade headlamp headlamp/headlamp \
|
helm upgrade headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml \
|
--values headlamp-values.yaml \
|
||||||
--wait
|
--wait
|
||||||
```
|
```
|
||||||
@@ -365,15 +365,15 @@ helm upgrade headlamp headlamp/headlamp \
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Update ConfigMap with new version
|
# Update ConfigMap with new version
|
||||||
kubectl -n <your-namespace> edit configmap headlamp-plugin-config
|
kubectl -n kube-system edit configmap headlamp-plugin-config
|
||||||
|
|
||||||
# Update version and URL:
|
# Update version and URL:
|
||||||
# version: 0.3.6
|
# version: 0.3.6
|
||||||
# url: https://github.com/.../v0.3.6/polaris-0.3.10.tar.gz
|
# url: https://github.com/.../v0.3.6/polaris-0.3.10.tar.gz
|
||||||
|
|
||||||
# Restart deployment to trigger init container
|
# Restart deployment to trigger init container
|
||||||
kubectl -n <your-namespace> rollout restart deployment/headlamp
|
kubectl -n kube-system rollout restart deployment/headlamp
|
||||||
kubectl -n <your-namespace> rollout status deployment/headlamp
|
kubectl -n kube-system rollout status deployment/headlamp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
@@ -382,25 +382,25 @@ kubectl -n <your-namespace> rollout status deployment/headlamp
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check Headlamp values
|
# Check Headlamp values
|
||||||
helm get values headlamp -n <your-namespace>
|
helm get values headlamp -n kube-system
|
||||||
|
|
||||||
# Verify plugin files exist
|
# Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec deployment/headlamp -c headlamp -- \
|
kubectl -n kube-system exec deployment/headlamp -c headlamp -- \
|
||||||
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
|
|
||||||
# If missing, reinstall plugin via UI or check init container logs
|
# If missing, reinstall plugin via UI or check init container logs
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp -c install-polaris-plugin
|
kubectl -n kube-system logs deployment/headlamp -c install-polaris-plugin
|
||||||
```
|
```
|
||||||
|
|
||||||
### Helm Release Stuck
|
### Helm Release Stuck
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check Helm release status
|
# Check Helm release status
|
||||||
helm list -n <your-namespace>
|
helm list -n kube-system
|
||||||
|
|
||||||
# If stuck, force upgrade
|
# If stuck, force upgrade
|
||||||
helm upgrade headlamp headlamp/headlamp \
|
helm upgrade headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml \
|
--values headlamp-values.yaml \
|
||||||
--force \
|
--force \
|
||||||
--wait
|
--wait
|
||||||
@@ -410,13 +410,13 @@ helm upgrade headlamp headlamp/headlamp \
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check HelmRelease status
|
# Check HelmRelease status
|
||||||
flux get helmreleases -n <your-namespace>
|
flux get helmreleases -n kube-system
|
||||||
|
|
||||||
# Check events
|
# Check events
|
||||||
kubectl -n <your-namespace> describe helmrelease headlamp
|
kubectl -n kube-system describe helmrelease headlamp
|
||||||
|
|
||||||
# Force reconciliation
|
# Force reconciliation
|
||||||
flux reconcile helmrelease headlamp -n <your-namespace>
|
flux reconcile helmrelease headlamp -n kube-system
|
||||||
```
|
```
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -71,7 +71,7 @@ kubectl -n polaris get rolebinding headlamp-polaris-proxy
|
|||||||
|
|
||||||
# Test permission
|
# Test permission
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp-plugin-config
|
name: headlamp-plugin-config
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: headlamp
|
app.kubernetes.io/name: headlamp
|
||||||
app.kubernetes.io/component: plugin-config
|
app.kubernetes.io/component: plugin-config
|
||||||
@@ -109,7 +109,7 @@ apiVersion: apps/v1
|
|||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: headlamp
|
app.kubernetes.io/name: headlamp
|
||||||
spec:
|
spec:
|
||||||
@@ -194,7 +194,7 @@ apiVersion: v1
|
|||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: headlamp
|
app.kubernetes.io/name: headlamp
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ apiVersion: v1
|
|||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
labels:
|
labels:
|
||||||
app.kubernetes.io/name: headlamp
|
app.kubernetes.io/name: headlamp
|
||||||
spec:
|
spec:
|
||||||
@@ -235,27 +235,27 @@ kubectl apply -f headlamp-service.yaml
|
|||||||
kubectl apply -f headlamp-serviceaccount.yaml
|
kubectl apply -f headlamp-serviceaccount.yaml
|
||||||
|
|
||||||
# Wait for deployment to be ready
|
# Wait for deployment to be ready
|
||||||
kubectl -n <your-namespace> wait --for=condition=available deployment/headlamp --timeout=300s
|
kubectl -n kube-system wait --for=condition=available deployment/headlamp --timeout=300s
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Verify Deployment
|
### 2. Verify Deployment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check pods are running
|
# Check pods are running
|
||||||
kubectl -n <your-namespace> get pods -l app.kubernetes.io/name=headlamp
|
kubectl -n kube-system get pods -l app.kubernetes.io/name=headlamp
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# NAME READY STATUS RESTARTS AGE
|
# NAME READY STATUS RESTARTS AGE
|
||||||
# headlamp-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
|
# headlamp-xxxxxxxxxx-xxxxx 1/1 Running 0 2m
|
||||||
|
|
||||||
# Check init container logs
|
# Check init container logs
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins
|
kubectl -n kube-system logs deployment/headlamp -c install-plugins
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# Plugin installation complete
|
# Plugin installation complete
|
||||||
|
|
||||||
# Verify plugin files exist
|
# Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec deployment/headlamp -c headlamp -- \
|
kubectl -n kube-system exec deployment/headlamp -c headlamp -- \
|
||||||
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
@@ -273,7 +273,7 @@ kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Port-forward to access locally
|
# Port-forward to access locally
|
||||||
kubectl -n <your-namespace> port-forward service/headlamp 8080:80
|
kubectl -n kube-system port-forward service/headlamp 8080:80
|
||||||
|
|
||||||
# Open browser to http://localhost:8080
|
# Open browser to http://localhost:8080
|
||||||
```
|
```
|
||||||
@@ -309,7 +309,7 @@ k8s/
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
|
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
|
|
||||||
commonLabels:
|
commonLabels:
|
||||||
app.kubernetes.io/name: headlamp
|
app.kubernetes.io/name: headlamp
|
||||||
@@ -401,7 +401,7 @@ spec:
|
|||||||
- apiVersion: apps/v1
|
- apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
```
|
```
|
||||||
|
|
||||||
## Upgrading the Plugin
|
## Upgrading the Plugin
|
||||||
@@ -410,24 +410,24 @@ spec:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Edit ConfigMap with new version
|
# Edit ConfigMap with new version
|
||||||
kubectl -n <your-namespace> edit configmap headlamp-plugin-config
|
kubectl -n kube-system edit configmap headlamp-plugin-config
|
||||||
|
|
||||||
# Update version and URL:
|
# Update version and URL:
|
||||||
# version: 0.3.6
|
# version: 0.3.6
|
||||||
# url: https://github.com/.../v0.3.6/polaris-0.3.10.tar.gz
|
# url: https://github.com/.../v0.3.6/polaris-0.3.10.tar.gz
|
||||||
|
|
||||||
# Restart deployment to trigger init container
|
# Restart deployment to trigger init container
|
||||||
kubectl -n <your-namespace> rollout restart deployment/headlamp
|
kubectl -n kube-system rollout restart deployment/headlamp
|
||||||
|
|
||||||
# Wait for rollout to complete
|
# Wait for rollout to complete
|
||||||
kubectl -n <your-namespace> rollout status deployment/headlamp
|
kubectl -n kube-system rollout status deployment/headlamp
|
||||||
```
|
```
|
||||||
|
|
||||||
### Verify Upgrade
|
### Verify Upgrade
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check init container logs
|
# Check init container logs
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins
|
kubectl -n kube-system logs deployment/headlamp -c install-plugins
|
||||||
|
|
||||||
# Verify new version in UI
|
# Verify new version in UI
|
||||||
# Navigate to Settings → Plugins in Headlamp
|
# Navigate to Settings → Plugins in Headlamp
|
||||||
@@ -439,7 +439,7 @@ kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check init container logs
|
# Check init container logs
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins
|
kubectl -n kube-system logs deployment/headlamp -c install-plugins
|
||||||
|
|
||||||
# Common issues:
|
# Common issues:
|
||||||
# 1. Network connectivity to GitHub
|
# 1. Network connectivity to GitHub
|
||||||
@@ -451,14 +451,14 @@ kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Verify HEADLAMP_CONFIG_WATCH_PLUGINS is false
|
# Verify HEADLAMP_CONFIG_WATCH_PLUGINS is false
|
||||||
kubectl -n <your-namespace> get deployment headlamp -o yaml | grep WATCH_PLUGINS
|
kubectl -n kube-system get deployment headlamp -o yaml | grep WATCH_PLUGINS
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# - name: HEADLAMP_CONFIG_WATCH_PLUGINS
|
# - name: HEADLAMP_CONFIG_WATCH_PLUGINS
|
||||||
# value: "false"
|
# value: "false"
|
||||||
|
|
||||||
# If not set or "true", update deployment
|
# If not set or "true", update deployment
|
||||||
kubectl -n <your-namespace> edit deployment headlamp
|
kubectl -n kube-system edit deployment headlamp
|
||||||
```
|
```
|
||||||
|
|
||||||
### RBAC Permissions Denied
|
### RBAC Permissions Denied
|
||||||
@@ -466,7 +466,7 @@ kubectl -n <your-namespace> edit deployment headlamp
|
|||||||
```bash
|
```bash
|
||||||
# Test RBAC
|
# Test RBAC
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ kubectl -n polaris get svc polaris-dashboard
|
|||||||
kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json | jq .PolarisOutputVersion
|
kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json | jq .PolarisOutputVersion
|
||||||
|
|
||||||
# Verify Headlamp
|
# Verify Headlamp
|
||||||
kubectl -n <your-namespace> get deployment headlamp
|
kubectl -n kube-system get deployment headlamp
|
||||||
kubectl -n <your-namespace> get svc headlamp
|
kubectl -n kube-system get svc headlamp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Production Checklist
|
## Production Checklist
|
||||||
@@ -60,17 +60,17 @@ kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy
|
|||||||
|
|
||||||
# 2. Verify RBAC permissions
|
# 2. Verify RBAC permissions
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
# Expected: yes
|
# Expected: yes
|
||||||
|
|
||||||
# 3. Check Headlamp logs for plugin loading
|
# 3. Check Headlamp logs for plugin loading
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp | grep -i polaris
|
kubectl -n kube-system logs deployment/headlamp | grep -i polaris
|
||||||
# Expected: No errors related to plugin loading
|
# Expected: No errors related to plugin loading
|
||||||
|
|
||||||
# 4. Verify plugin files exist
|
# 4. Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
kubectl -n kube-system exec deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
# Expected: dist/, package.json present
|
# Expected: dist/, package.json present
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ apiVersion: policy/v1
|
|||||||
kind: PodDisruptionBudget
|
kind: PodDisruptionBudget
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp-pdb
|
name: headlamp-pdb
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
spec:
|
spec:
|
||||||
minAvailable: 1
|
minAvailable: 1
|
||||||
selector:
|
selector:
|
||||||
@@ -295,7 +295,7 @@ apiVersion: monitoring.coreos.com/v1
|
|||||||
kind: ServiceMonitor
|
kind: ServiceMonitor
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
@@ -312,10 +312,10 @@ spec:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View logs
|
# View logs
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp -f
|
kubectl -n kube-system logs deployment/headlamp -f
|
||||||
|
|
||||||
# Filter for plugin-related logs
|
# Filter for plugin-related logs
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp | grep -i polaris
|
kubectl -n kube-system logs deployment/headlamp | grep -i polaris
|
||||||
```
|
```
|
||||||
|
|
||||||
**Polaris Dashboard Logs:**
|
**Polaris Dashboard Logs:**
|
||||||
@@ -341,14 +341,14 @@ apiVersion: monitoring.coreos.com/v1
|
|||||||
kind: PrometheusRule
|
kind: PrometheusRule
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp-alerts
|
name: headlamp-alerts
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
spec:
|
spec:
|
||||||
groups:
|
groups:
|
||||||
- name: headlamp
|
- name: headlamp
|
||||||
interval: 30s
|
interval: 30s
|
||||||
rules:
|
rules:
|
||||||
- alert: HeadlampPodNotReady
|
- alert: HeadlampPodNotReady
|
||||||
expr: kube_pod_status_ready{namespace="<your-namespace>", pod=~"headlamp-.*"} == 0
|
expr: kube_pod_status_ready{namespace="kube-system", pod=~"headlamp-.*"} == 0
|
||||||
for: 5m
|
for: 5m
|
||||||
labels:
|
labels:
|
||||||
severity: warning
|
severity: warning
|
||||||
@@ -422,9 +422,9 @@ If Headlamp or plugin becomes unavailable:
|
|||||||
2. **Redeploy Headlamp:**
|
2. **Redeploy Headlamp:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm upgrade --install headlamp headlamp/headlamp \
|
helm upgrade --install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml
|
--values headlamp-values.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Reapply RBAC:**
|
3. **Reapply RBAC:**
|
||||||
@@ -436,7 +436,7 @@ helm upgrade --install headlamp headlamp/headlamp \
|
|||||||
4. **Verify plugin files:**
|
4. **Verify plugin files:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl -n <your-namespace> exec deployment/headlamp -- \
|
kubectl -n kube-system exec deployment/headlamp -- \
|
||||||
ls /headlamp/plugins/headlamp-polaris-plugin/
|
ls /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -268,9 +268,10 @@ npm run e2e
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create token
|
# Create token
|
||||||
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n <your-namespace> --duration=24h)
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system --duration=24h)
|
||||||
|
|
||||||
kubectl port-forward -n <your-namespace> svc/headlamp 4466:80
|
# Port-forward for local testing
|
||||||
|
kubectl port-forward -n kube-system svc/headlamp 4466:80
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
HEADLAMP_URL=http://localhost:4466 npm run e2e
|
HEADLAMP_URL=http://localhost:4466 npm run e2e
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ Deploy or update Headlamp:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm upgrade --install headlamp headlamp/headlamp \
|
helm upgrade --install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml
|
--values headlamp-values.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ apiVersion: v1
|
|||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
name: headlamp-plugin-config
|
name: headlamp-plugin-config
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
data:
|
data:
|
||||||
plugin.yml: |
|
plugin.yml: |
|
||||||
- name: headlamp-polaris-plugin
|
- name: headlamp-polaris-plugin
|
||||||
@@ -138,14 +138,14 @@ kubectl apply -f headlamp-plugin-config.yaml
|
|||||||
|
|
||||||
# Deploy/update Headlamp with sidecar
|
# Deploy/update Headlamp with sidecar
|
||||||
helm upgrade --install headlamp headlamp/headlamp \
|
helm upgrade --install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml
|
--values headlamp-values.yaml
|
||||||
|
|
||||||
# Wait for pod to be ready
|
# Wait for pod to be ready
|
||||||
kubectl -n <your-namespace> wait --for=condition=ready pod -l app.kubernetes.io/name=headlamp --timeout=300s
|
kubectl -n kube-system wait --for=condition=ready pod -l app.kubernetes.io/name=headlamp --timeout=300s
|
||||||
|
|
||||||
# Verify plugin files
|
# Verify plugin files
|
||||||
kubectl -n <your-namespace> exec -it deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
kubectl -n kube-system exec -it deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# drwxr-xr-x dist/
|
# drwxr-xr-x dist/
|
||||||
@@ -270,7 +270,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -284,10 +284,10 @@ See [RBAC Permissions](../user-guide/rbac-permissions.md) for detailed RBAC conf
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# If you updated Helm values or ConfigMaps
|
# If you updated Helm values or ConfigMaps
|
||||||
kubectl -n <your-namespace> rollout restart deployment/headlamp
|
kubectl -n kube-system rollout restart deployment/headlamp
|
||||||
|
|
||||||
# Wait for pod to be ready
|
# Wait for pod to be ready
|
||||||
kubectl -n <your-namespace> wait --for=condition=ready pod -l app.kubernetes.io/name=headlamp --timeout=300s
|
kubectl -n kube-system wait --for=condition=ready pod -l app.kubernetes.io/name=headlamp --timeout=300s
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Clear Browser Cache
|
### 3. Clear Browser Cache
|
||||||
@@ -312,14 +312,14 @@ kubectl -n <your-namespace> wait --for=condition=ready pod -l app.kubernetes.io/
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Verify plugin files exist
|
# Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec -it deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
kubectl -n kube-system exec -it deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# drwxr-xr-x dist/
|
# drwxr-xr-x dist/
|
||||||
# -rw-r--r-- package.json
|
# -rw-r--r-- package.json
|
||||||
|
|
||||||
# Check Headlamp logs for errors
|
# Check Headlamp logs for errors
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp | grep -i polaris
|
kubectl -n kube-system logs deployment/headlamp | grep -i polaris
|
||||||
|
|
||||||
# Expected: No errors related to plugin loading
|
# Expected: No errors related to plugin loading
|
||||||
|
|
||||||
@@ -345,13 +345,13 @@ kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Verify plugin files exist
|
# 1. Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec deployment/headlamp -c headlamp -- \
|
kubectl -n kube-system exec deployment/headlamp -c headlamp -- \
|
||||||
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
|
|
||||||
# Expected: dist/, package.json present
|
# Expected: dist/, package.json present
|
||||||
|
|
||||||
# 2. Check Headlamp logs for plugin errors
|
# 2. Check Headlamp logs for plugin errors
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp | grep -i polaris
|
kubectl -n kube-system logs deployment/headlamp | grep -i polaris
|
||||||
|
|
||||||
# 3. Hard refresh browser (Cmd+Shift+R or Ctrl+Shift+R)
|
# 3. Hard refresh browser (Cmd+Shift+R or Ctrl+Shift+R)
|
||||||
|
|
||||||
@@ -404,7 +404,7 @@ helm install polaris fairwinds-stable/polaris \
|
|||||||
```bash
|
```bash
|
||||||
# Wait 30 minutes for ArtifactHub sync
|
# Wait 30 minutes for ArtifactHub sync
|
||||||
# Or manually force Headlamp restart:
|
# Or manually force Headlamp restart:
|
||||||
kubectl -n <your-namespace> rollout restart deployment/headlamp
|
kubectl -n kube-system rollout restart deployment/headlamp
|
||||||
```
|
```
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|||||||
@@ -67,14 +67,14 @@ kubectl -n polaris wait --for=condition=ready pod -l app.kubernetes.io/name=pola
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check Headlamp is deployed
|
# Check Headlamp is deployed
|
||||||
kubectl -n <your-namespace> get pods -l app.kubernetes.io/name=headlamp
|
kubectl -n kube-system get pods -l app.kubernetes.io/name=headlamp
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# NAME READY STATUS RESTARTS AGE
|
# NAME READY STATUS RESTARTS AGE
|
||||||
# headlamp-xxxxxxxxxx-xxxxx 1/1 Running 0 1h
|
# headlamp-xxxxxxxxxx-xxxxx 1/1 Running 0 1h
|
||||||
|
|
||||||
# Check Headlamp version (must be v0.26+)
|
# Check Headlamp version (must be v0.26+)
|
||||||
kubectl -n <your-namespace> get deployment headlamp -o jsonpath='{.spec.template.spec.containers[0].image}'
|
kubectl -n kube-system get deployment headlamp -o jsonpath='{.spec.template.spec.containers[0].image}'
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# ghcr.io/headlamp-k8s/headlamp:v0.39.0 (or similar)
|
# ghcr.io/headlamp-k8s/headlamp:v0.39.0 (or similar)
|
||||||
@@ -89,12 +89,12 @@ helm repo update
|
|||||||
|
|
||||||
# Install Headlamp
|
# Install Headlamp
|
||||||
helm install headlamp headlamp/headlamp \
|
helm install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--set config.pluginsDir="/headlamp/plugins" \
|
--set config.pluginsDir="/headlamp/plugins" \
|
||||||
--set pluginsManager.enabled=true
|
--set pluginsManager.enabled=true
|
||||||
|
|
||||||
# Wait for pod to be ready
|
# Wait for pod to be ready
|
||||||
kubectl -n <your-namespace> wait --for=condition=ready pod -l app.kubernetes.io/name=headlamp --timeout=300s
|
kubectl -n kube-system wait --for=condition=ready pod -l app.kubernetes.io/name=headlamp --timeout=300s
|
||||||
```
|
```
|
||||||
|
|
||||||
## RBAC Requirements
|
## RBAC Requirements
|
||||||
@@ -112,7 +112,7 @@ The plugin requires permissions to access the Polaris dashboard via Kubernetes s
|
|||||||
```bash
|
```bash
|
||||||
# Test if Headlamp service account has permission
|
# Test if Headlamp service account has permission
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ EOF
|
|||||||
|
|
||||||
# Update Headlamp
|
# Update Headlamp
|
||||||
helm upgrade --install headlamp headlamp/headlamp \
|
helm upgrade --install headlamp headlamp/headlamp \
|
||||||
--namespace <your-namespace> \
|
--namespace kube-system \
|
||||||
--values headlamp-values.yaml
|
--values headlamp-values.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -111,7 +111,7 @@ EOF
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Verify plugin files exist
|
# Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec -it deployment/headlamp -c headlamp -- \
|
kubectl -n kube-system exec -it deployment/headlamp -c headlamp -- \
|
||||||
ls /headlamp/plugins/headlamp-polaris-plugin/dist/
|
ls /headlamp/plugins/headlamp-polaris-plugin/dist/
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
@@ -119,7 +119,7 @@ kubectl -n <your-namespace> exec -it deployment/headlamp -c headlamp -- \
|
|||||||
|
|
||||||
# Verify RBAC is correct
|
# Verify RBAC is correct
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ Cluster score badge in top navigation:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Verify plugin files exist
|
# Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec -it deployment/headlamp -c headlamp -- \
|
kubectl -n kube-system exec -it deployment/headlamp -c headlamp -- \
|
||||||
ls /headlamp/plugins/headlamp-polaris-plugin/
|
ls /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
|
|
||||||
# If missing, reinstall via Headlamp UI or sidecar method
|
# If missing, reinstall via Headlamp UI or sidecar method
|
||||||
|
|||||||
@@ -38,17 +38,17 @@ kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy
|
|||||||
|
|
||||||
# 3. Verify RBAC permissions
|
# 3. Verify RBAC permissions
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
# Expected output: yes
|
# Expected output: yes
|
||||||
|
|
||||||
# 4. Check Headlamp pod is running
|
# 4. Check Headlamp pod is running
|
||||||
kubectl -n <your-namespace> get pods -l app.kubernetes.io/name=headlamp
|
kubectl -n kube-system get pods -l app.kubernetes.io/name=headlamp
|
||||||
|
|
||||||
# 5. Check Headlamp logs for plugin errors
|
# 5. Check Headlamp logs for plugin errors
|
||||||
kubectl -n <your-namespace> logs deployment/headlamp | grep -i polaris
|
kubectl -n kube-system logs deployment/headlamp | grep -i polaris
|
||||||
|
|
||||||
# Expected: No errors
|
# Expected: No errors
|
||||||
```
|
```
|
||||||
@@ -57,7 +57,7 @@ kubectl -n <your-namespace> logs deployment/headlamp | grep -i polaris
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Verify plugin files exist
|
# Verify plugin files exist
|
||||||
kubectl -n <your-namespace> exec deployment/headlamp -c headlamp -- \
|
kubectl -n kube-system exec deployment/headlamp -c headlamp -- \
|
||||||
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
ls -la /headlamp/plugins/headlamp-polaris-plugin/
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
@@ -76,7 +76,7 @@ kubectl -n polaris get rolebinding headlamp-polaris-proxy
|
|||||||
|
|
||||||
# Test permission (service account mode)
|
# Test permission (service account mode)
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ This guide covers common issues encountered when using the Headlamp Polaris Plug
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View Headlamp pod logs (plugin sidecar)
|
# View Headlamp pod logs (plugin sidecar)
|
||||||
kubectl logs -n <your-namespace> deployment/headlamp -c headlamp-plugin
|
kubectl logs -n kube-system deployment/headlamp -c headlamp-plugin
|
||||||
|
|
||||||
# Expected output:
|
# Expected output:
|
||||||
# Installing plugin from https://github.com/.../headlamp-polaris-plugin-X.Y.Z.tar.gz
|
# Installing plugin from https://github.com/.../headlamp-polaris-plugin-X.Y.Z.tar.gz
|
||||||
@@ -43,7 +43,7 @@ kubectl logs -n <your-namespace> deployment/headlamp -c headlamp-plugin
|
|||||||
**Verify plugin files exist**:
|
**Verify plugin files exist**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl exec -n <your-namespace> deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/
|
kubectl exec -n kube-system deployment/headlamp -c headlamp -- ls -la /headlamp/plugins/
|
||||||
# Should show: headlamp-polaris-plugin/
|
# Should show: headlamp-polaris-plugin/
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ Expected subjects:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
```
|
```
|
||||||
|
|
||||||
For OIDC mode:
|
For OIDC mode:
|
||||||
@@ -154,7 +154,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -169,7 +169,7 @@ Service account mode:
|
|||||||
```bash
|
```bash
|
||||||
# Impersonate Headlamp service account
|
# Impersonate Headlamp service account
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
--resource-name=polaris-dashboard \
|
--resource-name=polaris-dashboard \
|
||||||
-n polaris
|
-n polaris
|
||||||
# Expected: yes
|
# Expected: yes
|
||||||
@@ -189,7 +189,7 @@ kubectl auth can-i get services/proxy \
|
|||||||
After applying RBAC changes:
|
After applying RBAC changes:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl rollout restart deployment headlamp -n <your-namespace>
|
kubectl rollout restart deployment headlamp -n kube-system
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -490,7 +490,7 @@ Run this script to test all RBAC components:
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
NS="polaris"
|
NS="polaris"
|
||||||
SA="headlamp"
|
SA="headlamp"
|
||||||
SA_NS="<your-namespace>"
|
SA_NS="kube-system"
|
||||||
|
|
||||||
echo "=== Testing RBAC for Polaris Plugin ==="
|
echo "=== Testing RBAC for Polaris Plugin ==="
|
||||||
|
|
||||||
@@ -529,8 +529,8 @@ echo "=== Test complete ==="
|
|||||||
Test connectivity from Headlamp to Polaris:
|
Test connectivity from Headlamp to Polaris:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create debug pod in the namespace where Headlamp is installed
|
# Create debug pod in kube-system namespace
|
||||||
kubectl run netdebug -n <your-namespace> --rm -it --image=nicolaka/netshoot -- bash
|
kubectl run netdebug -n kube-system --rm -it --image=nicolaka/netshoot -- bash
|
||||||
|
|
||||||
# Inside pod, test DNS and HTTP
|
# Inside pod, test DNS and HTTP
|
||||||
nslookup polaris-dashboard.polaris.svc.cluster.local
|
nslookup polaris-dashboard.polaris.svc.cluster.local
|
||||||
@@ -545,11 +545,11 @@ If you have audit logging enabled, check for denied requests:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View recent audit logs (location varies by cluster)
|
# View recent audit logs (location varies by cluster)
|
||||||
kubectl logs -n <your-namespace> kube-apiserver-* | grep polaris-dashboard
|
kubectl logs -n kube-system kube-apiserver-* | grep polaris-dashboard
|
||||||
|
|
||||||
# Look for lines with:
|
# Look for lines with:
|
||||||
# "reason": "Forbidden"
|
# "reason": "Forbidden"
|
||||||
# "user": "system:serviceaccount:<your-namespace>:headlamp"
|
# "user": "system:serviceaccount:kube-system:headlamp"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -567,7 +567,7 @@ kubectl logs -n <your-namespace> kube-apiserver-* | grep polaris-dashboard
|
|||||||
**Check sidecar logs**:
|
**Check sidecar logs**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl logs -n <your-namespace> deployment/headlamp -c headlamp-plugin
|
kubectl logs -n kube-system deployment/headlamp -c headlamp-plugin
|
||||||
```
|
```
|
||||||
|
|
||||||
**Common errors**:
|
**Common errors**:
|
||||||
@@ -591,7 +591,7 @@ Error: 404 Not Found
|
|||||||
**Solution**: Verify `archive-url` in plugin config matches GitHub release:
|
**Solution**: Verify `archive-url` in plugin config matches GitHub release:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get configmap headlamp-plugin-config -n <your-namespace> -o yaml
|
kubectl get configmap headlamp-plugin-config -n kube-system -o yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected format:
|
Expected format:
|
||||||
@@ -677,13 +677,13 @@ If none of these solutions work, gather debugging information and open an issue:
|
|||||||
1. **Version Information**:
|
1. **Version Information**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get pods -n <your-namespace> -l app.kubernetes.io/name=headlamp -o yaml | grep image:
|
kubectl get pods -n kube-system -l app.kubernetes.io/name=headlamp -o yaml | grep image:
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Plugin Version**:
|
2. **Plugin Version**:
|
||||||
|
|
||||||
- Check Settings → Plugins in Headlamp UI
|
- Check Settings → Plugins in Headlamp UI
|
||||||
- Or: `kubectl exec -n <your-namespace> deployment/headlamp -c headlamp -- cat /headlamp/plugins/headlamp-polaris-plugin/package.json`
|
- Or: `kubectl exec -n kube-system deployment/headlamp -c headlamp -- cat /headlamp/plugins/headlamp-polaris-plugin/package.json`
|
||||||
|
|
||||||
3. **Browser Console Output**:
|
3. **Browser Console Output**:
|
||||||
|
|
||||||
@@ -698,11 +698,12 @@ If none of these solutions work, gather debugging information and open an issue:
|
|||||||
5. **Pod Logs**:
|
5. **Pod Logs**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl logs -n <your-namespace> deployment/headlamp -c headlamp --tail=100
|
kubectl logs -n kube-system deployment/headlamp -c headlamp --tail=100
|
||||||
kubectl logs -n polaris deployment/polaris-dashboard --tail=100
|
kubectl logs -n polaris deployment/polaris-dashboard --tail=100
|
||||||
```
|
```
|
||||||
|
|
||||||
6. **RBAC Configuration**:
|
6. **RBAC Configuration**:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl get role,rolebinding -n polaris
|
kubectl get role,rolebinding -n polaris
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -83,7 +83,7 @@ roleRef:
|
|||||||
```bash
|
```bash
|
||||||
# Test service account (in-cluster mode)
|
# Test service account (in-cluster mode)
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url');
|
|||||||
2. Check for JavaScript errors
|
2. Check for JavaScript errors
|
||||||
3. Disable privacy mode or try different browser
|
3. Disable privacy mode or try different browser
|
||||||
4. Check if localStorage is enabled:
|
4. Check if localStorage is enabled:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
console.log('localStorage available:', typeof localStorage !== 'undefined');
|
console.log('localStorage available:', typeof localStorage !== 'undefined');
|
||||||
```
|
```
|
||||||
@@ -317,7 +318,7 @@ kubectl -n polaris get rolebinding headlamp-polaris-proxy
|
|||||||
|
|
||||||
# Test permission
|
# Test permission
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp # Adjust to your Headlamp SA name
|
name: headlamp # Adjust to your Headlamp SA name
|
||||||
namespace: <your-namespace>
|
namespace: kube-system # Adjust to Headlamp's namespace
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -75,7 +75,7 @@ roleRef:
|
|||||||
**Adjust for your environment:**
|
**Adjust for your environment:**
|
||||||
|
|
||||||
- `subjects[0].name` - Your Headlamp service account name (often `headlamp`)
|
- `subjects[0].name` - Your Headlamp service account name (often `headlamp`)
|
||||||
- `subjects[0].namespace` - Namespace where Headlamp is installed
|
- `subjects[0].namespace` - Namespace where Headlamp runs (often `kube-system`)
|
||||||
|
|
||||||
### Step 3: Apply and Verify
|
### Step 3: Apply and Verify
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ kubectl -n polaris get rolebinding headlamp-polaris-proxy
|
|||||||
|
|
||||||
# Test permission
|
# Test permission
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ In token-auth mode, **each user's own identity** is used for Kubernetes API requ
|
|||||||
With service account mode:
|
With service account mode:
|
||||||
|
|
||||||
- Single RoleBinding grants access to all Headlamp users
|
- Single RoleBinding grants access to all Headlamp users
|
||||||
- Kubernetes sees all requests as `system:serviceaccount:<your-namespace>:headlamp`
|
- Kubernetes sees all requests as `system:serviceaccount:kube-system:headlamp`
|
||||||
|
|
||||||
With token-auth mode:
|
With token-auth mode:
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -281,7 +281,7 @@ metadata:
|
|||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
name: headlamp
|
name: headlamp
|
||||||
namespace: <your-namespace>
|
namespace: kube-system
|
||||||
roleRef:
|
roleRef:
|
||||||
kind: Role
|
kind: Role
|
||||||
name: polaris-proxy-reader
|
name: polaris-proxy-reader
|
||||||
@@ -411,7 +411,7 @@ Every plugin data fetch creates a Kubernetes API audit log entry.
|
|||||||
"level": "Metadata",
|
"level": "Metadata",
|
||||||
"verb": "get",
|
"verb": "get",
|
||||||
"user": {
|
"user": {
|
||||||
"username": "system:serviceaccount:<your-namespace>:headlamp"
|
"username": "system:serviceaccount:kube-system:headlamp"
|
||||||
},
|
},
|
||||||
"sourceIPs": ["10.96.0.1"],
|
"sourceIPs": ["10.96.0.1"],
|
||||||
"objectRef": {
|
"objectRef": {
|
||||||
@@ -494,7 +494,7 @@ If using a log aggregator (e.g., Elasticsearch), create filters to exclude or do
|
|||||||
```bash
|
```bash
|
||||||
# Service account mode
|
# Service account mode
|
||||||
kubectl auth can-i get services/proxy \
|
kubectl auth can-i get services/proxy \
|
||||||
--as=system:serviceaccount:<your-namespace>:headlamp \
|
--as=system:serviceaccount:kube-system:headlamp \
|
||||||
-n polaris \
|
-n polaris \
|
||||||
--resource-name=polaris-dashboard
|
--resource-name=polaris-dashboard
|
||||||
|
|
||||||
@@ -508,9 +508,11 @@ If using a log aggregator (e.g., Elasticsearch), create filters to exclude or do
|
|||||||
Expected: `yes`
|
Expected: `yes`
|
||||||
|
|
||||||
4. **Verify RoleBinding subjects match:**
|
4. **Verify RoleBinding subjects match:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl -n polaris get rolebinding headlamp-polaris-proxy -o yaml
|
kubectl -n polaris get rolebinding headlamp-polaris-proxy -o yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Check `subjects[].name` and `subjects[].namespace` match your Headlamp SA or user
|
Check `subjects[].name` and `subjects[].namespace` match your Headlamp SA or user
|
||||||
|
|
||||||
### "404 Not Found" Error
|
### "404 Not Found" Error
|
||||||
|
|||||||
+310
@@ -0,0 +1,310 @@
|
|||||||
|
# E2E Smoke Tests
|
||||||
|
|
||||||
|
Playwright-based smoke tests that validate the Polaris plugin against a live Headlamp deployment.
|
||||||
|
|
||||||
|
## CI
|
||||||
|
|
||||||
|
E2E tests run automatically in GitHub Actions on pushes to `main` and pull requests. The workflow (`.github/workflows/e2e.yaml`):
|
||||||
|
|
||||||
|
1. Builds the plugin (`npm run build`)
|
||||||
|
2. Creates a ConfigMap from the built `dist/` output
|
||||||
|
3. Deploys a stock Headlamp instance via Helm with the plugin mounted as a ConfigMap volume
|
||||||
|
4. Generates a ServiceAccount token for test auth
|
||||||
|
5. Runs Playwright tests against the E2E instance
|
||||||
|
6. Tears down the E2E instance
|
||||||
|
|
||||||
|
This approach uses the stock `ghcr.io/headlamp-k8s/headlamp` image with no custom Docker builds. The plugin is loaded via `HEADLAMP_PLUGINS_DIR` volume mount.
|
||||||
|
|
||||||
|
### Required GitHub Secrets
|
||||||
|
|
||||||
|
Configure these in GitHub repository settings (Settings → Secrets and variables → Actions):
|
||||||
|
|
||||||
|
| Secret | Required | Description |
|
||||||
|
| -------------------- | -------- | -------------------------------------------------------------- |
|
||||||
|
| `AUTHENTIK_USERNAME` | OIDC | Authentik email or username for a CI user with Headlamp access |
|
||||||
|
| `AUTHENTIK_PASSWORD` | OIDC | Password for that user |
|
||||||
|
|
||||||
|
Token-based auth is auto-generated by the deploy script. OIDC secrets are only needed if testing against the shared Headlamp instance.
|
||||||
|
|
||||||
|
No `GHCR_TOKEN` or Docker registry secrets are needed — the stock Headlamp image is public.
|
||||||
|
|
||||||
|
## Running Locally
|
||||||
|
|
||||||
|
### Option 1: OIDC via Authentik (same as CI)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AUTHENTIK_USERNAME=you@example.com AUTHENTIK_PASSWORD=... npm run e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
The default base URL is `https://headlamp.animaniacs.farh.net`. Override with `HEADLAMP_URL` if needed.
|
||||||
|
|
||||||
|
### Option 2: K8s bearer token (port-forward)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl port-forward -n kube-system svc/headlamp 4466:80
|
||||||
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system)
|
||||||
|
HEADLAMP_URL=http://localhost:4466 npm run e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
Or in headed mode (opens a browser window):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
HEADLAMP_URL=http://localhost:4466 npm run e2e:headed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
| Variable | Required | Default | Description |
|
||||||
|
| -------------------- | -------- | -------------------------------------- | --------------------------------------- |
|
||||||
|
| `HEADLAMP_URL` | No | `https://headlamp.animaniacs.farh.net` | Base URL of the Headlamp instance |
|
||||||
|
| `AUTHENTIK_USERNAME` | OIDC | — | Authentik email/username |
|
||||||
|
| `AUTHENTIK_PASSWORD` | OIDC | — | Authentik password |
|
||||||
|
| `HEADLAMP_TOKEN` | Token | — | Kubernetes bearer token (auto-generated in CI) |
|
||||||
|
|
||||||
|
In CI, `HEADLAMP_URL` and `HEADLAMP_TOKEN` are set automatically by the deploy script. For local runs, set either OIDC credentials or a token manually.
|
||||||
|
|
||||||
|
## What the Tests Validate
|
||||||
|
|
||||||
|
- **Sidebar entry** — The Polaris sidebar item appears after login
|
||||||
|
- **Overview page** — Cluster score and check distribution render correctly
|
||||||
|
- **Namespaces page** — Table of namespaces loads with clickable links
|
||||||
|
- **Namespace detail** — Clicking a namespace shows its score and resource table
|
||||||
|
|
||||||
|
These are smoke tests against real cluster data. They verify the plugin loads and renders without errors, not specific data values.
|
||||||
|
|
||||||
|
## Test Coverage
|
||||||
|
|
||||||
|
### Current Tests (`polaris.spec.ts`)
|
||||||
|
|
||||||
|
1. **`sidebar contains Polaris entry`**
|
||||||
|
- Verifies Polaris appears in the navigation sidebar
|
||||||
|
- Ensures plugin successfully registered sidebar entry
|
||||||
|
|
||||||
|
2. **`overview page renders cluster score`**
|
||||||
|
- Navigates to `/c/main/polaris`
|
||||||
|
- Checks for "Polaris — Overview" heading
|
||||||
|
- Verifies cluster score percentage is displayed
|
||||||
|
- Validates data fetching and rendering
|
||||||
|
|
||||||
|
3. **`namespaces page renders table with namespace buttons`**
|
||||||
|
- Navigates to `/c/main/polaris/namespaces`
|
||||||
|
- Checks for "Polaris — Namespaces" heading
|
||||||
|
- Verifies table is visible with at least one row
|
||||||
|
- Ensures namespace buttons are clickable
|
||||||
|
|
||||||
|
4. **`namespace detail drawer opens from table button`**
|
||||||
|
- Clicks first namespace button in table
|
||||||
|
- Verifies drawer opens with namespace name in heading
|
||||||
|
- Checks "Namespace Score" section is visible
|
||||||
|
- Confirms "Resources" table is displayed
|
||||||
|
- Validates URL hash is updated with namespace name
|
||||||
|
|
||||||
|
5. **`namespace detail drawer closes with Escape key`**
|
||||||
|
- Opens namespace drawer
|
||||||
|
- Presses Escape key
|
||||||
|
- Verifies drawer closes
|
||||||
|
- Checks URL hash is cleared
|
||||||
|
|
||||||
|
6. **`namespace detail drawer opens from URL hash`**
|
||||||
|
- Navigates directly to `/c/main/polaris/namespaces#<namespace>`
|
||||||
|
- Verifies drawer automatically opens
|
||||||
|
- Checks namespace details are displayed
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Cluster Requirements
|
||||||
|
|
||||||
|
1. **Polaris Deployment**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Verify Polaris is running
|
||||||
|
kubectl -n polaris get pods
|
||||||
|
kubectl -n polaris get svc polaris-dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Polaris Audit Data**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check if Polaris has generated audit results
|
||||||
|
kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json | jq '.AuditTime'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **RBAC Permissions**
|
||||||
|
- Headlamp service account (or test user) needs `get` on `services/proxy` for `polaris-dashboard`
|
||||||
|
- See main README for RBAC setup
|
||||||
|
|
||||||
|
### Local Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install dependencies
|
||||||
|
npm install
|
||||||
|
npx playwright install chromium
|
||||||
|
|
||||||
|
# 2. Create .env file (optional, for persistent config)
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
# 3. Set environment variables
|
||||||
|
export HEADLAMP_URL=https://your-headlamp-instance.com
|
||||||
|
export HEADLAMP_TOKEN=$(kubectl create token headlamp -n kube-system)
|
||||||
|
|
||||||
|
# 4. Run tests
|
||||||
|
npm run e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Run in Headed Mode
|
||||||
|
|
||||||
|
See the browser UI while tests run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run e2e:headed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable Debug Mode
|
||||||
|
|
||||||
|
Step through tests with Playwright Inspector:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test --debug
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate Trace
|
||||||
|
|
||||||
|
Record full trace for failed tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright test --trace on
|
||||||
|
npx playwright show-trace test-results/<test-name>/trace.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### Screenshot on Failure
|
||||||
|
|
||||||
|
Tests automatically capture screenshots on failure in `test-results/`
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Auth fails with "Sign In button not found":**
|
||||||
|
|
||||||
|
- Check HEADLAMP_URL is correct
|
||||||
|
- Verify Headlamp is accessible
|
||||||
|
- Ensure OIDC is configured if using Authentik
|
||||||
|
|
||||||
|
**Polaris sidebar entry not found:**
|
||||||
|
|
||||||
|
- Plugin may not be installed: Check Settings → Plugins in Headlamp
|
||||||
|
- Plugin may have failed to load: Check browser console
|
||||||
|
- Clear browser cache and hard refresh
|
||||||
|
|
||||||
|
**Cluster score not displayed:**
|
||||||
|
|
||||||
|
- Polaris may not have audit data yet
|
||||||
|
- Check Polaris is running: `kubectl -n polaris get pods`
|
||||||
|
- Verify service proxy: `kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json`
|
||||||
|
|
||||||
|
**Namespace table empty:**
|
||||||
|
|
||||||
|
- Polaris hasn't run audit yet (wait a few minutes)
|
||||||
|
- Check Polaris logs: `kubectl -n polaris logs -l app.kubernetes.io/name=polaris`
|
||||||
|
|
||||||
|
## Writing New Tests
|
||||||
|
|
||||||
|
### Example: Testing Plugin Settings
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
test('plugin settings page shows Polaris configuration', async ({ page }) => {
|
||||||
|
await page.goto('/c/main/settings/plugins');
|
||||||
|
|
||||||
|
// Find and click Polaris plugin
|
||||||
|
await page.getByText('headlamp-polaris-plugin').click();
|
||||||
|
|
||||||
|
// Check settings are visible
|
||||||
|
await expect(page.getByText('Polaris Settings')).toBeVisible();
|
||||||
|
await expect(page.getByText('Refresh Interval')).toBeVisible();
|
||||||
|
await expect(page.getByText('Dashboard URL')).toBeVisible();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Testing App Bar Badge
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
test('app bar displays Polaris score badge', async ({ page }) => {
|
||||||
|
await page.goto('/c/main');
|
||||||
|
|
||||||
|
// Badge should be visible in app bar
|
||||||
|
const badge = page.getByRole('button', { name: /Polaris: \d+%/ });
|
||||||
|
await expect(badge).toBeVisible();
|
||||||
|
|
||||||
|
// Clicking should navigate to overview
|
||||||
|
await badge.click();
|
||||||
|
await expect(page).toHaveURL(/\/c\/main\/polaris$/);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: Testing Dark Mode
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
test('plugin UI adapts to dark mode', async ({ page }) => {
|
||||||
|
await page.goto('/c/main/polaris');
|
||||||
|
|
||||||
|
// Toggle dark mode
|
||||||
|
await page.getByRole('button', { name: /theme/i }).click();
|
||||||
|
|
||||||
|
// Check background color changes
|
||||||
|
const body = page.locator('body');
|
||||||
|
await expect(body).toHaveCSS('background-color', 'rgb(18, 18, 18)');
|
||||||
|
|
||||||
|
// Plugin components should adapt
|
||||||
|
const sectionBox = page.locator('[class*="MuiPaper"]').first();
|
||||||
|
await expect(sectionBox).not.toHaveCSS('background-color', 'rgb(255, 255, 255)');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Integration
|
||||||
|
|
||||||
|
Tests run automatically in GitHub Actions on pushes to `main` and pull requests. See `.github/workflows/e2e.yaml` for workflow configuration.
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
|
The E2E workflow deploys a **dedicated Headlamp instance** for each test run:
|
||||||
|
|
||||||
|
1. Build plugin (`npm run build`)
|
||||||
|
2. Create ConfigMap from `dist/` output (`scripts/deploy-e2e-headlamp.sh`)
|
||||||
|
3. Deploy stock Headlamp via Helm with ConfigMap volume mount
|
||||||
|
4. Run Playwright tests against the E2E instance
|
||||||
|
5. Tear down (`scripts/teardown-e2e-headlamp.sh`)
|
||||||
|
|
||||||
|
No custom Docker images, no PVCs, no kubectl exec/cp, no patching of existing deployments. The plugin is mounted from a ConfigMap into the stock Headlamp image.
|
||||||
|
|
||||||
|
### Cluster Prerequisites
|
||||||
|
|
||||||
|
One-time setup by a cluster admin:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl apply -f deployment/e2e-ci-runner-rbac.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Trigger
|
||||||
|
|
||||||
|
You can manually trigger E2E tests from GitHub Actions:
|
||||||
|
|
||||||
|
1. Go to Actions → E2E Tests
|
||||||
|
2. Click "Run workflow"
|
||||||
|
3. Select branch and run
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Use semantic selectors**: `getByRole`, `getByText` over CSS selectors
|
||||||
|
2. **Wait for visibility**: Use `await expect(...).toBeVisible()` instead of `waitForTimeout`
|
||||||
|
3. **Keep tests independent**: Each test should work in isolation
|
||||||
|
4. **Test user flows**: Complete journeys, not just page loads
|
||||||
|
5. **Clean up state**: Close drawers/modals after tests
|
||||||
|
6. **Use storage state**: Reuse auth across tests (already configured)
|
||||||
|
7. **Parallelize carefully**: Currently disabled due to shared state
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Playwright Documentation](https://playwright.dev/)
|
||||||
|
- [Playwright Best Practices](https://playwright.dev/docs/best-practices)
|
||||||
|
- [Headlamp Plugin Development](https://headlamp.dev/docs/latest/development/plugins/)
|
||||||
|
- [Project Main README](../README.md)
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test.describe('Polaris app bar badge', () => {
|
||||||
|
test('badge displays cluster score in app bar', async ({ page }) => {
|
||||||
|
await page.goto('/c/main');
|
||||||
|
|
||||||
|
// Wait for page to load
|
||||||
|
await expect(page.getByRole('navigation', { name: 'Navigation' })).toBeVisible();
|
||||||
|
|
||||||
|
// Badge should be visible in app bar with score percentage
|
||||||
|
const badge = page.getByRole('button', { name: /Polaris: \d+%/ });
|
||||||
|
await expect(badge).toBeVisible({ timeout: 15_000 });
|
||||||
|
|
||||||
|
// Badge should show shield emoji
|
||||||
|
await expect(badge).toContainText('🛡️');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('clicking badge navigates to overview page', async ({ page }) => {
|
||||||
|
await page.goto('/c/main');
|
||||||
|
|
||||||
|
// Find and click the badge
|
||||||
|
const badge = page.getByRole('button', { name: /Polaris: \d+%/ });
|
||||||
|
await expect(badge).toBeVisible({ timeout: 15_000 });
|
||||||
|
await badge.click();
|
||||||
|
|
||||||
|
// Should navigate to Polaris overview
|
||||||
|
await expect(page).toHaveURL(/\/c\/main\/polaris$/);
|
||||||
|
await expect(page.getByRole('heading', { name: 'Polaris — Overview' })).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('badge color reflects score level', async ({ page }) => {
|
||||||
|
await page.goto('/c/main');
|
||||||
|
|
||||||
|
// Get the badge
|
||||||
|
const badge = page.getByRole('button', { name: /Polaris: \d+%/ });
|
||||||
|
await expect(badge).toBeVisible({ timeout: 15_000 });
|
||||||
|
|
||||||
|
// Extract score from button text
|
||||||
|
const badgeText = await badge.textContent();
|
||||||
|
const scoreMatch = badgeText?.match(/(\d+)%/);
|
||||||
|
expect(scoreMatch).toBeTruthy();
|
||||||
|
|
||||||
|
const score = parseInt(scoreMatch![1]);
|
||||||
|
|
||||||
|
// Check background color matches score level
|
||||||
|
const bgColor = await badge.evaluate(el =>
|
||||||
|
window.getComputedStyle(el).backgroundColor
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify that the badge has a non-default background color applied
|
||||||
|
// (theme-dependent RGB values vary across Headlamp versions, so we
|
||||||
|
// only assert that a real color is set rather than transparent/default)
|
||||||
|
expect(bgColor).not.toBe('rgba(0, 0, 0, 0)');
|
||||||
|
expect(bgColor).not.toBe('transparent');
|
||||||
|
expect(bgColor).toMatch(/^rgb/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('badge updates when navigating between clusters', async ({ page }) => {
|
||||||
|
// This test assumes multi-cluster setup; skip if only one cluster
|
||||||
|
await page.goto('/c/main');
|
||||||
|
|
||||||
|
// Get initial badge score
|
||||||
|
const badge = page.getByRole('button', { name: /Polaris: \d+%/ });
|
||||||
|
await expect(badge).toBeVisible({ timeout: 15_000 });
|
||||||
|
const initialScore = await badge.textContent();
|
||||||
|
|
||||||
|
// Try to switch clusters (if available)
|
||||||
|
const clusterSelector = page.getByRole('button', { name: /cluster/i });
|
||||||
|
if (await clusterSelector.isVisible()) {
|
||||||
|
// Note: This part will only work in multi-cluster setups
|
||||||
|
// For single-cluster, this test will just verify badge persists
|
||||||
|
await clusterSelector.click();
|
||||||
|
|
||||||
|
// Select different cluster if available
|
||||||
|
const clusterOptions = page.getByRole('menuitem');
|
||||||
|
const count = await clusterOptions.count();
|
||||||
|
|
||||||
|
if (count > 1) {
|
||||||
|
await clusterOptions.nth(1).click();
|
||||||
|
|
||||||
|
// Badge should update or disappear (if new cluster doesn't have Polaris)
|
||||||
|
// This is just verifying no crash occurs
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Badge should still be functional
|
||||||
|
await expect(badge).toBeEnabled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { test as setup, expect, Page } from '@playwright/test';
|
||||||
|
|
||||||
|
const AUTH_STATE_PATH = 'e2e/.auth/state.json';
|
||||||
|
|
||||||
|
async function authenticateWithOIDC(page: Page, username: string, password: string): Promise<void> {
|
||||||
|
// Navigate to login — Headlamp redirects / to /c/main/login
|
||||||
|
await page.goto('/');
|
||||||
|
await page.waitForURL('**/login');
|
||||||
|
|
||||||
|
// Click "Sign In" and capture the Authentik popup
|
||||||
|
const popupPromise = page.waitForEvent('popup');
|
||||||
|
await page.getByRole('button', { name: /sign in/i }).click();
|
||||||
|
const popup = await popupPromise;
|
||||||
|
|
||||||
|
// Wait for the Authentik popup to fully load before interacting
|
||||||
|
await popup.waitForLoadState('domcontentloaded');
|
||||||
|
await popup.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
// Authentik step 1: fill username — wait for the form to render
|
||||||
|
const usernameField = popup.getByRole('textbox', { name: /email or username/i });
|
||||||
|
await usernameField.waitFor({ state: 'visible', timeout: 15_000 });
|
||||||
|
await usernameField.fill(username);
|
||||||
|
await popup.getByRole('button', { name: /log in/i }).click();
|
||||||
|
|
||||||
|
// Authentik step 2: fill password — wait for the next step to load
|
||||||
|
await popup.waitForLoadState('networkidle');
|
||||||
|
const passwordField = popup.getByRole('textbox', { name: /password/i });
|
||||||
|
await passwordField.waitFor({ state: 'visible', timeout: 15_000 });
|
||||||
|
await passwordField.fill(password);
|
||||||
|
await popup.getByRole('button', { name: /continue|log in/i }).click();
|
||||||
|
|
||||||
|
// Wait for the popup to close (Authentik redirects back, Headlamp processes callback)
|
||||||
|
await popup.waitForEvent('close', { timeout: 15_000 });
|
||||||
|
|
||||||
|
// Original page should now be authenticated — wait for sidebar
|
||||||
|
await expect(page.getByRole('navigation', { name: 'Navigation' })).toBeVisible({
|
||||||
|
timeout: 15_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function authenticateWithToken(page: Page, token: string): Promise<void> {
|
||||||
|
await page.goto('/');
|
||||||
|
// Headlamp goes to /token directly when no OIDC is configured,
|
||||||
|
// or through /login when OIDC is configured
|
||||||
|
await page.waitForURL(/\/(login|token)$/);
|
||||||
|
|
||||||
|
if (page.url().includes('/login')) {
|
||||||
|
// OIDC login page — click "use a token" to reach token auth.
|
||||||
|
// Wait explicitly before clicking so failures surface at 15 s
|
||||||
|
// with a clear message rather than silently timing out at 60 s.
|
||||||
|
const useTokenBtn = page.getByRole('button', { name: /use a token/i });
|
||||||
|
await useTokenBtn.waitFor({ state: 'visible', timeout: 15_000 });
|
||||||
|
await useTokenBtn.click();
|
||||||
|
await page.waitForURL('**/token');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the "ID token" field and submit
|
||||||
|
await page.getByRole('textbox', { name: /id token/i }).fill(token);
|
||||||
|
await page.getByRole('button', { name: /authenticate/i }).click();
|
||||||
|
|
||||||
|
// Wait for the main UI to load
|
||||||
|
await expect(page.getByRole('navigation', { name: 'Navigation' })).toBeVisible({
|
||||||
|
timeout: 15_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setup('authenticate with Headlamp', async ({ page }) => {
|
||||||
|
const username = process.env.AUTHENTIK_USERNAME;
|
||||||
|
const password = process.env.AUTHENTIK_PASSWORD;
|
||||||
|
const token = process.env.HEADLAMP_TOKEN;
|
||||||
|
|
||||||
|
if (username && password) {
|
||||||
|
await authenticateWithOIDC(page, username, password);
|
||||||
|
} else if (token) {
|
||||||
|
await authenticateWithToken(page, token);
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'Set AUTHENTIK_USERNAME + AUTHENTIK_PASSWORD for OIDC auth, or HEADLAMP_TOKEN for token auth'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.context().storageState({ path: AUTH_STATE_PATH });
|
||||||
|
});
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test.describe('Polaris plugin smoke tests', () => {
|
||||||
|
test('sidebar contains Polaris entry', async ({ page }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
// The sidebar is the "Navigation" nav element (not "Appbar Tools")
|
||||||
|
const sidebar = page.getByRole('navigation', { name: 'Navigation' });
|
||||||
|
await expect(sidebar).toBeVisible({ timeout: 15_000 });
|
||||||
|
await expect(sidebar.getByRole('button', { name: 'Polaris' })).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('overview page renders cluster score', async ({ page }) => {
|
||||||
|
await page.goto('/c/main/polaris');
|
||||||
|
|
||||||
|
// SectionHeader renders a heading
|
||||||
|
await expect(page.getByRole('heading', { name: 'Polaris \u2014 Overview' })).toBeVisible();
|
||||||
|
|
||||||
|
// "Cluster Score" section exists with a percentage
|
||||||
|
await expect(page.getByText('Cluster Score')).toBeVisible();
|
||||||
|
await expect(page.locator('main').getByText(/%/).first()).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('namespaces page renders table with namespace buttons', async ({ page }) => {
|
||||||
|
await page.goto('/c/main/polaris/namespaces');
|
||||||
|
|
||||||
|
await expect(page.getByRole('heading', { name: 'Polaris \u2014 Namespaces' })).toBeVisible();
|
||||||
|
|
||||||
|
// Table should have at least one row with a namespace button
|
||||||
|
const table = page.locator('table');
|
||||||
|
await expect(table).toBeVisible();
|
||||||
|
const rows = table.locator('tbody tr');
|
||||||
|
await expect(rows.first()).toBeVisible();
|
||||||
|
|
||||||
|
// Each namespace row should contain a button (now buttons instead of links for drawer)
|
||||||
|
const firstButton = rows.first().locator('button');
|
||||||
|
await expect(firstButton).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('namespace detail drawer opens from table button', async ({ page }) => {
|
||||||
|
await page.goto('/c/main/polaris/namespaces');
|
||||||
|
|
||||||
|
// Click the first namespace button in the table
|
||||||
|
const table = page.locator('table');
|
||||||
|
await expect(table).toBeVisible();
|
||||||
|
const firstButton = table.locator('tbody tr').first().locator('button');
|
||||||
|
const namespaceName = await firstButton.textContent();
|
||||||
|
await firstButton.click();
|
||||||
|
|
||||||
|
// Drawer should open and show the namespace name in the heading
|
||||||
|
await expect(
|
||||||
|
page.getByRole('heading', { name: `Polaris \u2014 ${namespaceName}` })
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// "Namespace Score" section should be present in drawer
|
||||||
|
await expect(page.getByText('Namespace Score')).toBeVisible();
|
||||||
|
|
||||||
|
// Resources table should exist in drawer
|
||||||
|
await expect(page.getByRole('heading', { name: 'Resources' })).toBeVisible();
|
||||||
|
|
||||||
|
// URL hash should be updated with namespace name
|
||||||
|
await expect(page).toHaveURL(/\/polaris\/namespaces#/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('namespace detail drawer closes with Escape key', async ({ page }) => {
|
||||||
|
await page.goto('/c/main/polaris/namespaces');
|
||||||
|
|
||||||
|
// Open the drawer by clicking a namespace button
|
||||||
|
const table = page.locator('table');
|
||||||
|
await expect(table).toBeVisible();
|
||||||
|
const firstButton = table.locator('tbody tr').first().locator('button');
|
||||||
|
const namespaceName = await firstButton.textContent();
|
||||||
|
await firstButton.click();
|
||||||
|
|
||||||
|
// Verify drawer is open
|
||||||
|
await expect(
|
||||||
|
page.getByRole('heading', { name: `Polaris \u2014 ${namespaceName}` })
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Press Escape key
|
||||||
|
await page.keyboard.press('Escape');
|
||||||
|
|
||||||
|
// Drawer should close (heading should not be visible anymore)
|
||||||
|
await expect(
|
||||||
|
page.getByRole('heading', { name: `Polaris \u2014 ${namespaceName}` })
|
||||||
|
).not.toBeVisible();
|
||||||
|
|
||||||
|
// URL hash should be cleared
|
||||||
|
await expect(page).toHaveURL(/\/polaris\/namespaces$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('namespace detail drawer opens from URL hash', async ({ page }) => {
|
||||||
|
// Get a namespace name first
|
||||||
|
await page.goto('/c/main/polaris/namespaces');
|
||||||
|
const table = page.locator('table');
|
||||||
|
await expect(table).toBeVisible();
|
||||||
|
const firstButton = table.locator('tbody tr').first().locator('button');
|
||||||
|
const namespaceName = await firstButton.textContent();
|
||||||
|
|
||||||
|
// Navigate directly to URL with hash
|
||||||
|
await page.goto(`/c/main/polaris/namespaces#${namespaceName}`);
|
||||||
|
|
||||||
|
// Drawer should automatically open with the namespace details
|
||||||
|
await expect(
|
||||||
|
page.getByRole('heading', { name: `Polaris \u2014 ${namespaceName}` })
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// "Namespace Score" section should be present
|
||||||
|
await expect(page.getByText('Namespace Score')).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { test, expect, Page } from '@playwright/test';
|
||||||
|
|
||||||
|
/** Navigate to the Polaris plugin settings page and wait for settings to render. */
|
||||||
|
async function goToPolarisSettings(page: Page) {
|
||||||
|
// Headlamp's plugin settings page is a HOME-context route at /settings/plugins,
|
||||||
|
// not an in-cluster route (/c/main/settings/plugins would 404). Headlamp loads
|
||||||
|
// plugin scripts asynchronously on SPA init. When registerPluginSettings() fires,
|
||||||
|
// it dispatches a Redux action — PluginSettings uses useTypedSelector so it
|
||||||
|
// re-renders automatically once the plugin registers. No preloading needed.
|
||||||
|
await page.goto('/settings/plugins');
|
||||||
|
|
||||||
|
// Wait for the plugin to appear in the settings list. The timeout covers
|
||||||
|
// async plugin script loading + registration.
|
||||||
|
const pluginEntry = page.locator('text=headlamp-polaris').first();
|
||||||
|
await expect(pluginEntry).toBeVisible({ timeout: 30_000 });
|
||||||
|
await pluginEntry.click();
|
||||||
|
|
||||||
|
// Wait for the PolarisSettings component to render
|
||||||
|
await expect(page.getByText('Polaris Settings')).toBeVisible({ timeout: 15_000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe('Polaris plugin settings', () => {
|
||||||
|
test('settings page shows configuration options', async ({ page }) => {
|
||||||
|
await goToPolarisSettings(page);
|
||||||
|
|
||||||
|
// SectionBox title should be visible
|
||||||
|
await expect(page.getByText('Polaris Settings')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('refresh interval setting is configurable', async ({ page }) => {
|
||||||
|
await goToPolarisSettings(page);
|
||||||
|
|
||||||
|
// Find the refresh interval dropdown
|
||||||
|
const intervalSelect = page.locator('select').filter({ hasText: /minute|second/ });
|
||||||
|
await expect(intervalSelect).toBeVisible();
|
||||||
|
|
||||||
|
// Get current value
|
||||||
|
const currentValue = await intervalSelect.inputValue();
|
||||||
|
|
||||||
|
// Change to a different value
|
||||||
|
const newValue = currentValue === '300' ? '600' : '300';
|
||||||
|
await intervalSelect.selectOption(newValue);
|
||||||
|
|
||||||
|
// Value should be updated
|
||||||
|
await expect(intervalSelect).toHaveValue(newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dashboard URL setting is configurable', async ({ page }) => {
|
||||||
|
await goToPolarisSettings(page);
|
||||||
|
|
||||||
|
// Find the dashboard URL input
|
||||||
|
const urlInput = page.getByPlaceholder(/polaris-dashboard/);
|
||||||
|
await expect(urlInput).toBeVisible();
|
||||||
|
|
||||||
|
// Input should have the default proxy URL or custom URL
|
||||||
|
const currentUrl = await urlInput.inputValue();
|
||||||
|
expect(currentUrl).toBeTruthy();
|
||||||
|
|
||||||
|
// Examples text should be visible
|
||||||
|
await expect(page.getByText('Examples:')).toBeVisible();
|
||||||
|
await expect(page.getByText(/K8s proxy:/)).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('connection test button is available', async ({ page }) => {
|
||||||
|
await goToPolarisSettings(page);
|
||||||
|
|
||||||
|
// Find and verify test connection button
|
||||||
|
const testButton = page.getByRole('button', { name: /test connection/i });
|
||||||
|
await expect(testButton).toBeVisible();
|
||||||
|
await expect(testButton).toBeEnabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('connection test works with valid URL', async ({ page }) => {
|
||||||
|
await goToPolarisSettings(page);
|
||||||
|
|
||||||
|
// Click test connection
|
||||||
|
const testButton = page.getByRole('button', { name: /test connection/i });
|
||||||
|
await testButton.click();
|
||||||
|
|
||||||
|
// Wait for either success or error message
|
||||||
|
// Note: This will succeed if Polaris is accessible, fail otherwise
|
||||||
|
await page.waitForSelector('text=/Connected successfully|Connection failed/', {
|
||||||
|
timeout: 15_000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Either success or failure is acceptable (depends on environment)
|
||||||
|
const result = await page.textContent('body');
|
||||||
|
expect(result).toMatch(/(Connected successfully|Connection failed)/);
|
||||||
|
});
|
||||||
|
});
|
||||||
Generated
+18642
File diff suppressed because it is too large
Load Diff
+6
-8
@@ -23,7 +23,9 @@
|
|||||||
"format": "prettier --write src/",
|
"format": "prettier --write src/",
|
||||||
"format:check": "prettier --check src/",
|
"format:check": "prettier --check src/",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest"
|
"test:watch": "vitest",
|
||||||
|
"e2e": "playwright test",
|
||||||
|
"e2e:headed": "playwright test --headed"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
@@ -33,17 +35,13 @@
|
|||||||
"overrides": {
|
"overrides": {
|
||||||
"tar": "^7.5.11",
|
"tar": "^7.5.11",
|
||||||
"undici": "^7.24.3",
|
"undici": "^7.24.3",
|
||||||
"flatted": "^3.4.2",
|
"flatted": "^3.4.2"
|
||||||
"lodash": ">=4.18.0",
|
|
||||||
"picomatch": ">=4.0.4",
|
|
||||||
"vite": ">=6.4.2",
|
|
||||||
"elliptic": ">=6.6.1",
|
|
||||||
"fast-uri": ">=3.1.2"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@kinvolk/headlamp-plugin": "^0.14.0",
|
"@kinvolk/headlamp-plugin": "^0.13.0",
|
||||||
"@mui/material": "^5.15.14",
|
"@mui/material": "^5.15.14",
|
||||||
|
"@playwright/test": "^1.58.2",
|
||||||
"@testing-library/jest-dom": "^6.4.8",
|
"@testing-library/jest-dom": "^6.4.8",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.0",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './e2e',
|
||||||
|
timeout: 30_000,
|
||||||
|
expect: { timeout: 10_000 },
|
||||||
|
fullyParallel: false,
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 1 : 0,
|
||||||
|
reporter: 'list',
|
||||||
|
use: {
|
||||||
|
baseURL: process.env.HEADLAMP_URL || 'https://headlamp.animaniacs.farh.net',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
screenshot: 'only-on-failure',
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{ name: 'setup', testMatch: /auth\.setup\.ts/, timeout: 60_000 },
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: {
|
||||||
|
...devices['Desktop Chrome'],
|
||||||
|
storageState: 'e2e/.auth/state.json',
|
||||||
|
},
|
||||||
|
dependencies: ['setup'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
Generated
+212
-543
File diff suppressed because it is too large
Load Diff
Executable
+210
@@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# deploy-e2e-headlamp.sh
|
||||||
|
#
|
||||||
|
# Deploys a stock Headlamp instance with the polaris plugin loaded via
|
||||||
|
# a ConfigMap volume mount. No custom Docker images — the plugin is built
|
||||||
|
# in CI and injected as a ConfigMap.
|
||||||
|
#
|
||||||
|
# E2E resources are deployed to the `privilegedescalation-dev` namespace. Nothing
|
||||||
|
# persists beyond the test run — teardown cleans up all created resources.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - Plugin built (dist/ exists with plugin-main.js + package.json)
|
||||||
|
# - kubectl configured with cluster access
|
||||||
|
# - RBAC applied: kubectl apply -f deployment/e2e-ci-runner-rbac.yaml
|
||||||
|
#
|
||||||
|
# Environment:
|
||||||
|
# E2E_NAMESPACE — namespace for E2E Headlamp (default: privilegedescalation-dev)
|
||||||
|
# E2E_RELEASE — release/resource name prefix (default: headlamp-e2e)
|
||||||
|
# HEADLAMP_VERSION — Headlamp image tag (default: v0.40.1, pinned to match production)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
DIST_DIR="$REPO_ROOT/dist"
|
||||||
|
|
||||||
|
E2E_NAMESPACE="${E2E_NAMESPACE:-privilegedescalation-dev}"
|
||||||
|
E2E_RELEASE="${E2E_RELEASE:-headlamp-e2e}"
|
||||||
|
HEADLAMP_VERSION="${HEADLAMP_VERSION:-v0.40.1}"
|
||||||
|
|
||||||
|
if [ ! -d "$DIST_DIR" ]; then
|
||||||
|
echo "ERROR: dist/ not found. Run 'npm run build' first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Preflight: verify RBAC before touching the cluster ---
|
||||||
|
echo "Checking RBAC permissions in namespace '${E2E_NAMESPACE}'..."
|
||||||
|
if ! kubectl auth can-i delete configmaps -n "$E2E_NAMESPACE" --quiet 2>/dev/null; then
|
||||||
|
echo "ERROR: Missing RBAC — cannot delete configmaps in namespace '${E2E_NAMESPACE}'." >&2
|
||||||
|
echo " Apply RBAC first: kubectl apply -f deployment/e2e-ci-runner-rbac.yaml" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "=== E2E Headlamp Deployment ==="
|
||||||
|
echo " Image: ghcr.io/headlamp-k8s/headlamp:${HEADLAMP_VERSION}"
|
||||||
|
echo " Namespace: $E2E_NAMESPACE"
|
||||||
|
echo " Release: $E2E_RELEASE"
|
||||||
|
|
||||||
|
# --- Create ConfigMap from built plugin ---
|
||||||
|
echo ""
|
||||||
|
echo "Creating ConfigMap with plugin files..."
|
||||||
|
|
||||||
|
# Delete existing ConfigMap if present (idempotent redeploy)
|
||||||
|
kubectl delete configmap headlamp-polaris-plugin \
|
||||||
|
-n "$E2E_NAMESPACE" --ignore-not-found
|
||||||
|
|
||||||
|
# Create ConfigMap from dist/ contents and package.json
|
||||||
|
kubectl create configmap headlamp-polaris-plugin \
|
||||||
|
-n "$E2E_NAMESPACE" \
|
||||||
|
--from-file="$DIST_DIR" \
|
||||||
|
--from-file=package.json="$REPO_ROOT/package.json"
|
||||||
|
|
||||||
|
# --- Tear down any existing E2E deployment for a clean start ---
|
||||||
|
# kubectl apply without prior deletion only patches in-place: if the pod spec is
|
||||||
|
# unchanged between runs, no new rollout is triggered and a degraded pod keeps
|
||||||
|
# serving. Delete first to guarantee a fresh pod regardless of prior state.
|
||||||
|
echo ""
|
||||||
|
echo "Removing any existing E2E deployment (clean-start)..."
|
||||||
|
kubectl delete deployment "${E2E_RELEASE}" -n "$E2E_NAMESPACE" --ignore-not-found --wait
|
||||||
|
kubectl delete service "${E2E_RELEASE}" -n "$E2E_NAMESPACE" --ignore-not-found --wait
|
||||||
|
kubectl delete serviceaccount "${E2E_RELEASE}" -n "$E2E_NAMESPACE" --ignore-not-found --wait
|
||||||
|
|
||||||
|
# --- Deploy Headlamp via kubectl apply ---
|
||||||
|
echo ""
|
||||||
|
echo "Deploying Headlamp E2E instance..."
|
||||||
|
|
||||||
|
kubectl apply -f - <<EOF
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: ${E2E_RELEASE}
|
||||||
|
namespace: ${E2E_NAMESPACE}
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ${E2E_RELEASE}
|
||||||
|
namespace: ${E2E_NAMESPACE}
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: headlamp
|
||||||
|
app.kubernetes.io/instance: ${E2E_RELEASE}
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: headlamp
|
||||||
|
app.kubernetes.io/instance: ${E2E_RELEASE}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: headlamp
|
||||||
|
app.kubernetes.io/instance: ${E2E_RELEASE}
|
||||||
|
spec:
|
||||||
|
serviceAccountName: ${E2E_RELEASE}
|
||||||
|
automountServiceAccountToken: true
|
||||||
|
securityContext: {}
|
||||||
|
containers:
|
||||||
|
- name: headlamp
|
||||||
|
image: ghcr.io/headlamp-k8s/headlamp:${HEADLAMP_VERSION}
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
privileged: false
|
||||||
|
runAsUser: 100
|
||||||
|
runAsGroup: 101
|
||||||
|
args:
|
||||||
|
- "-in-cluster"
|
||||||
|
- "-in-cluster-context-name=main"
|
||||||
|
- "-plugins-dir=/headlamp/plugins"
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 4466
|
||||||
|
protocol: TCP
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
failureThreshold: 6
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
periodSeconds: 10
|
||||||
|
volumeMounts:
|
||||||
|
- name: polaris-plugin
|
||||||
|
mountPath: /headlamp/plugins/headlamp-polaris
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: polaris-plugin
|
||||||
|
configMap:
|
||||||
|
name: headlamp-polaris-plugin
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ${E2E_RELEASE}
|
||||||
|
namespace: ${E2E_NAMESPACE}
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: headlamp
|
||||||
|
app.kubernetes.io/instance: ${E2E_RELEASE}
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: headlamp
|
||||||
|
app.kubernetes.io/instance: ${E2E_RELEASE}
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: http
|
||||||
|
protocol: TCP
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Waiting for rollout..."
|
||||||
|
kubectl rollout status "deployment/${E2E_RELEASE}" \
|
||||||
|
-n "$E2E_NAMESPACE" --timeout=120s
|
||||||
|
|
||||||
|
# --- Generate a service URL for tests ---
|
||||||
|
SVC_URL="http://${E2E_RELEASE}.${E2E_NAMESPACE}.svc.cluster.local"
|
||||||
|
|
||||||
|
# --- Wait for DNS and HTTP reachability ---
|
||||||
|
# rollout status only confirms the pod is ready per readinessProbe.
|
||||||
|
# Kubernetes Service DNS may still be propagating to the runner pod.
|
||||||
|
# Poll until the service is reachable over HTTP before handing off.
|
||||||
|
echo ""
|
||||||
|
echo "Waiting for ${SVC_URL} to be reachable..."
|
||||||
|
ATTEMPTS=0
|
||||||
|
MAX_ATTEMPTS=24 # 24 × 5s = 120s max
|
||||||
|
until curl -sf --max-time 5 "${SVC_URL}" -o /dev/null 2>/dev/null; do
|
||||||
|
ATTEMPTS=$((ATTEMPTS + 1))
|
||||||
|
if [ "$ATTEMPTS" -ge "$MAX_ATTEMPTS" ]; then
|
||||||
|
echo "ERROR: ${SVC_URL} not reachable after $((MAX_ATTEMPTS * 5))s" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " [${ATTEMPTS}/${MAX_ATTEMPTS}] not yet reachable, retrying in 5s..."
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo "E2E Headlamp is ready at: ${SVC_URL}"
|
||||||
|
echo " export HEADLAMP_URL=${SVC_URL}"
|
||||||
|
|
||||||
|
# --- Generate a token for test auth ---
|
||||||
|
echo ""
|
||||||
|
echo "Creating service account token for E2E auth..."
|
||||||
|
kubectl create serviceaccount headlamp-e2e-test \
|
||||||
|
-n "$E2E_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
TOKEN=$(kubectl create token headlamp-e2e-test -n "$E2E_NAMESPACE" --duration=1h 2>/dev/null || echo "")
|
||||||
|
if [ -n "$TOKEN" ]; then
|
||||||
|
echo " export HEADLAMP_TOKEN=<generated>"
|
||||||
|
echo ""
|
||||||
|
echo "HEADLAMP_URL=${SVC_URL}" > "$REPO_ROOT/.env.e2e"
|
||||||
|
echo "HEADLAMP_TOKEN=${TOKEN}" >> "$REPO_ROOT/.env.e2e"
|
||||||
|
echo "Wrote .env.e2e with HEADLAMP_URL and HEADLAMP_TOKEN"
|
||||||
|
else
|
||||||
|
echo " WARNING: Could not generate token. Set HEADLAMP_TOKEN manually or use OIDC."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "E2E deployment complete."
|
||||||
Executable
+34
@@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# teardown-e2e-headlamp.sh
|
||||||
|
#
|
||||||
|
# Tears down the dedicated E2E Headlamp instance deployed by deploy-e2e-headlamp.sh.
|
||||||
|
#
|
||||||
|
# Environment:
|
||||||
|
# E2E_NAMESPACE — namespace to clean up (default: privilegedescalation-dev)
|
||||||
|
# E2E_RELEASE — release/resource name prefix (default: headlamp-e2e)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|
||||||
|
E2E_NAMESPACE="${E2E_NAMESPACE:-privilegedescalation-dev}"
|
||||||
|
E2E_RELEASE="${E2E_RELEASE:-headlamp-e2e}"
|
||||||
|
|
||||||
|
echo "=== E2E Headlamp Teardown ==="
|
||||||
|
echo " Namespace: $E2E_NAMESPACE"
|
||||||
|
echo " Release: $E2E_RELEASE"
|
||||||
|
|
||||||
|
echo "Removing Headlamp Deployment, Service, and ServiceAccount..."
|
||||||
|
kubectl delete deployment "${E2E_RELEASE}" -n "$E2E_NAMESPACE" --ignore-not-found
|
||||||
|
kubectl delete service "${E2E_RELEASE}" -n "$E2E_NAMESPACE" --ignore-not-found
|
||||||
|
kubectl delete serviceaccount "${E2E_RELEASE}" -n "$E2E_NAMESPACE" --ignore-not-found
|
||||||
|
|
||||||
|
echo "Cleaning up ConfigMap..."
|
||||||
|
kubectl delete configmap headlamp-polaris-plugin -n "$E2E_NAMESPACE" --ignore-not-found
|
||||||
|
|
||||||
|
echo "Cleaning up test service account..."
|
||||||
|
kubectl delete serviceaccount headlamp-e2e-test -n "$E2E_NAMESPACE" --ignore-not-found
|
||||||
|
|
||||||
|
# Clean up local env file
|
||||||
|
rm -f "$REPO_ROOT/.env.e2e"
|
||||||
|
|
||||||
|
echo "Teardown complete."
|
||||||
Reference in New Issue
Block a user