mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Convert to injectable IPC
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
853573afcb
commit
0a7c214767
@ -10,8 +10,10 @@ import { broadcastMessage } from "../ipc";
|
||||
import { app } from "electron";
|
||||
import type { CatalogEntityConstructor, CatalogEntitySpec } from "../catalog/catalog-entity";
|
||||
import { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
|
||||
import { requestClusterActivation, requestClusterDisconnection } from "../../renderer/ipc";
|
||||
import { requestClusterDisconnection } from "../../renderer/ipc";
|
||||
import KubeClusterCategoryIcon from "./icons/kubernetes.svg";
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import requestClusterActivationInjectable from "../../renderer/cluster/request-activation.injectable";
|
||||
|
||||
export interface KubernetesClusterPrometheusMetrics {
|
||||
address?: {
|
||||
@ -63,6 +65,8 @@ export function isKubernetesCluster(item: unknown): item is KubernetesCluster {
|
||||
return item instanceof KubernetesCluster;
|
||||
}
|
||||
|
||||
const requestClusterActivation = asLegacyGlobalFunctionForExtensionApi(requestClusterActivationInjectable);
|
||||
|
||||
export class KubernetesCluster<
|
||||
Metadata extends KubernetesClusterMetadata = KubernetesClusterMetadata,
|
||||
Status extends KubernetesClusterStatus = KubernetesClusterStatus,
|
||||
@ -78,7 +82,10 @@ export class KubernetesCluster<
|
||||
if (app) {
|
||||
await ClusterStore.getInstance().getById(this.getId())?.activate();
|
||||
} else {
|
||||
await requestClusterActivation(this.getId(), false);
|
||||
await requestClusterActivation({
|
||||
clusterId: this.getId(),
|
||||
force: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
src/common/cluster-store/clusters.injectable.ts
Normal file
18
src/common/cluster-store/clusters.injectable.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
import clusterStoreInjectable from "./cluster-store.injectable";
|
||||
|
||||
const clustersInjectable = getInjectable({
|
||||
id: "clusters",
|
||||
instantiate: (di) => {
|
||||
const store = di.inject(clusterStoreInjectable);
|
||||
|
||||
return computed(() => [...store.clusters.values()]);
|
||||
},
|
||||
});
|
||||
|
||||
export default clustersInjectable;
|
||||
@ -25,6 +25,8 @@ import type { CanI } from "./authorization-review.injectable";
|
||||
import type { ListNamespaces } from "./list-namespaces.injectable";
|
||||
import assert from "assert";
|
||||
import type { Logger } from "../logger";
|
||||
import type { ReadFileSync } from "../fs/read-file-sync.injectable";
|
||||
import type { EmitClusterConnectionUpdate } from "../../main/cluster/emit-connection-update.injectable";
|
||||
|
||||
export interface ClusterDependencies {
|
||||
readonly directoryForKubeConfigs: string;
|
||||
@ -36,6 +38,10 @@ export interface ClusterDependencies {
|
||||
createAuthorizationReview: (config: KubeConfig) => CanI;
|
||||
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
||||
createVersionDetector: (cluster: Cluster) => VersionDetector;
|
||||
emitClusterConnectionUpdate: EmitClusterConnectionUpdate;
|
||||
|
||||
// TODO: creating a Cluster should not have such wild side effects
|
||||
readFileSync: ReadFileSync;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -625,7 +631,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
const update: KubeAuthUpdate = { message, isError };
|
||||
|
||||
this.dependencies.logger.debug(`[CLUSTER]: broadcasting connection update`, { ...update, meta: this.getMeta() });
|
||||
broadcastMessage(`cluster:connection-update`, this.id, update);
|
||||
this.dependencies.emitClusterConnectionUpdate({ clusterId: this.id, update });
|
||||
}
|
||||
|
||||
protected async getAllowedNamespaces(proxyConfig: KubeConfig) {
|
||||
|
||||
26
src/common/cluster/connection-update-channel.injectable.ts
Normal file
26
src/common/cluster/connection-update-channel.injectable.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ClusterId, KubeAuthUpdate } from "../cluster-types";
|
||||
import type { MessageChannel } from "../utils/channel/message-channel-injection-token";
|
||||
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
export interface ClusterConnectionUpdateMessage {
|
||||
clusterId: ClusterId;
|
||||
update: KubeAuthUpdate;
|
||||
}
|
||||
|
||||
export type ClusterConnectionUpdateChannel = MessageChannel<ClusterConnectionUpdateMessage>;
|
||||
|
||||
const clusterConnectionUpdateChannelInjectable = getInjectable({
|
||||
id: "cluster-connection-update-channel",
|
||||
instantiate: (): ClusterConnectionUpdateChannel => ({
|
||||
id: "cluster:connection-update",
|
||||
}),
|
||||
injectionToken: messageChannelInjectionToken,
|
||||
});
|
||||
|
||||
export default clusterConnectionUpdateChannelInjectable;
|
||||
23
src/common/cluster/request-activation.injectable.ts
Normal file
23
src/common/cluster/request-activation.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 type { ClusterId } from "../cluster-types";
|
||||
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
|
||||
|
||||
export interface ClusterActivationRequest {
|
||||
clusterId: ClusterId;
|
||||
force: boolean;
|
||||
}
|
||||
export type ClusterActivationRequestChannel = RequestChannel<ClusterActivationRequest, void>;
|
||||
|
||||
const clusterActivationRequestChannelInjectable = getInjectable({
|
||||
id: "cluster-activation-request-channel",
|
||||
instantiate: (): ClusterActivationRequestChannel => ({
|
||||
id: "cluster-request-activation",
|
||||
}),
|
||||
});
|
||||
|
||||
export default clusterActivationRequestChannelInjectable;
|
||||
@ -3,7 +3,6 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export const clusterActivateHandler = "cluster:activate";
|
||||
export const clusterSetFrameIdHandler = "cluster:set-frame-id";
|
||||
export const clusterVisibilityHandler = "cluster:visibility";
|
||||
export const clusterRefreshHandler = "cluster:refresh";
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
|
||||
export interface Channel<MessageTemplate = void, ReturnTemplate = void> {
|
||||
id: string;
|
||||
_messageTemplate?: MessageTemplate;
|
||||
_returnTemplate?: ReturnTemplate;
|
||||
}
|
||||
|
||||
@ -4,9 +4,8 @@
|
||||
*/
|
||||
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { JsonValue } from "type-fest";
|
||||
|
||||
export interface MessageChannel<Message extends JsonValue | void = void> {
|
||||
export interface MessageChannel<Message = void> {
|
||||
id: string;
|
||||
_messageSignature?: Message;
|
||||
}
|
||||
|
||||
@ -6,6 +6,10 @@ import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { SetRequired } from "type-fest";
|
||||
import type { MessageChannel } from "./message-channel-injection-token";
|
||||
|
||||
export type EmitChannelMessage<Channel> = Channel extends MessageChannel<infer Message>
|
||||
? (message: Message) => void
|
||||
: never;
|
||||
|
||||
export interface MessageToChannel {
|
||||
<TChannel extends MessageChannel<TMessage>, TMessage extends void>(
|
||||
channel: TChannel,
|
||||
|
||||
@ -4,11 +4,10 @@
|
||||
*/
|
||||
|
||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { JsonValue } from "type-fest";
|
||||
|
||||
export interface RequestChannel<
|
||||
Request extends JsonValue | void = void,
|
||||
Response extends JsonValue | void = void,
|
||||
Request = void,
|
||||
Response = void,
|
||||
> {
|
||||
id: string;
|
||||
_requestSignature?: Request;
|
||||
|
||||
@ -6,6 +6,12 @@ import { getInjectionToken } from "@ogre-tools/injectable";
|
||||
import type { SetRequired } from "type-fest";
|
||||
import type { RequestChannel } from "./request-channel-injection-token";
|
||||
|
||||
export type RequestFromChannelImpl<Channel> = Channel extends RequestChannel<infer Request, infer Response>
|
||||
? Request extends void
|
||||
? () => Promise<Response>
|
||||
: (req: Request) => Promise<Response>
|
||||
: never;
|
||||
|
||||
export type RequestFromChannel = <
|
||||
TChannel extends RequestChannel<any, any>,
|
||||
>(
|
||||
|
||||
84
src/features/cluster/connection-status.test.tsx
Normal file
84
src/features/cluster/connection-status.test.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ClusterStore } from "../../common/cluster-store/cluster-store";
|
||||
import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable";
|
||||
import type { ClusterId } from "../../common/cluster-types";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import navigateToClusterViewInjectable from "../../common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable";
|
||||
import type { ReadFileSync } from "../../common/fs/read-file-sync.injectable";
|
||||
import readFileSyncInjectable from "../../common/fs/read-file-sync.injectable";
|
||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||
import createClusterInjectable from "../../renderer/create-cluster/create-cluster.injectable";
|
||||
|
||||
describe("cluster connection status", () => {
|
||||
let clusterStore: ClusterStore;
|
||||
let clusters: Map<ClusterId, Cluster>;
|
||||
let cluster: Cluster;
|
||||
let applicationBuilder: ApplicationBuilder;
|
||||
|
||||
beforeEach(async () => {
|
||||
applicationBuilder = getApplicationBuilder();
|
||||
|
||||
const readFileSyncMock: ReadFileSync = (filePath) => {
|
||||
expect(filePath).toBe("/some/file/path");
|
||||
|
||||
return JSON.stringify({
|
||||
apiVersion: "v1",
|
||||
clusters: [{
|
||||
name: "minikube",
|
||||
cluster: {
|
||||
server: "https://192.168.64.3:8443",
|
||||
},
|
||||
}],
|
||||
contexts: [{
|
||||
context: {
|
||||
cluster: "minikube",
|
||||
user: "minikube",
|
||||
},
|
||||
name: "minikube",
|
||||
}],
|
||||
users: [{
|
||||
name: "minikube",
|
||||
}],
|
||||
kind: "Config",
|
||||
preferences: {},
|
||||
});
|
||||
};
|
||||
|
||||
applicationBuilder.dis.rendererDi.override(readFileSyncInjectable, () => readFileSyncMock);
|
||||
|
||||
const navigateToClusterView = applicationBuilder.dis.rendererDi.inject(navigateToClusterViewInjectable);
|
||||
const createCluster = applicationBuilder.dis.rendererDi.inject(createClusterInjectable);
|
||||
|
||||
cluster = createCluster({
|
||||
contextName: "minikube",
|
||||
id: "some-cluster-id",
|
||||
kubeConfigPath: "/some/file/path",
|
||||
}, {
|
||||
clusterServerUrl: "https://localhost:1234",
|
||||
});
|
||||
|
||||
clusters = new Map();
|
||||
|
||||
clusters.set(cluster.id, cluster);
|
||||
|
||||
clusterStore = ({
|
||||
clusters,
|
||||
get clustersList() {
|
||||
return [...clusters.values()];
|
||||
},
|
||||
getById: (id) => clusters.get(id),
|
||||
}) as ClusterStore;
|
||||
|
||||
applicationBuilder.dis.mainDi.override(clusterStoreInjectable, () => clusterStore);
|
||||
applicationBuilder.dis.rendererDi.override(clusterStoreInjectable, () => clusterStore);
|
||||
|
||||
await applicationBuilder.render();
|
||||
|
||||
navigateToClusterView(cluster.id);
|
||||
});
|
||||
});
|
||||
23
src/main/cluster/emit-connection-update.injectable.ts
Normal file
23
src/main/cluster/emit-connection-update.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 type { ClusterConnectionUpdateChannel } from "../../common/cluster/connection-update-channel.injectable";
|
||||
import clusterConnectionUpdateChannelInjectable from "../../common/cluster/connection-update-channel.injectable";
|
||||
import type { EmitChannelMessage } from "../../common/utils/channel/message-to-channel-injection-token";
|
||||
import messageToChannelInjectable from "../utils/channel/message-to-channel.injectable";
|
||||
|
||||
export type EmitClusterConnectionUpdate = EmitChannelMessage<ClusterConnectionUpdateChannel>;
|
||||
|
||||
const emitClusterConnectionUpdateInjectable = getInjectable({
|
||||
id: "emit-cluster-connection-update",
|
||||
instantiate: (di): EmitClusterConnectionUpdate => {
|
||||
const channel = di.inject(clusterConnectionUpdateChannelInjectable);
|
||||
const messageToChannel = di.inject(messageToChannelInjectable);
|
||||
|
||||
return (message) => messageToChannel(channel, message);
|
||||
},
|
||||
});
|
||||
|
||||
export default emitClusterConnectionUpdateInjectable;
|
||||
28
src/main/cluster/request-activation-handler.injectable.ts
Normal file
28
src/main/cluster/request-activation-handler.injectable.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable";
|
||||
import type { ClusterActivationRequestChannel } from "../../common/cluster/request-activation.injectable";
|
||||
import clusterActivationRequestChannelInjectable from "../../common/cluster/request-activation.injectable";
|
||||
import type { RequestChannelListener } from "../../common/utils/channel/request-channel-listener-injection-token";
|
||||
import { requestChannelListenerInjectionToken } from "../../common/utils/channel/request-channel-listener-injection-token";
|
||||
|
||||
const requestClusterActivationHandlerInjectable = getInjectable({
|
||||
id: "request-cluster-activation-handler",
|
||||
instantiate: (di) => {
|
||||
const channel = di.inject(clusterActivationRequestChannelInjectable);
|
||||
const store = di.inject(clusterStoreInjectable);
|
||||
|
||||
return {
|
||||
channel,
|
||||
handler: ({ clusterId, force }) => {
|
||||
store.getById(clusterId)?.activate(force);
|
||||
},
|
||||
} as RequestChannelListener<ClusterActivationRequestChannel>;
|
||||
},
|
||||
injectionToken: requestChannelListenerInjectionToken,
|
||||
});
|
||||
|
||||
export default requestClusterActivationHandlerInjectable;
|
||||
@ -15,6 +15,8 @@ import listNamespacesInjectable from "../../common/cluster/list-namespaces.injec
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import detectorRegistryInjectable from "../cluster-detectors/detector-registry.injectable";
|
||||
import createVersionDetectorInjectable from "../cluster-detectors/create-version-detector.injectable";
|
||||
import readFileSyncInjectable from "../../common/fs/read-file-sync.injectable";
|
||||
import emitClusterConnectionUpdateInjectable from "../cluster/emit-connection-update.injectable";
|
||||
|
||||
const createClusterInjectable = getInjectable({
|
||||
id: "create-cluster",
|
||||
@ -30,6 +32,8 @@ const createClusterInjectable = getInjectable({
|
||||
logger: di.inject(loggerInjectable),
|
||||
detectorRegistry: di.inject(detectorRegistryInjectable),
|
||||
createVersionDetector: di.inject(createVersionDetectorInjectable),
|
||||
readFileSync: di.inject(readFileSyncInjectable),
|
||||
emitClusterConnectionUpdate: di.inject(emitClusterConnectionUpdateInjectable),
|
||||
};
|
||||
|
||||
return (model, configData) => new Cluster(dependencies, model, configData);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import type { IpcMainInvokeEvent } from "electron";
|
||||
import { BrowserWindow, Menu } from "electron";
|
||||
import { clusterFrameMap } from "../../../../common/cluster-frames";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler, clusterSetDeletingHandler, clusterClearDeletingHandler } from "../../../../common/ipc/cluster";
|
||||
import { clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterDeleteHandler, clusterSetDeletingHandler, clusterClearDeletingHandler } from "../../../../common/ipc/cluster";
|
||||
import type { ClusterId } from "../../../../common/cluster-types";
|
||||
import { ClusterStore } from "../../../../common/cluster-store/cluster-store";
|
||||
import { appEventBus } from "../../../../common/app-event-bus/event-bus";
|
||||
@ -37,12 +37,6 @@ interface Dependencies {
|
||||
}
|
||||
|
||||
export const setupIpcMainHandlers = ({ applicationMenuItems, directoryForLensLocalStorage, getAbsolutePath, clusterManager, catalogEntityRegistry, clusterStore, operatingSystemTheme, askUserForFilePaths }: Dependencies) => {
|
||||
ipcMainHandle(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||
return ClusterStore.getInstance()
|
||||
.getById(clusterId)
|
||||
?.activate(force);
|
||||
});
|
||||
|
||||
ipcMainHandle(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => {
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
|
||||
@ -95,6 +95,7 @@ import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx";
|
||||
import electronInjectable from "./utils/resolve-system-proxy/electron.injectable";
|
||||
import type { HotbarStore } from "../common/hotbars/store";
|
||||
import focusApplicationInjectable from "./electron-app/features/focus-application.injectable";
|
||||
import readFileSyncInjectable from "../common/fs/read-file-sync.injectable";
|
||||
import type { GlobalOverride } from "../common/test-utils/get-global-override";
|
||||
|
||||
export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) {
|
||||
@ -177,6 +178,7 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {})
|
||||
readJsonFileInjectable,
|
||||
readFileInjectable,
|
||||
execFileInjectable,
|
||||
readFileSyncInjectable,
|
||||
]);
|
||||
|
||||
// TODO: Remove usages of globally exported appEventBus to get rid of this
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { MessageToChannel } from "../../../common/utils/channel/message-to-channel-injection-token";
|
||||
import { messageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
|
||||
import type { MessageChannel } from "../../../common/utils/channel/message-channel-injection-token";
|
||||
import { tentativeStringifyJson } from "../../../common/utils/tentative-stringify-json";
|
||||
import getVisibleWindowsInjectable from "../../start-main-application/lens-window/get-visible-windows.injectable";
|
||||
|
||||
@ -14,15 +14,13 @@ const messageToChannelInjectable = getInjectable({
|
||||
instantiate: (di) => {
|
||||
const getVisibleWindows = di.inject(getVisibleWindowsInjectable);
|
||||
|
||||
// TODO: Figure out way to improve typing in internals
|
||||
// Notice that this should be injected using "messageToChannelInjectionToken" which is typed correctly.
|
||||
return (channel: MessageChannel<any>, message?: unknown) => {
|
||||
return ((channel, message) => {
|
||||
const stringifiedMessage = tentativeStringifyJson(message);
|
||||
|
||||
getVisibleWindows().forEach((lensWindow) =>
|
||||
lensWindow.send({ channel: channel.id, data: stringifiedMessage ? [stringifiedMessage] : [] }),
|
||||
);
|
||||
};
|
||||
}) as MessageToChannel;
|
||||
},
|
||||
|
||||
injectionToken: messageToChannelInjectionToken,
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 clusterConnectionStatusStateInjectable from "../components/cluster-manager/cluster-status.state.injectable";
|
||||
import type { MessageChannelListener } from "../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import { messageChannelListenerInjectionToken } from "../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import type { ClusterConnectionUpdateChannel } from "../../common/cluster/connection-update-channel.injectable";
|
||||
import clusterConnectionUpdateChannelInjectable from "../../common/cluster/connection-update-channel.injectable";
|
||||
|
||||
const clusterConnectionChannelListenerInjectable = getInjectable({
|
||||
id: "cluster-connection-channel-listener",
|
||||
instantiate: (di): MessageChannelListener<ClusterConnectionUpdateChannel> => {
|
||||
const clusterConnectionUpdateChannel = di.inject(clusterConnectionUpdateChannelInjectable);
|
||||
const state = di.inject(clusterConnectionStatusStateInjectable);
|
||||
|
||||
return {
|
||||
channel: clusterConnectionUpdateChannel,
|
||||
handler: ({ clusterId, update }) => {
|
||||
const status = state.forCluster(clusterId);
|
||||
|
||||
status.appendAuthUpdate(update);
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: messageChannelListenerInjectionToken,
|
||||
});
|
||||
|
||||
export default clusterConnectionChannelListenerInjectable;
|
||||
23
src/renderer/cluster/request-activation.injectable.ts
Normal file
23
src/renderer/cluster/request-activation.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 type { ClusterActivationRequestChannel } from "../../common/cluster/request-activation.injectable";
|
||||
import clusterActivationRequestChannelInjectable from "../../common/cluster/request-activation.injectable";
|
||||
import type { RequestFromChannelImpl } from "../../common/utils/channel/request-from-channel-injection-token";
|
||||
import requestFromChannelInjectable from "../utils/channel/request-from-channel.injectable";
|
||||
|
||||
export type RequestClusterActivation = RequestFromChannelImpl<ClusterActivationRequestChannel>;
|
||||
|
||||
const requestClusterActivationInjectable = getInjectable({
|
||||
id: "request-cluster-activation",
|
||||
instantiate: (di): RequestClusterActivation => {
|
||||
const channel = di.inject(clusterActivationRequestChannelInjectable);
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectable);
|
||||
|
||||
return (clusterId) => requestFromChannel(channel, clusterId);
|
||||
},
|
||||
});
|
||||
|
||||
export default requestClusterActivationInjectable;
|
||||
@ -3,55 +3,53 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { action, reaction } from "mobx";
|
||||
import type { ClusterStore } from "../../../common/cluster-store/cluster-store";
|
||||
import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable";
|
||||
import type { ClusterId, KubeAuthUpdate } from "../../../common/cluster-types";
|
||||
import ipcRendererInjectable from "../../app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||
import type { ClusterConnectionStatusState } from "./cluster-status.state.injectable";
|
||||
import { action, computed, reaction } from "mobx";
|
||||
import clustersInjectable from "../../../common/cluster-store/clusters.injectable";
|
||||
import type { ClusterId } from "../../../common/cluster-types";
|
||||
import setupAppPathsInjectable from "../../app-paths/setup-app-paths.injectable";
|
||||
import { beforeFrameStartsInjectionToken } from "../../before-frame-starts/before-frame-starts-injection-token";
|
||||
import clusterConnectionStatusStateInjectable from "./cluster-status.state.injectable";
|
||||
|
||||
function computeRisingEdgeForClusterDisconnect(store: ClusterStore, onNewlyDisconnected: (clusterId: ClusterId) => void) {
|
||||
const disconnectedStateComputer = () => store.clustersList.map(cluster => [cluster.id, cluster.disconnected] as const);
|
||||
const state = new Map(disconnectedStateComputer());
|
||||
|
||||
reaction(
|
||||
disconnectedStateComputer,
|
||||
(disconnectedStates) => {
|
||||
for (const [clusterId, isDisconnected] of disconnectedStates) {
|
||||
if (state.get(clusterId) === isDisconnected) {
|
||||
// do nothing
|
||||
} else {
|
||||
state.set(clusterId, isDisconnected); // save the new state
|
||||
|
||||
if (isDisconnected) {
|
||||
// If the new value is `true` then the previous value was falsy and this is the rising edge.
|
||||
onNewlyDisconnected(clusterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// This needs to be an `init` function to bypass a bug in the setup -> injectable -> setup path
|
||||
const initClusterStatusWatcherInjectable = getInjectable({
|
||||
const startClusterStatusClearingInjectable = getInjectable({
|
||||
id: "cluster-status-watcher",
|
||||
instantiate: (di) => {
|
||||
const ipcRenderer = di.inject(ipcRendererInjectable);
|
||||
const clusterStore = di.inject(clusterStoreInjectable);
|
||||
const statusState = di.inject(clusterConnectionStatusStateInjectable);
|
||||
const state = new Map<string, boolean>();
|
||||
const onNewlyDisconnected = action((clusterId: ClusterId) => {
|
||||
const status = statusState.forCluster(clusterId);
|
||||
|
||||
return (state: ClusterConnectionStatusState) => {
|
||||
ipcRenderer.on("cluster:connection-update", (evt, clusterId: ClusterId, update: KubeAuthUpdate) => {
|
||||
state.forCluster(clusterId).appendAuthUpdate(update);
|
||||
});
|
||||
computeRisingEdgeForClusterDisconnect(clusterStore, action((clusterId) => {
|
||||
const forCluster = state.forCluster(clusterId);
|
||||
status.clearReconnectingState();
|
||||
status.resetAuthOutput();
|
||||
});
|
||||
|
||||
forCluster.clearReconnectingState();
|
||||
forCluster.resetAuthOutput();
|
||||
}));
|
||||
return {
|
||||
run: () => {
|
||||
const clusters = di.inject(clustersInjectable); // This has to be in here so that it happens after the `setupAppPaths`
|
||||
const disconnectedStates = computed(() => clusters.get().map(cluster => [cluster.id, cluster.disconnected] as const));
|
||||
|
||||
reaction(
|
||||
() => disconnectedStates.get(),
|
||||
states => {
|
||||
for (const [clusterId, isDisconnected] of states) {
|
||||
if (state.get(clusterId) !== isDisconnected) {
|
||||
state.set(clusterId, isDisconnected); // save the new state
|
||||
|
||||
if (isDisconnected) {
|
||||
// If the new value is `true` then the previous value was falsy and this is the rising edge.
|
||||
onNewlyDisconnected(clusterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
fireImmediately: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
runAfter: di.inject(setupAppPathsInjectable),
|
||||
};
|
||||
},
|
||||
injectionToken: beforeFrameStartsInjectionToken,
|
||||
});
|
||||
|
||||
export default initClusterStatusWatcherInjectable;
|
||||
export default startClusterStatusClearingInjectable;
|
||||
|
||||
@ -8,7 +8,6 @@ import { action, computed, observable } from "mobx";
|
||||
import type { ClusterId, KubeAuthUpdate } from "../../../common/cluster-types";
|
||||
import loggerInjectable from "../../../common/logger.injectable";
|
||||
import { getOrInsert, hasTypedProperty, isBoolean, isObject, isString } from "../../utils";
|
||||
import initClusterStatusWatcherInjectable from "./cluster-status-watcher.injectable";
|
||||
|
||||
export interface ClusterConnectionStatus {
|
||||
readonly authOutput: IComputedValue<KubeAuthUpdate[]>;
|
||||
@ -21,18 +20,17 @@ export interface ClusterConnectionStatus {
|
||||
}
|
||||
|
||||
export interface ClusterConnectionStatusState {
|
||||
forCluster(clusterId: ClusterId): Readonly<ClusterConnectionStatus>;
|
||||
forCluster(clusterId: ClusterId): ClusterConnectionStatus;
|
||||
}
|
||||
|
||||
const clusterConnectionStatusStateInjectable = getInjectable({
|
||||
id: "cluster-connection-status-state",
|
||||
instantiate: (di) => {
|
||||
instantiate: (di): ClusterConnectionStatusState => {
|
||||
const authOutputs = observable.map<ClusterId, KubeAuthUpdate[]>();
|
||||
const reconnecting = observable.set<ClusterId>();
|
||||
const initWatcher = di.inject(initClusterStatusWatcherInjectable);
|
||||
const logger = di.inject(loggerInjectable);
|
||||
|
||||
const state: ClusterConnectionStatusState = {
|
||||
return {
|
||||
forCluster: (clusterId) => {
|
||||
const authOutput = computed(() => authOutputs.get(clusterId) ?? []);
|
||||
|
||||
@ -63,10 +61,6 @@ const clusterConnectionStatusStateInjectable = getInjectable({
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
initWatcher(state);
|
||||
|
||||
return state;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -15,13 +15,14 @@ import { Button } from "../button";
|
||||
import { Icon } from "../icon";
|
||||
import { Spinner } from "../spinner";
|
||||
import type { CatalogEntityRegistry } from "../../api/catalog/entity/registry";
|
||||
import { requestClusterActivation } from "../../ipc";
|
||||
import type { NavigateToEntitySettings } from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import navigateToEntitySettingsInjectable from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable";
|
||||
import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable";
|
||||
import type { ClusterConnectionStatus } from "./cluster-status.state.injectable";
|
||||
import clusterConnectionStatusStateInjectable from "./cluster-status.state.injectable";
|
||||
import type { RequestClusterActivation } from "../../cluster/request-activation.injectable";
|
||||
import requestClusterActivationInjectable from "../../cluster/request-activation.injectable";
|
||||
|
||||
export interface ClusterStatusProps {
|
||||
className?: IClassName;
|
||||
@ -32,6 +33,7 @@ interface Dependencies {
|
||||
navigateToEntitySettings: NavigateToEntitySettings;
|
||||
entityRegistry: CatalogEntityRegistry;
|
||||
state: ClusterConnectionStatus;
|
||||
requestClusterActivation: RequestClusterActivation;
|
||||
}
|
||||
|
||||
const NonInjectedClusterStatus = observer((props: ClusterStatusProps & Dependencies) => {
|
||||
@ -41,6 +43,7 @@ const NonInjectedClusterStatus = observer((props: ClusterStatusProps & Dependenc
|
||||
state,
|
||||
className,
|
||||
entityRegistry,
|
||||
requestClusterActivation,
|
||||
} = props;
|
||||
const entity = entityRegistry.getById(cluster.id);
|
||||
const clusterName = entity?.getName() ?? cluster.name;
|
||||
@ -52,7 +55,10 @@ const NonInjectedClusterStatus = observer((props: ClusterStatusProps & Dependenc
|
||||
});
|
||||
|
||||
try {
|
||||
await requestClusterActivation(cluster.id, true);
|
||||
await requestClusterActivation({
|
||||
clusterId: cluster.id,
|
||||
force: true,
|
||||
});
|
||||
} catch (error) {
|
||||
state.appendAuthUpdate({
|
||||
message: String(error),
|
||||
@ -141,5 +147,6 @@ export const ClusterStatus = withInjectables<Dependencies, ClusterStatusProps>(N
|
||||
navigateToEntitySettings: di.inject(navigateToEntitySettingsInjectable),
|
||||
entityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
state: di.inject(clusterConnectionStatusStateInjectable).forCluster(props.cluster.id),
|
||||
requestClusterActivation: di.inject(requestClusterActivationInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -12,7 +12,6 @@ import { ClusterStatus } from "./cluster-status";
|
||||
import type { ClusterFrameHandler } from "./cluster-frame-handler";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import type { ClusterStore } from "../../../common/cluster-store/cluster-store";
|
||||
import { requestClusterActivation } from "../../ipc";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
|
||||
@ -25,6 +24,8 @@ import clusterConnectionStatusStateInjectable from "./cluster-status.state.injec
|
||||
import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable";
|
||||
import type { Disposer } from "../../utils";
|
||||
import { disposer } from "../../utils";
|
||||
import type { RequestClusterActivation } from "../../cluster/request-activation.injectable";
|
||||
import requestClusterActivationInjectable from "../../cluster/request-activation.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
clusterId: IComputedValue<string | undefined>;
|
||||
@ -33,6 +34,7 @@ interface Dependencies {
|
||||
entityRegistry: CatalogEntityRegistry;
|
||||
clusterConnectionStatusState: ClusterConnectionStatusState;
|
||||
clusterStore: ClusterStore;
|
||||
requestClusterActivation: RequestClusterActivation;
|
||||
}
|
||||
|
||||
@observer
|
||||
@ -104,7 +106,10 @@ class NonInjectedClusterView extends React.Component<Dependencies> {
|
||||
|
||||
this.props.clusterFrames.setVisibleCluster(clusterId);
|
||||
this.props.clusterFrames.initView(clusterId);
|
||||
requestClusterActivation(clusterId, false); // activate and fetch cluster's state from main
|
||||
this.props.requestClusterActivation({
|
||||
clusterId,
|
||||
force: false,
|
||||
});
|
||||
this.props.entityRegistry.activeEntity = clusterId;
|
||||
|
||||
const navigateToClusterDisposer = disposer(
|
||||
@ -160,6 +165,7 @@ export const ClusterView = withInjectables<Dependencies>(NonInjectedClusterView,
|
||||
entityRegistry: di.inject(catalogEntityRegistryInjectable),
|
||||
clusterConnectionStatusState: di.inject(clusterConnectionStatusStateInjectable),
|
||||
clusterStore: di.inject(clusterStoreInjectable),
|
||||
requestClusterActivation: di.inject(requestClusterActivationInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -143,8 +143,6 @@ export const getApplicationBuilder = () => {
|
||||
doGeneralOverrides: true,
|
||||
});
|
||||
|
||||
mainDi.register(mainExtensionsStateInjectable);
|
||||
|
||||
const overrideChannelsForWindow = overrideChannels(mainDi);
|
||||
|
||||
const beforeApplicationStartCallbacks: Callback[] = [];
|
||||
|
||||
@ -8,6 +8,7 @@ import { Cluster } from "../../common/cluster/cluster";
|
||||
import directoryForKubeConfigsInjectable from "../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import readFileSyncInjectable from "../../common/fs/read-file-sync.injectable";
|
||||
|
||||
const createClusterInjectable = getInjectable({
|
||||
id: "create-cluster",
|
||||
@ -16,6 +17,7 @@ const createClusterInjectable = getInjectable({
|
||||
const dependencies: ClusterDependencies = {
|
||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
readFileSync: di.inject(readFileSyncInjectable),
|
||||
|
||||
// TODO: Dismantle wrong abstraction
|
||||
// Note: "as never" to get around strictness in unnatural scenario
|
||||
@ -26,6 +28,7 @@ const createClusterInjectable = getInjectable({
|
||||
createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
detectorRegistry: undefined as never,
|
||||
createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
emitClusterConnectionUpdate: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||
};
|
||||
|
||||
return (model, configData) => new Cluster(dependencies, model, configData);
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
import type { OpenDialogOptions } from "electron";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { clusterActivateHandler, clusterClearDeletingHandler, clusterDeleteHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterSetDeletingHandler, clusterSetFrameIdHandler, clusterStates } from "../../common/ipc/cluster";
|
||||
import { clusterClearDeletingHandler, clusterDeleteHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler, clusterSetDeletingHandler, clusterSetFrameIdHandler, clusterStates } from "../../common/ipc/cluster";
|
||||
import type { ClusterId, ClusterState } from "../../common/cluster-types";
|
||||
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel, type WindowAction } from "../../common/ipc/window";
|
||||
import { openFilePickingDialogChannel } from "../../common/ipc/dialog";
|
||||
@ -43,10 +43,6 @@ export function requestSetClusterFrameId(clusterId: ClusterId): Promise<void> {
|
||||
return requestMain(clusterSetFrameIdHandler, clusterId);
|
||||
}
|
||||
|
||||
export function requestClusterActivation(clusterId: ClusterId, force?: boolean): Promise<void> {
|
||||
return requestMain(clusterActivateHandler, clusterId, force);
|
||||
}
|
||||
|
||||
export function requestClusterDisconnection(clusterId: ClusterId, force?: boolean): Promise<void> {
|
||||
return requestMain(clusterDisconnectHandler, clusterId, force);
|
||||
}
|
||||
|
||||
@ -24,9 +24,11 @@ const enlistMessageChannelListenerInjectable = getInjectable({
|
||||
);
|
||||
};
|
||||
|
||||
console.debug(`[IPC]: listening on ${channel.id}`);
|
||||
ipcRenderer.on(channel.id, nativeCallback);
|
||||
|
||||
return () => {
|
||||
console.debug(`[IPC]: listenering off ${channel.id}`);
|
||||
ipcRenderer.off(channel.id, nativeCallback);
|
||||
};
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user