Files
headlamp-sealed-secrets-plugin/src/lib/SealedSecretCRD.ts
T
DevContainer User 9d9bc5f22f fix: remove any types, dead code, unused exports; add comprehensive tests
- Fix handleRotate bug ignoring Result from rotateSealedSecret()
- Fix dead code branch in useControllerHealth
- Replace all `any` types with `unknown` + type guards
- Delete unused functions/exports (452 lines removed)
- Add 18 new test files covering all hooks, libs, and components
- 233 tests passing, zero tsc errors, zero lint issues

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:13:00 +00:00

205 lines
5.3 KiB
TypeScript

/**
* SealedSecret Custom Resource Definition
*/
import { ApiProxy, K8s } from '@kinvolk/headlamp-plugin/lib';
const { apiFactoryWithNamespace } = ApiProxy;
const { KubeObject } = K8s.cluster;
import { AsyncResult, Err, Ok, tryCatchAsync } from '../types';
import {
SealedSecretInterface,
SealedSecretScope,
SealedSecretSpec,
SealedSecretStatus,
} from '../types';
interface CRDVersion {
name: string;
storage?: boolean;
served?: boolean;
}
/**
* SealedSecret CRD class
* Represents a Bitnami Sealed Secret resource in the cluster
*/
export class SealedSecret extends KubeObject<SealedSecretInterface> {
/**
* Default API version (fallback)
*/
static readonly DEFAULT_VERSION = 'bitnami.com/v1alpha1';
/**
* Cached detected API version
*/
private static detectedVersion: string | null = null;
/**
* API endpoint for SealedSecret resources
* bitnami.com/v1alpha1/sealedsecrets
*/
static apiEndpoint = apiFactoryWithNamespace('bitnami.com', 'v1alpha1', 'sealedsecrets');
/**
* Class name used for Headlamp registration
*/
static get className(): string {
return 'SealedSecret';
}
/**
* Get the SealedSecret spec
*/
get spec(): SealedSecretSpec {
return this.jsonData.spec;
}
/**
* Get the SealedSecret status
*/
get status(): SealedSecretStatus | undefined {
return this.jsonData.status;
}
/**
* Get the scope of this SealedSecret (strict, namespace-wide, or cluster-wide)
*/
get scope(): SealedSecretScope {
const annotations = this.metadata.annotations || {};
if (annotations['sealedsecrets.bitnami.com/cluster-wide'] === 'true') {
return 'cluster-wide';
}
if (annotations['sealedsecrets.bitnami.com/namespace-wide'] === 'true') {
return 'namespace-wide';
}
return 'strict';
}
/**
* Get the count of encrypted keys
*/
get encryptedKeysCount(): number {
return Object.keys(this.spec.encryptedData || {}).length;
}
/**
* Check if the SealedSecret is synced
*/
get isSynced(): boolean {
const syncCondition = this.status?.conditions?.find(c => c.type === 'Synced');
return syncCondition?.status === 'True';
}
/**
* Get the sync status condition
*/
get syncCondition() {
return this.status?.conditions?.find(c => c.type === 'Synced');
}
/**
* Get the sync status message
*/
get syncMessage(): string {
const condition = this.syncCondition;
if (!condition) {
return 'Unknown';
}
// Ensure we always return a string, not an object
const message = condition.message || condition.reason || condition.status;
return String(message || 'Unknown');
}
/**
* Detect the API version available in the cluster
*
* Queries the SealedSecrets CRD to determine which API version is installed
* and preferred. Returns the storage version (the version used for persisting
* objects in etcd).
*
* @returns Result containing the API version string (e.g., "bitnami.com/v1alpha1")
*/
static async detectApiVersion(): AsyncResult<string, string> {
// Return cached version if available
if (this.detectedVersion) {
return Ok(this.detectedVersion);
}
const result = await tryCatchAsync(async () => {
// Query the CRD to get available versions using Headlamp's API proxy
const crd = await ApiProxy.request(
'/apis/apiextensions.k8s.io/v1/customresourcedefinitions/sealedsecrets.bitnami.com'
);
// Find the storage version (the version used for persistence)
const storageVersion = crd.spec?.versions?.find((v: CRDVersion) => v.storage === true);
if (storageVersion) {
const version = `${crd.spec.group}/${storageVersion.name}`;
this.detectedVersion = version;
return version;
}
// Fallback to first served version if no storage version found
const servedVersion = crd.spec?.versions?.find((v: CRDVersion) => v.served === true);
if (servedVersion) {
const version = `${crd.spec.group}/${servedVersion.name}`;
this.detectedVersion = version;
return version;
}
// Ultimate fallback to default
return this.DEFAULT_VERSION;
});
if (result.ok === false) {
return Err(result.error.message);
}
return Ok(result.value);
}
/**
* Get API endpoint with auto-detected version
*
* Automatically detects and uses the correct API version from the cluster.
* Falls back to default version (v1alpha1) if detection fails.
*
* @returns API endpoint configured with the detected version
*/
static async getApiEndpoint() {
const versionResult = await this.detectApiVersion();
if (versionResult.ok) {
const [group, version] = versionResult.value.split('/');
return apiFactoryWithNamespace(group, version, 'sealedsecrets');
}
// Fallback to default endpoint
return this.apiEndpoint;
}
/**
* Get the detected API version
*
* Returns the cached detected version or null if not yet detected.
*
* @returns The detected API version string or null
*/
static getDetectedVersion(): string | null {
return this.detectedVersion;
}
/**
* Clear the cached API version
*
* Forces re-detection on next call to detectApiVersion().
* Useful for refreshing after CRD updates.
*/
static clearVersionCache(): void {
this.detectedVersion = null;
}
}