mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Simplify CRD KubeApi registrations
- Switch to auto injectable registrations Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
0f1f030a06
commit
886c812679
@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
|
||||
import hostedClusterInjectable from "../../../renderer/cluster-frame-context/hosted-cluster.injectable";
|
||||
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||
@ -21,6 +22,8 @@ import maybeKubeApiInjectable from "../maybe-kube-api.injectable";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KubeApi as ExternalKubeApi } from "../../../extensions/common-api/k8s-api";
|
||||
import { Cluster } from "../../cluster/cluster";
|
||||
import { runInAction } from "mobx";
|
||||
import { customResourceDefinitionApiInjectionToken } from "../api-manager/crd-api-token";
|
||||
|
||||
class TestApi extends KubeApi<KubeObject> {
|
||||
protected async checkPreferredVersion() {
|
||||
@ -117,4 +120,58 @@ describe("ApiManager", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("given than a CRD has a default KubeApi registered for it", () => {
|
||||
const apiBase = "/apis/aquasecurity.github.io/v1alpha1/vulnerabilityreports";
|
||||
|
||||
beforeEach(() => {
|
||||
runInAction(() => {
|
||||
di.register(getInjectable({
|
||||
id: `default-kube-api-for-custom-resource-definition-${apiBase}`,
|
||||
instantiate: (di) => {
|
||||
const objectConstructor = class extends KubeObject {
|
||||
static readonly kind = "VulnerabilityReport";
|
||||
static readonly namespaced = true;
|
||||
static readonly apiBase = apiBase;
|
||||
};
|
||||
|
||||
return Object.assign(
|
||||
new KubeApi({
|
||||
logger: di.inject(loggerInjectable),
|
||||
maybeKubeApi: di.inject(maybeKubeApiInjectable),
|
||||
}, { objectConstructor }),
|
||||
{
|
||||
myField: 1,
|
||||
},
|
||||
);
|
||||
},
|
||||
injectionToken: customResourceDefinitionApiInjectionToken,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
it("can be retrieved from apiManager", () => {
|
||||
expect(apiManager.getApi(apiBase)).toMatchObject({
|
||||
myField: 1,
|
||||
});
|
||||
});
|
||||
|
||||
describe("given that an extension registers an api with the same apibase", () => {
|
||||
beforeEach(() => {
|
||||
void Object.assign(new ExternalKubeApi({
|
||||
objectConstructor: KubeObject,
|
||||
apiBase,
|
||||
kind: "VulnerabilityReport",
|
||||
}), {
|
||||
myField: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it("the extension's instance is retrievable instead from apiManager", () => {
|
||||
expect(apiManager.getApi(apiBase)).toMatchObject({
|
||||
myField: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -26,6 +26,7 @@ export type FindApiCallback = (api: KubeApi<KubeObject>) => boolean;
|
||||
|
||||
interface Dependencies {
|
||||
readonly apis: IComputedValue<KubeApi[]>;
|
||||
readonly crdApis: IComputedValue<KubeApi[]>;
|
||||
readonly stores: IComputedValue<KubeObjectStore[]>;
|
||||
}
|
||||
|
||||
@ -56,6 +57,12 @@ export class ApiManager {
|
||||
}
|
||||
}
|
||||
|
||||
for (const crdApi of this.dependencies.crdApis.get()) {
|
||||
if (!newState.has(crdApi.apiBase)) {
|
||||
newState.set(crdApi.apiBase, crdApi);
|
||||
}
|
||||
}
|
||||
|
||||
this.apis.replace(newState);
|
||||
});
|
||||
}
|
||||
|
||||
@ -5,11 +5,9 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import EventEmitter from "events";
|
||||
import type TypedEventEmitter from "typed-emitter";
|
||||
import type { CustomResourceDefinition } from "../endpoints";
|
||||
import type { KubeApi } from "../kube-api";
|
||||
|
||||
export interface LegacyAutoRegistration {
|
||||
customResourceDefinition: (crd: CustomResourceDefinition) => void;
|
||||
kubeApi: (api: KubeApi<any, any>) => void;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { KubeApi } from "../kube-api";
|
||||
|
||||
export const customResourceDefinitionApiInjectionToken = getInjectionToken<KubeApi>({
|
||||
id: "custom-resource-definition-api-token",
|
||||
});
|
||||
@ -9,6 +9,7 @@ import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-f
|
||||
import { kubeObjectStoreInjectionToken } from "./kube-object-store-token";
|
||||
import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
|
||||
import { computed } from "mobx";
|
||||
import { customResourceDefinitionApiInjectionToken } from "./crd-api-token";
|
||||
|
||||
const apiManagerInjectable = getInjectable({
|
||||
id: "api-manager",
|
||||
@ -23,6 +24,9 @@ const apiManagerInjectable = getInjectable({
|
||||
stores: storesAndApisCanBeCreated
|
||||
? computedInjectMany(kubeObjectStoreInjectionToken)
|
||||
: computed(() => []),
|
||||
crdApis: storesAndApisCanBeCreated
|
||||
? computedInjectMany(customResourceDefinitionApiInjectionToken)
|
||||
: computed(() => []),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
25
packages/core/src/common/utils/registrator-helper.ts
Normal file
25
packages/core/src/common/utils/registrator-helper.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { iter } from "@k8slens/utilities";
|
||||
import type { DiContainerForInjection, Injectable } from "@ogre-tools/injectable";
|
||||
|
||||
// Register new injectables and deregister removed injectables by id
|
||||
|
||||
export const injectableDifferencingRegistratorWith = (di: DiContainerForInjection) => (
|
||||
(rawCurrent: Injectable<any, any, any>[], rawPrevious: Injectable<any, any, any>[] = []) => {
|
||||
const current = new Map(rawCurrent.map(inj => [inj.id, inj]));
|
||||
const previous = new Map(rawPrevious.map(inj => [inj.id, inj]));
|
||||
const toAdd = iter.chain(current.entries())
|
||||
.filter(([id]) => previous.has(id))
|
||||
.collect(entries => new Map(entries));
|
||||
const toRemove = iter.chain(previous.entries())
|
||||
.filter(([id]) => !current.has(id))
|
||||
.collect(entries => new Map(entries));
|
||||
|
||||
di.deregister(...toRemove.values());
|
||||
di.register(...toAdd.values());
|
||||
}
|
||||
);
|
||||
@ -2,29 +2,18 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { Injectable } from "@ogre-tools/injectable";
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { difference, find, map } from "lodash";
|
||||
import { reaction, runInAction } from "mobx";
|
||||
import { disposer } from "@k8slens/utilities";
|
||||
import type { LensExtension } from "../../lens-extension";
|
||||
import { extensionRegistratorInjectionToken } from "../extension-registrator-injection-token";
|
||||
import { injectableDifferencingRegistratorWith } from "../../../common/utils/registrator-helper";
|
||||
|
||||
export interface Extension {
|
||||
register: () => void;
|
||||
deregister: () => void;
|
||||
}
|
||||
|
||||
const idsToInjectables = (ids: string[], injectables: Injectable<any, any, any>[]) => ids.map(id => {
|
||||
const injectable = find(injectables, { id });
|
||||
|
||||
if (!injectable) {
|
||||
throw new Error(`Injectable ${id} not found`);
|
||||
}
|
||||
|
||||
return injectable;
|
||||
});
|
||||
|
||||
const extensionInjectable = getInjectable({
|
||||
id: "extension",
|
||||
|
||||
@ -35,6 +24,7 @@ const extensionInjectable = getInjectable({
|
||||
instantiate: (childDi) => {
|
||||
const extensionRegistrators = childDi.injectMany(extensionRegistratorInjectionToken);
|
||||
const reactionDisposer = disposer();
|
||||
const injectableDifferencingRegistrator = injectableDifferencingRegistratorWith(childDi);
|
||||
|
||||
return {
|
||||
register: () => {
|
||||
@ -46,21 +36,8 @@ const extensionInjectable = getInjectable({
|
||||
// we need to update the registered injectables with a reaction every time they change
|
||||
reaction(
|
||||
() => Array.isArray(injectables) ? injectables : injectables.get(),
|
||||
(currentInjectables, previousInjectables = []) => {
|
||||
// Register new injectables and deregister removed injectables by id
|
||||
const currentIds = map(currentInjectables, "id");
|
||||
const previousIds = map(previousInjectables, "id");
|
||||
const idsToAdd = difference(currentIds, previousIds);
|
||||
const idsToRemove = previousIds.filter(previousId => !currentIds.includes(previousId));
|
||||
|
||||
if (idsToRemove.length > 0) {
|
||||
childDi.deregister(...idsToInjectables(idsToRemove, previousInjectables));
|
||||
}
|
||||
|
||||
if (idsToAdd.length > 0) {
|
||||
childDi.register(...idsToInjectables(idsToAdd, currentInjectables));
|
||||
}
|
||||
}, {
|
||||
injectableDifferencingRegistrator,
|
||||
{
|
||||
fireImmediately: true,
|
||||
},
|
||||
));
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { reaction } from "mobx";
|
||||
import { customResourceDefinitionApiInjectionToken } from "../../../common/k8s-api/api-manager/crd-api-token";
|
||||
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
|
||||
import { KubeApi } from "../../../common/k8s-api/kube-api";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import maybeKubeApiInjectable from "../../../common/k8s-api/maybe-kube-api.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import { injectableDifferencingRegistratorWith } from "../../../common/utils/registrator-helper";
|
||||
import customResourceDefinitionStoreInjectable from "../../components/+custom-resources/definition.store.injectable";
|
||||
import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens";
|
||||
|
||||
const setupAutoCrdApiCreationsInjectable = getInjectable({
|
||||
id: "setup-auto-crd-api-creations",
|
||||
instantiate: (di) => ({
|
||||
run: () => {
|
||||
const customResourceDefinitionStore = di.inject(customResourceDefinitionStoreInjectable);
|
||||
const injectableDifferencingRegistrator = injectableDifferencingRegistratorWith(di);
|
||||
|
||||
reaction(
|
||||
() => customResourceDefinitionStore.getItems().map(toCrdApiInjectable),
|
||||
injectableDifferencingRegistrator,
|
||||
{
|
||||
fireImmediately: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
}),
|
||||
injectionToken: beforeClusterFrameStartsSecondInjectionToken,
|
||||
});
|
||||
|
||||
export default setupAutoCrdApiCreationsInjectable;
|
||||
|
||||
const toCrdApiInjectable = (crd: CustomResourceDefinition) => getInjectable({
|
||||
id: `default-kube-api-for-custom-resource-definition-${crd.getResourceApiBase()}`,
|
||||
instantiate: (di) => {
|
||||
const objectConstructor = class extends KubeObject {
|
||||
static readonly kind = crd.getResourceKind();
|
||||
static readonly namespaced = crd.isNamespaced();
|
||||
static readonly apiBase = crd.getResourceApiBase();
|
||||
};
|
||||
|
||||
return new KubeApi({
|
||||
logger: di.inject(loggerInjectable),
|
||||
maybeKubeApi: di.inject(maybeKubeApiInjectable),
|
||||
}, { objectConstructor });
|
||||
},
|
||||
injectionToken: customResourceDefinitionApiInjectionToken,
|
||||
});
|
||||
@ -63,13 +63,6 @@ const setupAutoRegistrationInjectable = getInjectable({
|
||||
};
|
||||
|
||||
autoRegistrationEmitter
|
||||
.on("customResourceDefinition", (crd) => {
|
||||
if (initialized) {
|
||||
autoInitCustomResourceStore(crd);
|
||||
} else {
|
||||
beforeApiManagerInitializationCrds.push(crd);
|
||||
}
|
||||
})
|
||||
.on("kubeApi", (api) => {
|
||||
if (initialized) {
|
||||
autoInitKubeApi(api);
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import assert from "assert";
|
||||
import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
|
||||
import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token";
|
||||
import customResourceDefinitionApiInjectable from "../../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
@ -20,7 +19,6 @@ const customResourceDefinitionStoreInjectable = getInjectable({
|
||||
const api = di.inject(customResourceDefinitionApiInjectable);
|
||||
|
||||
return new CustomResourceDefinitionStore({
|
||||
autoRegistration: di.inject(autoRegistrationEmitterInjectable),
|
||||
context: di.inject(clusterFrameContextForClusterScopedResourcesInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
}, api);
|
||||
|
||||
@ -3,37 +3,22 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { computed, reaction, makeObservable } from "mobx";
|
||||
import { computed, makeObservable } from "mobx";
|
||||
import type { KubeObjectStoreDependencies, KubeObjectStoreOptions } from "../../../common/k8s-api/kube-object.store";
|
||||
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
|
||||
import type { CustomResourceDefinition, CustomResourceDefinitionApi } from "../../../common/k8s-api/endpoints/custom-resource-definition.api";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type TypedEventEmitter from "typed-emitter";
|
||||
import type { LegacyAutoRegistration } from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
|
||||
import autoBind from "auto-bind";
|
||||
|
||||
export interface CustomResourceDefinitionStoreDependencies extends KubeObjectStoreDependencies {
|
||||
readonly autoRegistration: TypedEventEmitter<LegacyAutoRegistration>;
|
||||
}
|
||||
|
||||
export class CustomResourceDefinitionStore extends KubeObjectStore<CustomResourceDefinition, CustomResourceDefinitionApi> {
|
||||
constructor(
|
||||
protected readonly dependencies: CustomResourceDefinitionStoreDependencies,
|
||||
dependencies: KubeObjectStoreDependencies,
|
||||
api: CustomResourceDefinitionApi,
|
||||
opts?: KubeObjectStoreOptions,
|
||||
) {
|
||||
super(dependencies, api, opts);
|
||||
makeObservable(this);
|
||||
autoBind(this);
|
||||
|
||||
reaction(
|
||||
() => this.getItems(),
|
||||
crds => {
|
||||
for (const crd of crds) {
|
||||
this.dependencies.autoRegistration.emit("customResourceDefinition", crd);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
protected sortItems(items: CustomResourceDefinition[]) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user