1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Make cluster state sync fully injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-12-05 10:59:14 -05:00
parent 659b63d0cf
commit f7b5ae7b57
13 changed files with 237 additions and 65 deletions

View File

@ -4,14 +4,12 @@
*/
import { ipcMain, ipcRenderer, webFrame } from "electron";
import { action, comparer, computed, makeObservable, observable, reaction } from "mobx";
import { action, comparer, computed, makeObservable, observable } from "mobx";
import type { BaseStoreDependencies } from "../base-store/base-store";
import { BaseStore } from "../base-store/base-store";
import { Cluster } from "../cluster/cluster";
import { disposer, toJS } from "../utils";
import type { ClusterModel, ClusterId, ClusterState } from "../cluster-types";
import { requestInitialClusterStates } from "../../renderer/ipc";
import { toJS } from "../utils";
import type { ClusterModel, ClusterId } from "../cluster-types";
import type { CreateCluster } from "../cluster/create-cluster-injection-token";
import type { ReadClusterConfigSync } from "./read-cluster-config.injectable";
import type { EmitAppEvent } from "../app-event-bus/emit-event.injectable";
@ -29,8 +27,6 @@ interface Dependencies extends BaseStoreDependencies {
export class ClusterStore extends BaseStore<ClusterStoreModel> {
readonly clusters = observable.map<ClusterId, Cluster>();
protected readonly disposer = disposer();
constructor(protected readonly dependencies: Dependencies) {
super(dependencies, {
configName: "lens-cluster-store",
@ -41,39 +37,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
});
makeObservable(this);
this.load();
this.pushStateToViewsAutomatically();
}
async loadInitialOnRenderer() {
this.dependencies.logger.info("[CLUSTER-STORE] requesting initial state sync");
for (const { id, state } of await requestInitialClusterStates()) {
this.getById(id)?.setState(state);
}
}
protected pushStateToViewsAutomatically() {
if (ipcMain) {
this.disposer.push(
reaction(() => this.connectedClustersList, () => this.pushState()),
);
}
}
registerIpcListener() {
this.dependencies.logger.info(`[CLUSTER-STORE] start to listen (${webFrame.routingId})`);
const ipc = ipcMain ?? ipcRenderer;
ipc?.on("cluster:state", (event, clusterId: ClusterId, state: ClusterState) => {
this.getById(clusterId)?.setState(state);
});
}
pushState() {
this.clusters.forEach((c) => {
c.pushState();
});
}
@computed get clustersList(): Cluster[] {

View File

@ -316,7 +316,6 @@ export class Cluster implements ClusterModel, ClusterState {
const refreshMetadataTimer = setInterval(() => this.available && this.refreshAccessibilityAndMetadata(), 900000); // every 15 minutes
this.eventsDisposer.push(
reaction(() => this.getState(), state => this.pushState(state)),
reaction(
() => this.prometheusPreferences,
prefs => this.contextHandler.setupPrometheus(prefs),
@ -349,7 +348,7 @@ export class Cluster implements ClusterModel, ClusterState {
@action
async activate(force = false) {
if (this.activated && !force) {
return this.pushState();
return;
}
this.dependencies.logger.info(`[CLUSTER]: activate`, this.getMeta());
@ -395,7 +394,6 @@ export class Cluster implements ClusterModel, ClusterState {
}
this.activated = true;
this.pushState();
}
/**
@ -437,7 +435,6 @@ export class Cluster implements ClusterModel, ClusterState {
this.activated = false;
this.allowedNamespaces = [];
this.resourceAccessStatuses.clear();
this.pushState();
this.dependencies.logger.info(`[CLUSTER]: disconnected`, { id: this.id });
}
@ -448,7 +445,6 @@ export class Cluster implements ClusterModel, ClusterState {
async refresh() {
this.dependencies.logger.info(`[CLUSTER]: refresh`, this.getMeta());
await this.refreshConnectionStatus();
this.pushState();
}
/**
@ -614,16 +610,15 @@ export class Cluster implements ClusterModel, ClusterState {
* @param state cluster state
*/
@action setState(state: ClusterState) {
Object.assign(this, state);
}
/**
* @internal
* @param state cluster state
*/
pushState(state = this.getState()) {
this.dependencies.logger.silly(`[CLUSTER]: push-state`, state);
this.dependencies.broadcastMessage("cluster:state", this.id, state);
this.accessible = state.accessible;
this.allowedNamespaces = state.allowedNamespaces;
this.allowedResources = state.allowedResources;
this.apiUrl = state.apiUrl;
this.disconnected = state.disconnected;
this.isAdmin = state.isAdmin;
this.isGlobalWatchEnabled = state.isGlobalWatchEnabled;
this.online = state.online;
this.ready = state.ready;
}
// get cluster system meta, e.g. use in "logger"

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ClusterId, ClusterState } from "../../../../common/cluster-types";
import type { MessageChannel } from "../../../../common/utils/channel/message-channel-listener-injection-token";
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
export interface ClusterStateSync {
clusterId: ClusterId;
state: ClusterState;
}
export const clusterStateSyncChannel: MessageChannel<ClusterStateSync> = {
id: "cluster-state-sync",
};
export const initialClusterStatesChannel: RequestChannel<void, ClusterStateSync[]> = {
id: "initial-cluster-state-sync",
};

View File

@ -0,0 +1,21 @@
/**
* 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 { MessageChannelHandler } from "../../../../common/utils/channel/message-channel-listener-injection-token";
import { sendMessageToChannelInjectionToken } from "../../../../common/utils/channel/message-to-channel-injection-token";
import { clusterStateSyncChannel } from "../common/channels";
export type EmitClusterStateUpdate = MessageChannelHandler<typeof clusterStateSyncChannel>;
const emitClusterStateUpdateInjectable = getInjectable({
id: "emit-cluster-state-update",
instantiate: (di): EmitClusterStateUpdate => {
const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken);
return (message) => sendMessageToChannel(clusterStateSyncChannel, message);
},
});
export default emitClusterStateUpdateInjectable;

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens";
import { initialClusterStatesChannel } from "../common/channels";
const handleInitialClusterStateSyncInjectable = getRequestChannelListenerInjectable({
channel: initialClusterStatesChannel,
handler: (di) => {
const clusterStore = di.inject(clusterStoreInjectable);
return () => clusterStore.clustersList.map(cluster => ({
clusterId: cluster.id,
state: cluster.getState(),
}));
},
});
export default handleInitialClusterStateSyncInjectable;

View File

@ -0,0 +1,36 @@
/**
* 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 clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import { beforeApplicationIsLoadingInjectionToken } from "../../../../main/start-main-application/runnable-tokens/before-application-is-loading-injection-token";
import initClusterStoreInjectable from "../../store/main/init.injectable";
import emitClusterStateUpdateInjectable from "./emit-update.injectable";
const setupClusterStateBroadcastingInjectable = getInjectable({
id: "setup-cluster-state-broadcasting",
instantiate: (di) => {
const emitClusterStateUpdate = di.inject(emitClusterStateUpdateInjectable);
const clusterStore = di.inject(clusterStoreInjectable);
return {
id: "setup-cluster-state-broadcasting",
run: () => {
reaction(() => clusterStore.connectedClustersList, () => {
for (const cluster of clusterStore.clusters.values()) {
emitClusterStateUpdate({
clusterId: cluster.id,
state: cluster.getState(),
});
}
});
},
runAfter: di.inject(initClusterStoreInjectable),
};
},
injectionToken: beforeApplicationIsLoadingInjectionToken,
});
export default setupClusterStateBroadcastingInjectable;

View File

@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable";
import { getMessageChannelListenerInjectable } from "../../../../common/utils/channel/message-channel-listener-injection-token";
import { clusterStateSyncChannel } from "../common/channels";
const clusterStateListenerInjectable = getMessageChannelListenerInjectable({
channel: clusterStateSyncChannel,
id: "main",
handler: (di) => {
const getClusterById = di.inject(getClusterByIdInjectable);
return ({ clusterId, state }) => getClusterById(clusterId)?.setState(state);
},
});
export default clusterStateListenerInjectable;

View File

@ -0,0 +1,21 @@
/**
* 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 { RequestChannelHandler } from "../../../../main/utils/channel/channel-listeners/listener-tokens";
import requestFromChannelInjectable from "../../../../renderer/utils/channel/request-from-channel.injectable";
import { initialClusterStatesChannel } from "../common/channels";
export type RequestInitialClusterStates = RequestChannelHandler<typeof initialClusterStatesChannel>;
const requestInitialClusterStatesInjectable = getInjectable({
id: "request-initial-cluster-states",
instantiate: (di): RequestInitialClusterStates => {
const requestFromChannel = di.inject(requestFromChannelInjectable);
return () => requestFromChannel(initialClusterStatesChannel);
},
});
export default requestInitialClusterStatesInjectable;

View File

@ -0,0 +1,32 @@
/**
* 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 getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable";
import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
import initClusterStoreInjectable from "../../store/renderer/init.injectable";
import requestInitialClusterStatesInjectable from "./request-initial.injectable";
const setupClusterStateSyncInjectable = getInjectable({
id: "setup-cluster-state-sync",
instantiate: (di) => {
const requestInitialClusterStates = di.inject(requestInitialClusterStatesInjectable);
const getClusterById = di.inject(getClusterByIdInjectable);
return {
id: "setup-cluster-state-sync",
run: async () => {
const initalStates = await requestInitialClusterStates();
for (const { clusterId, state } of initalStates) {
getClusterById(clusterId)?.setState(state);
}
},
runAfter: di.inject(initClusterStoreInjectable),
};
},
injectionToken: beforeFrameStartsInjectionToken,
});
export default setupClusterStateSyncInjectable;

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 { getInjectable } from "@ogre-tools/injectable";
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import { beforeApplicationIsLoadingInjectionToken } from "../../../../main/start-main-application/runnable-tokens/before-application-is-loading-injection-token";
import initUserStoreInjectable from "../../../../main/stores/init-user-store.injectable";
const initClusterStoreInjectable = getInjectable({
id: "init-cluster-store",
instantiate: (di) => {
const clusterStore = di.inject(clusterStoreInjectable);
return {
id: "init-cluster-store",
run: () => {
clusterStore.load();
},
runAfter: di.inject(initUserStoreInjectable),
};
},
injectionToken: beforeApplicationIsLoadingInjectionToken,
});
export default initClusterStoreInjectable;

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 { getInjectable } from "@ogre-tools/injectable";
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import { beforeFrameStartsInjectionToken } from "../../../../renderer/before-frame-starts/tokens";
import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable";
const initClusterStoreInjectable = getInjectable({
id: "init-cluster-store",
instantiate: (di) => {
const clusterStore = di.inject(clusterStoreInjectable);
return {
id: "init-cluster-store",
run: () => {
clusterStore.load();
},
runAfter: di.inject(initUserStoreInjectable),
};
},
injectionToken: beforeFrameStartsInjectionToken,
});
export default initClusterStoreInjectable;

View File

@ -20,7 +20,6 @@ import type { DiContainer } from "@ogre-tools/injectable";
import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable";
import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable";
import extensionInstallationStateStoreInjectable from "../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable";
import initRootFrameInjectable from "./frames/root-frame/init-root-frame/init-root-frame.injectable";
import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable";
import { Router } from "react-router";
@ -47,11 +46,6 @@ export async function bootstrap(di: DiContainer) {
extensionDiscovery.init();
// ClusterStore depends on: UserStore
const clusterStore = di.inject(clusterStoreInjectable);
await clusterStore.loadInitialOnRenderer();
// HotbarStore depends on: ClusterStore
di.inject(hotbarStoreInjectable).load();
@ -63,9 +57,6 @@ export async function bootstrap(di: DiContainer) {
extensionInstallationStateStore.bindIpcListeners();
// Register additional store listeners
clusterStore.registerIpcListener();
let App;
let initializeApp;

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import setupAppPathsInjectable from "../app-paths/setup-app-paths.injectable";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/tokens";
import initDefaultUpdateChannelInjectable from "../vars/default-update-channel/init.injectable";
const initUserStoreInjectable = getInjectable({