diff --git a/src/common/k8s-api/__tests__/crd.test.ts b/src/common/k8s-api/__tests__/crd.test.ts index 459a7f67d9..a981cdda12 100644 --- a/src/common/k8s-api/__tests__/crd.test.ts +++ b/src/common/k8s-api/__tests__/crd.test.ts @@ -20,92 +20,108 @@ */ import { CustomResourceDefinition } from "../endpoints"; -import type { KubeObjectMetadata } from "../kube-object"; describe("Crds", () => { describe("getVersion", () => { - it("should get the first version name from the list of versions", () => { + it("should throw if none of the versions are served", () => { const crd = new CustomResourceDefinition({ - apiVersion: "foo", + apiVersion: "apiextensions.k8s.io/v1", kind: "CustomResourceDefinition", - metadata: {} as KubeObjectMetadata, + metadata: { + name: "foo", + resourceVersion: "12345", + uid: "12345", + }, + spec: { + versions: [ + { + name: "123", + served: false, + storage: false, + }, + { + name: "1234", + served: false, + storage: false, + }, + ], + }, }); - crd.spec = { - versions: [ - { - name: "123", - served: false, - storage: false, - } - ] - } as any; + expect(() => crd.getVersion()).toThrowError("Failed to find a version for CustomResourceDefinition foo"); + }); + + it("should should get the version that is both served and stored", () => { + const crd = new CustomResourceDefinition({ + apiVersion: "apiextensions.k8s.io/v1", + kind: "CustomResourceDefinition", + metadata: { + name: "foo", + resourceVersion: "12345", + uid: "12345", + }, + spec: { + versions: [ + { + name: "123", + served: true, + storage: true, + }, + { + name: "1234", + served: false, + storage: false, + }, + ], + }, + }); expect(crd.getVersion()).toBe("123"); }); - it("should get the first version name from the list of versions (length 2)", () => { + it("should should get the version that is both served and stored even with version field", () => { const crd = new CustomResourceDefinition({ - apiVersion: "foo", + apiVersion: "apiextensions.k8s.io/v1", kind: "CustomResourceDefinition", - metadata: {} as KubeObjectMetadata, + metadata: { + name: "foo", + resourceVersion: "12345", + uid: "12345", + }, + spec: { + version: "abc", + versions: [ + { + name: "123", + served: true, + storage: true, + }, + { + name: "1234", + served: false, + storage: false, + }, + ], + }, }); - crd.spec = { - versions: [ - { - name: "123", - served: false, - storage: false, - }, - { - name: "1234", - served: false, - storage: false, - } - ] - } as any; - expect(crd.getVersion()).toBe("123"); }); - it("should get the first version name from the list of versions (length 2) even with version field", () => { + it("should get the version name from the version field", () => { const crd = new CustomResourceDefinition({ - apiVersion: "foo", + apiVersion: "apiextensions.k8s.io/v1beta1", kind: "CustomResourceDefinition", - metadata: {} as KubeObjectMetadata, + metadata: { + name: "foo", + resourceVersion: "12345", + uid: "12345", + }, + spec: { + version: "abc", + } }); - crd.spec = { - version: "abc", - versions: [ - { - name: "123", - served: false, - storage: false, - }, - { - name: "1234", - served: false, - storage: false, - } - ] - } as any; - - expect(crd.getVersion()).toBe("123"); - }); - - it("should get the first version name from the version field", () => { - const crd = new CustomResourceDefinition({ - apiVersion: "foo", - kind: "CustomResourceDefinition", - metadata: {} as KubeObjectMetadata, - }); - - crd.spec = { - version: "abc" - } as any; - expect(crd.getVersion()).toBe("abc"); }); }); diff --git a/src/common/k8s-api/endpoints/crd.api.ts b/src/common/k8s-api/endpoints/crd.api.ts index 0c76ee1b97..98c8d38df0 100644 --- a/src/common/k8s-api/endpoints/crd.api.ts +++ b/src/common/k8s-api/endpoints/crd.api.ts @@ -40,11 +40,19 @@ type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & { JSONPath: string; }; +export interface CRDVersion { + name: string; + served: boolean; + storage: boolean; + schema?: object; // required in v1 but not present in v1beta + additionalPrinterColumns?: AdditionalPrinterColumnsV1[]; +} + export interface CustomResourceDefinition { spec: { group: string; /** - * This is deprecated for apiextensions.k8s.io/v1 but used previously + * @deprecated for apiextensions.k8s.io/v1 but used previously */ version?: string; names: { @@ -54,19 +62,19 @@ export interface CustomResourceDefinition { listKind: string; }; scope: "Namespaced" | "Cluster" | string; - validation?: any; - versions?: { - name: string; - served: boolean; - storage: boolean; - schema?: unknown; // required in v1 but not present in v1beta - additionalPrinterColumns?: AdditionalPrinterColumnsV1[] - }[]; + /** + * @deprecated for apiextensions.k8s.io/v1 but used previously + */ + validation?: object; + versions?: CRDVersion[]; conversion: { strategy?: string; webhook?: any; }; - additionalPrinterColumns?: AdditionalPrinterColumnsV1Beta[]; // removed in v1 + /** + * @deprecated for apiextensions.k8s.io/v1 but used previously + */ + additionalPrinterColumns?: AdditionalPrinterColumnsV1Beta[]; }; status: { conditions: { @@ -141,25 +149,38 @@ export class CustomResourceDefinition extends KubeObject { return this.spec.scope; } - getVersion() { + getPreferedVersion(): CRDVersion { // Prefer the modern `versions` over the legacy `version` - for (const version of this.spec.versions || []) { - /** - * If the version is not served then 404 errors will occur - * We should also prefer the storage version - */ - if (version.served && version.storage) { - return version.name; + if (this.spec.versions) { + for (const version of this.spec.versions) { + /** + * If the version is not served then 404 errors will occur + * We should also prefer the storage version + */ + if (version.served && version.storage) { + return version; + } } - } + } else if (this.spec.version) { + const { additionalPrinterColumns: apc } = this.spec; + const additionalPrinterColumns = apc?.map(({ JSONPath, ...apc}) => ({ ...apc, jsonPath: JSONPath })); - if (this.spec.version) { - return this.spec.version; + return { + name: this.spec.version, + served: true, + storage: true, + schema: this.spec.validation, + additionalPrinterColumns, + }; } throw new Error(`Failed to find a version for CustomResourceDefinition ${this.metadata.name}`); } + getVersion() { + return this.getPreferedVersion().name; + } + isNamespaced() { return this.getScope() === "Namespaced"; } @@ -177,17 +198,14 @@ export class CustomResourceDefinition extends KubeObject { } getPrinterColumns(ignorePriority = true): AdditionalPrinterColumnsV1[] { - const columns = this.spec.versions?.find(a => this.getVersion() == a.name)?.additionalPrinterColumns - ?? this.spec.additionalPrinterColumns?.map(({ JSONPath, ...rest }) => ({ ...rest, jsonPath: JSONPath })) // map to V1 shape - ?? []; + const columns = this.getPreferedVersion().additionalPrinterColumns ?? []; return columns - .filter(column => column.name != "Age") - .filter(column => ignorePriority ? true : !column.priority); + .filter(column => column.name != "Age" && (ignorePriority || !column.priority)); } getValidation() { - return JSON.stringify(this.spec.validation ?? this.spec.versions?.[0]?.schema, null, 2); + return JSON.stringify(this.getPreferedVersion().schema, null, 2); } getConditions() { diff --git a/src/renderer/utils/createStorage.ts b/src/renderer/utils/createStorage.ts index 854a7aef2f..f3827d52dd 100755 --- a/src/renderer/utils/createStorage.ts +++ b/src/renderer/utils/createStorage.ts @@ -28,6 +28,7 @@ import { StorageHelper } from "./storageHelper"; import { ClusterStore } from "../../common/cluster-store"; import logger from "../../main/logger"; import { getHostedClusterId, getPath } from "../../common/utils"; +import { isTestEnv } from "../../common/vars"; const storage = observable({ initialized: false, @@ -50,7 +51,7 @@ export function createAppStorage(key: string, defaultValue: T, clusterId?: st const fileName = `${clusterId ?? "app"}.json`; const filePath = path.resolve(folder, fileName); - if (!storage.initialized) { + if (!storage.initialized && !isTestEnv) { init(); // called once per cluster-view }