docs: add architecture decision records for service proxy, error boundary, settings, and exemptions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
# ADR-002: Service Proxy as Single Data Source
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-03-05
|
||||
**Deciders:** Plugin maintainers
|
||||
|
||||
## Context
|
||||
|
||||
The Polaris plugin needs audit data from the Polaris dashboard. Polaris dashboard exposes a `/results.json` endpoint containing pre-computed audit results for all workloads in the cluster.
|
||||
|
||||
Several approaches were considered for obtaining this data:
|
||||
|
||||
1. Query Kubernetes resources directly and re-implement Polaris audit logic
|
||||
2. Use the Polaris CLI as a sidecar container
|
||||
3. Use the Polaris dashboard's REST API via Kubernetes service proxy
|
||||
4. Embed Polaris as a Go/JS library
|
||||
|
||||
The service proxy approach uses the Kubernetes API server's built-in service proxy capability to reach the Polaris dashboard at `/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json`. This means the plugin receives pre-computed audit results without needing to understand Polaris internals.
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- Headlamp plugins can make API calls via `ApiProxy.request()` (proxied through the Headlamp backend) or direct `fetch()` for external URLs
|
||||
- The Polaris dashboard service name, namespace, and port may vary across cluster setups
|
||||
- Some users may run Polaris externally (not in-cluster)
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Retrieve all audit data in a single API call
|
||||
- Support configurable endpoint URL for different cluster configurations
|
||||
- Support external Polaris instances via full HTTP/HTTPS URLs
|
||||
- Work through existing Kubernetes RBAC without additional configuration
|
||||
|
||||
## Decision
|
||||
|
||||
Use **`ApiProxy.request()`** to fetch from the Polaris dashboard service proxy as the single data source for all audit data.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- Default endpoint: `/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json`
|
||||
- URL is configurable via plugin settings stored in `localStorage` (key: `polaris-plugin-dashboard-url`)
|
||||
- For full URLs starting with `http://` or `https://`, use browser `fetch()` directly to support external Polaris instances
|
||||
- `getPolarisApiPath()` in `polaris.ts` resolves the configured URL, with `isFullUrl()` determining the fetch strategy
|
||||
- Single fetch shared across all views via `PolarisDataProvider` (see ADR-001)
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- ✅ **Single API call gets all audit data** - One request to `/results.json` returns scores for every workload
|
||||
- ✅ **No need to understand Polaris internals** - Plugin receives pre-computed results, no audit logic duplication
|
||||
- ✅ **Works through existing K8s RBAC** - Service proxy uses standard Kubernetes RBAC (`get` on `services/proxy`)
|
||||
- ✅ **Configurable endpoint** - Users can customize namespace, service name, or point to an external instance
|
||||
- ✅ **Minimal plugin complexity** - No CRD watches, no custom controllers, no library dependencies
|
||||
|
||||
### Negative
|
||||
|
||||
- ❌ **Requires Polaris dashboard to be deployed and accessible** - Plugin has no data without the dashboard
|
||||
- **Mitigated by:** Clear error messages guiding users to install Polaris (404/503 → install guidance)
|
||||
- ❌ **Single point of failure** - If the dashboard service is down, the plugin shows no data
|
||||
- **Mitigated by:** Status-code-specific error messages (403 → RBAC guidance, 404/503 → deployment guidance)
|
||||
- ❌ **Dashboard must be running continuously** - Unlike CRD-based approaches where data persists
|
||||
- **Mitigated by:** Polaris dashboard is typically deployed as a long-running service
|
||||
|
||||
### Neutral
|
||||
|
||||
- The Polaris dashboard is a lightweight Go service with minimal resource requirements
|
||||
- Service proxy is a standard Kubernetes pattern used by many tools (kubectl port-forward, dashboard proxying)
|
||||
- The configurable URL approach supports both in-cluster and external Polaris deployments
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Option 1: Query Polaris CRDs Directly
|
||||
|
||||
**Pros:**
|
||||
|
||||
- No dependency on Polaris dashboard being running
|
||||
- Data persists in CRDs even if dashboard restarts
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Polaris audit logic is complex and would need to be duplicated in the plugin
|
||||
- Would require watching multiple CRD types
|
||||
- Plugin would need to be updated whenever Polaris changes its audit rules
|
||||
|
||||
**Decision:** Rejected (would duplicate Polaris internals, maintenance burden)
|
||||
|
||||
### Option 2: Use Polaris CLI as a Sidecar
|
||||
|
||||
**Pros:**
|
||||
|
||||
- CLI has full audit capability
|
||||
- Could run audits on-demand
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Adds operational complexity (sidecar container management)
|
||||
- Not suitable for a browser-based plugin (CLI runs server-side)
|
||||
- Would require a separate backend service to bridge CLI output to the plugin
|
||||
|
||||
**Decision:** Rejected (operational complexity, not suitable for plugin architecture)
|
||||
|
||||
### Option 3: Embed Polaris as a Library
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Full control over audit execution
|
||||
- No external service dependency
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Polaris is a Go library, not available in JavaScript/TypeScript plugin runtime
|
||||
- Would massively increase bundle size
|
||||
- Would duplicate the entire Polaris engine
|
||||
|
||||
**Decision:** Rejected (not available in plugin runtime, massive dependency)
|
||||
|
||||
## References
|
||||
|
||||
- [Kubernetes Service Proxy](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster-services/)
|
||||
- [Polaris Dashboard](https://polaris.docs.fairwinds.com/dashboard/)
|
||||
- [Plugin Implementation](../../api/polaris.ts)
|
||||
- [Data Context](../../api/PolarisDataContext.tsx)
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change |
|
||||
| ---------- | ----------- | ---------------- |
|
||||
| 2026-03-05 | Plugin Team | Initial decision |
|
||||
@@ -0,0 +1,124 @@
|
||||
# ADR-003: Error Boundary as Class Component Exception
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-03-05
|
||||
**Deciders:** Plugin maintainers
|
||||
|
||||
## Context
|
||||
|
||||
The plugin follows a strict "functional components only" convention (see CLAUDE.md). However, React error boundaries require the `getDerivedStateFromError` and `componentDidCatch` lifecycle methods, which are only available on class components. As of React 18, there is no hooks-based error boundary API, and the React team has not announced a timeline for one.
|
||||
|
||||
The plugin registers components at multiple Headlamp integration points:
|
||||
|
||||
- Routes (dashboard, namespaces list)
|
||||
- Detail view sections (Deployment, StatefulSet, DaemonSet, Job, CronJob)
|
||||
- App bar action (score badge)
|
||||
- Plugin settings page
|
||||
|
||||
An unhandled error in any one of these registered components would crash the entire Headlamp UI, not just the plugin. This is because Headlamp renders plugin components inline within its own React tree.
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- React does not support error boundaries via hooks or functional components
|
||||
- The `react-error-boundary` library is not available as a peer dependency in Headlamp plugins
|
||||
- Plugin errors must not crash the host Headlamp application
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Catch and contain errors in all plugin-registered components
|
||||
- Provide user-friendly error display with recovery option
|
||||
- Isolate failures per registration point (an error in the app bar badge should not affect the dashboard view)
|
||||
|
||||
## Decision
|
||||
|
||||
Define **`PolarisErrorBoundary`** as a class component directly in `index.tsx`. This is the sole exception to the functional-component-only convention.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- `PolarisErrorBoundary` is a React class component with `getDerivedStateFromError` and `componentDidCatch`
|
||||
- Every registered component (routes, detail sections, app bar action) is wrapped in this boundary
|
||||
- On error, displays a user-friendly fallback with an option to retry
|
||||
- Error details are logged to the console for debugging
|
||||
- The boundary is minimal (~30 lines) and co-located in `index.tsx` to minimize the convention violation
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- ✅ **Prevents plugin errors from crashing Headlamp** - Errors are caught and contained within the boundary
|
||||
- ✅ **User-friendly error display** - Shows a clear message with recovery option instead of a blank screen
|
||||
- ✅ **Isolated per registration point** - Each registered component has its own boundary instance
|
||||
- ✅ **No external dependencies** - Uses built-in React class component API
|
||||
- ✅ **Minimal implementation** - Small class component, easy to understand and maintain
|
||||
|
||||
### Negative
|
||||
|
||||
- ❌ **Breaks functional-only convention** - One class component in an otherwise functional codebase
|
||||
- **Mitigated by:** Kept minimal and co-located in `index.tsx` with clear documentation of why
|
||||
- ❌ **Class component syntax less familiar to contributors** - Modern React developers may not be fluent in class components
|
||||
- **Mitigated by:** The boundary is simple (no complex state, no lifecycle methods beyond error handling)
|
||||
|
||||
### Neutral
|
||||
|
||||
- This is a well-known React limitation acknowledged by the React team
|
||||
- Many React projects that otherwise use functional components make this same exception for error boundaries
|
||||
- The pattern is explicitly documented in the React documentation
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Option 1: No Error Boundary
|
||||
|
||||
**Pros:**
|
||||
|
||||
- No class component needed
|
||||
- Simpler code
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Plugin errors would crash the entire Headlamp UI
|
||||
- Users would see a blank screen with no recovery option
|
||||
- Poor user experience and potential data loss in other Headlamp features
|
||||
|
||||
**Decision:** Rejected (unacceptable risk of crashing host application)
|
||||
|
||||
### Option 2: react-error-boundary Library
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Provides a functional component API for error boundaries
|
||||
- Well-maintained, widely used library
|
||||
- Supports error recovery and reset
|
||||
|
||||
**Cons:**
|
||||
|
||||
- External dependency not available in Headlamp plugin runtime
|
||||
- Cannot add peer dependencies that Headlamp does not provide
|
||||
|
||||
**Decision:** Rejected (dependency not available in plugin environment)
|
||||
|
||||
### Option 3: Wait for React Hooks-Based Error Boundary API
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Would maintain functional-only convention
|
||||
- Official React solution
|
||||
|
||||
**Cons:**
|
||||
|
||||
- No timeline from the React team for this feature
|
||||
- Plugin needs error boundaries now, not at some future date
|
||||
- May never be implemented (React team has not committed to this)
|
||||
|
||||
**Decision:** Rejected (no timeline, cannot ship without error boundaries)
|
||||
|
||||
## References
|
||||
|
||||
- [React Error Boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)
|
||||
- [React getDerivedStateFromError](https://react.dev/reference/react/Component#static-getderivedstatefromerror)
|
||||
- [Plugin Implementation](../../../src/index.tsx)
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change |
|
||||
| ---------- | ----------- | ---------------- |
|
||||
| 2026-03-05 | Plugin Team | Initial decision |
|
||||
@@ -0,0 +1,132 @@
|
||||
# ADR-004: Browser localStorage for User Settings
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-03-05
|
||||
**Deciders:** Plugin maintainers
|
||||
|
||||
## Context
|
||||
|
||||
The plugin has two user-configurable settings:
|
||||
|
||||
1. **Auto-refresh interval** (1-30 minutes, default 5 minutes) - how often to re-fetch Polaris audit data
|
||||
2. **Polaris dashboard URL** - endpoint for the Polaris dashboard service (supports custom namespaces/service names and external instances)
|
||||
|
||||
These are per-user preferences, not cluster configuration. They should persist across browser sessions and page reloads.
|
||||
|
||||
Several storage mechanisms are available:
|
||||
|
||||
- Browser `localStorage` - simple key-value store, persistent, synchronous API
|
||||
- Headlamp `ConfigStore` API - backed by Redux, reactive, integrated with Headlamp's state management
|
||||
- React state only - in-memory, lost on page reload
|
||||
- URL query parameters - visible in URL, lost on navigation
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- Settings need to be reactive: the `PolarisDataProvider` must detect changes made on the settings page
|
||||
- Headlamp provides `registerPluginSettings` which renders a settings component - the settings page and the data provider are separate component trees
|
||||
- Only two scalar values need to be stored
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Persist settings across browser sessions and page reloads
|
||||
- React to setting changes without requiring a full page reload
|
||||
- Simple implementation for two scalar values
|
||||
- Work with Headlamp's `registerPluginSettings` API
|
||||
|
||||
## Decision
|
||||
|
||||
Use **browser `localStorage`** directly for persisting plugin settings.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- Refresh interval stored at key `polaris-plugin-refresh-interval` (value in minutes as string)
|
||||
- Dashboard URL stored at key `polaris-plugin-dashboard-url` (URL string or empty for default)
|
||||
- `PolarisSettings` component (registered via `registerPluginSettings`) reads/writes these keys
|
||||
- `PolarisDataProvider` polls `localStorage` via `setInterval` every 1 second to detect setting changes
|
||||
- Helper functions in `polaris.ts` (`getRefreshInterval()`, `getPolarisApiPath()`) encapsulate localStorage access
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- ✅ **Simple and well-understood API** - `localStorage.getItem`/`setItem` is straightforward
|
||||
- ✅ **Persists across browser sessions** - Data survives page reloads, tab closes, browser restarts
|
||||
- ✅ **No dependency on Headlamp store internals** - Decoupled from Headlamp's Redux implementation
|
||||
- ✅ **Works with `registerPluginSettings`** - Settings page and data provider communicate via shared localStorage keys
|
||||
- ✅ **Minimal code** - No state management boilerplate for two simple values
|
||||
|
||||
### Negative
|
||||
|
||||
- ❌ **Not reactive by default** - localStorage has no built-in change notification within the same tab
|
||||
- **Mitigated by:** 1-second polling interval in `PolarisDataProvider` to detect changes
|
||||
- ❌ **Settings are browser-local** - Not synced across devices or browsers
|
||||
- **Mitigated by:** These are user preferences, browser-local storage is appropriate
|
||||
- ❌ **No type safety on stored values** - All values stored as strings
|
||||
- **Mitigated by:** Helper functions with `parseInt` and default values handle type conversion
|
||||
|
||||
### Neutral
|
||||
|
||||
- localStorage has a 5-10 MB limit per origin, more than sufficient for two string values
|
||||
- The 1-second polling interval has negligible performance impact (reading two string keys)
|
||||
- The `storage` event could detect cross-tab changes but does not fire for same-tab writes
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Option 1: Headlamp ConfigStore API
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Integrated with Headlamp's Redux store
|
||||
- Reactive (Redux state changes trigger re-renders)
|
||||
- Type-safe with TypeScript
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Couples plugin to Headlamp's internal Redux store implementation
|
||||
- More complex API for two scalar values
|
||||
- ConfigStore API may change across Headlamp versions
|
||||
|
||||
**Decision:** Not chosen (localStorage is simpler for two scalar values, avoids coupling to Headlamp's Redux internals)
|
||||
|
||||
### Option 2: React State Only (No Persistence)
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Simplest implementation
|
||||
- Fully reactive
|
||||
- No side effects
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Settings lost on page reload - users must reconfigure every session
|
||||
- Poor user experience for frequently changed settings
|
||||
|
||||
**Decision:** Rejected (settings must persist across page reloads)
|
||||
|
||||
### Option 3: URL Query Parameters
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Shareable via URL
|
||||
- No storage API needed
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Lost on navigation to different routes
|
||||
- Clutters the URL
|
||||
- Not suitable for persistent settings
|
||||
|
||||
**Decision:** Rejected (does not persist across navigation)
|
||||
|
||||
## References
|
||||
|
||||
- [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)
|
||||
- [Headlamp Plugin Settings](https://headlamp.dev/docs/latest/development/plugins/)
|
||||
- [Settings Component](../../../src/components/PolarisSettings.tsx)
|
||||
- [Data Context](../../../src/api/PolarisDataContext.tsx)
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change |
|
||||
| ---------- | ----------- | ---------------- |
|
||||
| 2026-03-05 | Plugin Team | Initial decision |
|
||||
@@ -0,0 +1,131 @@
|
||||
# ADR-005: Annotation-Based Exemption Management
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-03-05
|
||||
**Deciders:** Plugin maintainers
|
||||
|
||||
## Context
|
||||
|
||||
Polaris allows exempting specific workloads from audit checks. When a workload is exempt, Polaris skips all audit checks for that resource. The exemption mechanism uses the annotation `polaris.fairwinds.com/exempt=true` on the workload resource.
|
||||
|
||||
The plugin needs to let users manage these exemptions directly from the Headlamp UI. Several approaches were considered:
|
||||
|
||||
1. Use Polaris's native annotation-based exemption mechanism
|
||||
2. Create a separate exemption ConfigMap
|
||||
3. Define a custom ExemptionPolicy CRD
|
||||
4. Read-only display with kubectl instructions
|
||||
|
||||
**Constraints:**
|
||||
|
||||
- Polaris only recognizes `polaris.fairwinds.com/exempt` annotations on workload resources
|
||||
- The plugin is otherwise read-only (this would be the only write operation)
|
||||
- Users need appropriate RBAC permissions to patch workload resources
|
||||
- Supported workload types: Deployments, StatefulSets, DaemonSets, Jobs, CronJobs
|
||||
|
||||
**Requirements:**
|
||||
|
||||
- Allow users to toggle exemptions for workloads from the Headlamp UI
|
||||
- Use a mechanism that Polaris actually respects (exemptions must take effect on next scan)
|
||||
- Support all workload types that Polaris audits
|
||||
- Respect Kubernetes RBAC (only authorized users can manage exemptions)
|
||||
|
||||
## Decision
|
||||
|
||||
Use **Polaris's native annotation-based exemption mechanism**. The `ExemptionManager` component patches `polaris.fairwinds.com/exempt` annotations onto workload resources via `ApiProxy.request`.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
- `ExemptionManager` component in `ExemptionManager.tsx` provides a toggle UI for each workload
|
||||
- Exemptions are applied via `ApiProxy.request` with `method: 'PATCH'` and `Content-Type: application/strategic-merge-patch+json`
|
||||
- Patch payload sets `metadata.annotations["polaris.fairwinds.com/exempt"]` to `"true"` or removes the annotation
|
||||
- This is the only write operation in the entire plugin
|
||||
- RBAC is enforced by Kubernetes - users without `patch` permission on the workload resource will receive a 403 error
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- ✅ **Uses Polaris's own exemption mechanism** - No custom storage or translation layer needed
|
||||
- ✅ **Exemptions visible in standard kubectl output** - `kubectl get deployment -o yaml` shows the annotation
|
||||
- ✅ **No additional CRDs or ConfigMaps** - No custom resources to manage or clean up
|
||||
- ✅ **Polaris automatically respects annotations** - Exemptions take effect on the next audit scan
|
||||
- ✅ **Standard Kubernetes pattern** - Annotations are the idiomatic way to attach metadata to resources
|
||||
|
||||
### Negative
|
||||
|
||||
- ❌ **Requires write RBAC on workload resources** - Users need `patch` permission on deployments, statefulsets, etc.
|
||||
- **Mitigated by:** RBAC scoping - only users with patch permission can manage exemptions; UI shows clear error for 403
|
||||
- ❌ **Annotation changes not versioned or auditable** - Beyond standard Kubernetes resource history
|
||||
- **Mitigated by:** Kubernetes audit logging captures annotation patches; resource `metadata.managedFields` tracks changes
|
||||
- ❌ **Only supports full-resource exemption** - Cannot exempt individual checks (Polaris limitation)
|
||||
- **Mitigated by:** This matches Polaris's own annotation-level granularity
|
||||
|
||||
### Neutral
|
||||
|
||||
- Strategic merge patch is the standard Kubernetes patching strategy for adding/removing annotations
|
||||
- The annotation key (`polaris.fairwinds.com/exempt`) is defined by Polaris and unlikely to change
|
||||
- Exemption state is stored on the workload resource itself, so it moves with the resource if migrated
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Option 1: Separate Exemption ConfigMap
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Centralizes all exemptions in one place
|
||||
- Does not require write access to workload resources
|
||||
- Easy to audit all exemptions at once
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Polaris does not read exemptions from ConfigMaps - it only checks annotations
|
||||
- Would require a custom reconciliation controller to sync ConfigMap entries to annotations
|
||||
- Adds operational complexity
|
||||
|
||||
**Decision:** Rejected (Polaris does not support ConfigMap-based exemptions)
|
||||
|
||||
### Option 2: Custom ExemptionPolicy CRD
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Dedicated resource type for exemption management
|
||||
- Could support per-check exemptions, time-based exemptions, etc.
|
||||
- Clean separation of concerns
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Over-engineering for what is essentially an annotation toggle
|
||||
- Would require a custom controller to reconcile CRDs to annotations
|
||||
- Adds CRD installation as a prerequisite
|
||||
- Polaris still needs the annotation, so the CRD would be an indirection layer
|
||||
|
||||
**Decision:** Rejected (over-engineering for annotation toggle, would require a controller)
|
||||
|
||||
### Option 3: Read-Only Display with kubectl Instructions
|
||||
|
||||
**Pros:**
|
||||
|
||||
- No write operations in the plugin
|
||||
- No RBAC requirements beyond read access
|
||||
- Simpler implementation
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Poor user experience - users must switch to terminal to manage exemptions
|
||||
- Defeats the purpose of a UI plugin
|
||||
- Error-prone (users may mistype annotation keys)
|
||||
|
||||
**Decision:** Rejected (poor UX compared to in-UI toggle)
|
||||
|
||||
## References
|
||||
|
||||
- [Polaris Exemptions Documentation](https://polaris.docs.fairwinds.com/customization/exemptions/)
|
||||
- [Kubernetes Annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)
|
||||
- [Strategic Merge Patch](https://kubernetes.io/docs/tasks/manage-kubernetes-resources/update-api-object-kubectl-patch/#use-a-strategic-merge-patch-to-update-a-deployment)
|
||||
- [Plugin Implementation](../../../src/components/ExemptionManager.tsx)
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Change |
|
||||
| ---------- | ----------- | ---------------- |
|
||||
| 2026-03-05 | Plugin Team | Initial decision |
|
||||
@@ -70,8 +70,10 @@ What becomes easier or more difficult to do because of this change?
|
||||
| ADR | Title | Status | Date |
|
||||
| ------------------------------------- | -------------------------------------- | -------- | ---------- |
|
||||
| [001](001-react-context-for-state.md) | Use React Context for State Management | Accepted | 2026-02-12 |
|
||||
|
||||
**Note:** Additional ADRs documenting other significant decisions (service proxy approach, drawer navigation, MUI import restrictions) can be created following the template above.
|
||||
| [002](002-service-proxy-data-source.md) | Service Proxy as Single Data Source | Accepted | 2026-03-05 |
|
||||
| [003](003-error-boundary-class-component.md) | Error Boundary as Class Component Exception | Accepted | 2026-03-05 |
|
||||
| [004](004-localstorage-settings.md) | Browser localStorage for User Settings | Accepted | 2026-03-05 |
|
||||
| [005](005-annotation-exemption-management.md) | Annotation-Based Exemption Management | Accepted | 2026-03-05 |
|
||||
|
||||
## Creating a New ADR
|
||||
|
||||
|
||||
Reference in New Issue
Block a user