1
0
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:
Sebastian Malton 2022-06-16 14:36:25 -04:00
parent 853573afcb
commit 0a7c214767
28 changed files with 361 additions and 96 deletions

View File

@ -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,
});
}
}

View 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;

View File

@ -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) {

View 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;

View 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;

View File

@ -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";

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;

View File

@ -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>,
>(

View 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);
});
});

View 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;

View 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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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;

View 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;

View File

@ -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;

View File

@ -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;
},
});

View File

@ -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),
}),
});

View File

@ -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),
}),
});

View File

@ -143,8 +143,6 @@ export const getApplicationBuilder = () => {
doGeneralOverrides: true,
});
mainDi.register(mainExtensionsStateInjectable);
const overrideChannelsForWindow = overrideChannels(mainDi);
const beforeApplicationStartCallbacks: Callback[] = [];

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
};
};