mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Catalog & Hotbar - initial groundwork (#2418)
Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
1 parent
e18a041b13
commit
99a464c61d
109 files changed
+1604
-2612
No files matched your search
@@ -31,7 +31,6 @@ jest.mock("request-promise-native");
|
||||
|
||||
import { Console } from "console";
|
||||
import mockFs from "mock-fs";
|
||||
import { workspaceStore } from "../../common/workspace-store";
|
||||
import { Cluster } from "../cluster";
|
||||
import { ContextHandler } from "../context-handler";
|
||||
import { getFreePort } from "../port";
|
||||
@@ -81,8 +80,7 @@ describe("create clusters", () => {
|
||||
c = new Cluster({
|
||||
id: "foo",
|
||||
contextName: "minikube",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
workspace: workspaceStore.currentWorkspaceId
|
||||
kubeConfigPath: "minikube-config.yml"
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,8 +160,7 @@ describe("create clusters", () => {
|
||||
}({
|
||||
id: "foo",
|
||||
contextName: "minikube",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
workspace: workspaceStore.currentWorkspaceId
|
||||
kubeConfigPath: "minikube-config.yml"
|
||||
});
|
||||
|
||||
await c.init(port);
|
||||
|
||||
@@ -26,7 +26,6 @@ jest.mock("winston", () => ({
|
||||
import { KubeconfigManager } from "../kubeconfig-manager";
|
||||
import mockFs from "mock-fs";
|
||||
import { Cluster } from "../cluster";
|
||||
import { workspaceStore } from "../../common/workspace-store";
|
||||
import { ContextHandler } from "../context-handler";
|
||||
import { getFreePort } from "../port";
|
||||
import fse from "fs-extra";
|
||||
@@ -77,8 +76,7 @@ describe("kubeconfig manager tests", () => {
|
||||
const cluster = new Cluster({
|
||||
id: "foo",
|
||||
contextName: "minikube",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
workspace: workspaceStore.currentWorkspaceId
|
||||
kubeConfigPath: "minikube-config.yml"
|
||||
});
|
||||
const contextHandler = new ContextHandler(cluster);
|
||||
const port = await getFreePort();
|
||||
@@ -98,8 +96,7 @@ describe("kubeconfig manager tests", () => {
|
||||
const cluster = new Cluster({
|
||||
id: "foo",
|
||||
contextName: "minikube",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
workspace: workspaceStore.currentWorkspaceId
|
||||
kubeConfigPath: "minikube-config.yml"
|
||||
});
|
||||
const contextHandler = new ContextHandler(cluster);
|
||||
const port = await getFreePort();
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { autorun, toJS } from "mobx";
|
||||
import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "../common/ipc";
|
||||
import { CatalogEntityRegistry} from "../common/catalog-entity-registry";
|
||||
import "../common/catalog-entities/kubernetes-cluster";
|
||||
|
||||
export class CatalogPusher {
|
||||
static init(catalog: CatalogEntityRegistry) {
|
||||
new CatalogPusher(catalog).init();
|
||||
}
|
||||
|
||||
private constructor(private catalog: CatalogEntityRegistry) {}
|
||||
|
||||
init() {
|
||||
const disposers: { (): void; }[] = [];
|
||||
|
||||
disposers.push(autorun(() => {
|
||||
this.broadcast();
|
||||
}));
|
||||
|
||||
const listener = subscribeToBroadcast("catalog:broadcast", () => {
|
||||
this.broadcast();
|
||||
});
|
||||
|
||||
disposers.push(() => unsubscribeFromBroadcast("catalog:broadcast", listener));
|
||||
|
||||
return disposers;
|
||||
}
|
||||
|
||||
broadcast() {
|
||||
broadcastMessage("catalog:items", toJS(this.catalog.items, { recurseEverything: true }));
|
||||
}
|
||||
}
|
||||
+104
-1
@@ -1,16 +1,25 @@
|
||||
import "../common/cluster-ipc";
|
||||
import type http from "http";
|
||||
import { ipcMain } from "electron";
|
||||
import { autorun, reaction } from "mobx";
|
||||
import { action, autorun, observable, reaction, toJS } from "mobx";
|
||||
import { clusterStore, getClusterIdFromHost } from "../common/cluster-store";
|
||||
import { Cluster } from "./cluster";
|
||||
import logger from "./logger";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import { Singleton } from "../common/utils";
|
||||
import { CatalogEntity } from "../common/catalog-entity";
|
||||
import { KubernetesCluster } from "../common/catalog-entities/kubernetes-cluster";
|
||||
import { catalogEntityRegistry } from "../common/catalog-entity-registry";
|
||||
|
||||
const clusterOwnerRef = "ClusterManager";
|
||||
|
||||
export class ClusterManager extends Singleton {
|
||||
@observable.deep catalogSource: CatalogEntity[] = [];
|
||||
|
||||
constructor(public readonly port: number) {
|
||||
super();
|
||||
|
||||
catalogEntityRegistry.addSource("lens:kubernetes-clusters", this.catalogSource);
|
||||
// auto-init clusters
|
||||
reaction(() => clusterStore.enabledClustersList, (clusters) => {
|
||||
clusters.forEach((cluster) => {
|
||||
@@ -19,8 +28,18 @@ export class ClusterManager extends Singleton {
|
||||
cluster.init(port);
|
||||
}
|
||||
});
|
||||
|
||||
}, { fireImmediately: true });
|
||||
|
||||
reaction(() => toJS(clusterStore.enabledClustersList, { recurseEverything: true }), () => {
|
||||
this.updateCatalogSource(clusterStore.enabledClustersList);
|
||||
}, { fireImmediately: true });
|
||||
|
||||
reaction(() => catalogEntityRegistry.getItemsForApiKind<KubernetesCluster>("entity.k8slens.dev/v1alpha1", "KubernetesCluster"), (entities) => {
|
||||
this.syncClustersFromCatalog(entities);
|
||||
});
|
||||
|
||||
|
||||
// auto-stop removed clusters
|
||||
autorun(() => {
|
||||
const removedClusters = Array.from(clusterStore.removedClusters.values());
|
||||
@@ -40,6 +59,90 @@ export class ClusterManager extends Singleton {
|
||||
ipcMain.on("network:online", () => { this.onNetworkOnline(); });
|
||||
}
|
||||
|
||||
@action protected updateCatalogSource(clusters: Cluster[]) {
|
||||
this.catalogSource.forEach((entity, index) => {
|
||||
const clusterIndex = clusters.findIndex((cluster) => entity.metadata.uid === cluster.id);
|
||||
|
||||
if (clusterIndex === -1) {
|
||||
this.catalogSource.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
clusters.filter((c) => !c.ownerRef).forEach((cluster) => {
|
||||
const entityIndex = this.catalogSource.findIndex((entity) => entity.metadata.uid === cluster.id);
|
||||
const newEntity = this.catalogEntityFromCluster(cluster);
|
||||
|
||||
if (entityIndex === -1) {
|
||||
this.catalogSource.push(newEntity);
|
||||
} else {
|
||||
const oldEntity = this.catalogSource[entityIndex];
|
||||
|
||||
newEntity.status.phase = cluster.disconnected ? "disconnected" : "connected";
|
||||
newEntity.status.active = !cluster.disconnected;
|
||||
newEntity.metadata.labels = {
|
||||
...newEntity.metadata.labels,
|
||||
...oldEntity.metadata.labels
|
||||
};
|
||||
this.catalogSource.splice(entityIndex, 1, newEntity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action syncClustersFromCatalog(entities: KubernetesCluster[]) {
|
||||
entities.filter((entity) => entity.metadata.source !== "local").forEach((entity: KubernetesCluster) => {
|
||||
const cluster = clusterStore.getById(entity.metadata.uid);
|
||||
|
||||
if (!cluster) {
|
||||
clusterStore.addCluster({
|
||||
id: entity.metadata.uid,
|
||||
enabled: true,
|
||||
ownerRef: clusterOwnerRef,
|
||||
preferences: {
|
||||
clusterName: entity.metadata.name
|
||||
},
|
||||
kubeConfigPath: entity.spec.kubeconfigPath,
|
||||
contextName: entity.spec.kubeconfigContext
|
||||
});
|
||||
} else {
|
||||
cluster.enabled = true;
|
||||
if (!cluster.ownerRef) cluster.ownerRef = clusterOwnerRef;
|
||||
cluster.preferences.clusterName = entity.metadata.name;
|
||||
cluster.kubeConfigPath = entity.spec.kubeconfigPath;
|
||||
cluster.contextName = entity.spec.kubeconfigContext;
|
||||
|
||||
entity.status = {
|
||||
phase: cluster.disconnected ? "disconnected" : "connected",
|
||||
active: !cluster.disconnected
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected catalogEntityFromCluster(cluster: Cluster) {
|
||||
return new KubernetesCluster(toJS({
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
metadata: {
|
||||
uid: cluster.id,
|
||||
name: cluster.name,
|
||||
source: "local",
|
||||
labels: {
|
||||
"distro": (cluster.metadata["distribution"] || "unknown").toString()
|
||||
}
|
||||
},
|
||||
spec: {
|
||||
kubeconfigPath: cluster.kubeConfigPath,
|
||||
kubeconfigContext: cluster.contextName
|
||||
},
|
||||
status: {
|
||||
phase: cluster.disconnected ? "disconnected" : "connected",
|
||||
reason: "",
|
||||
message: "",
|
||||
active: !cluster.disconnected
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected onNetworkOffline() {
|
||||
logger.info("[CLUSTER-MANAGER]: network is offline");
|
||||
clusterStore.enabledClustersList.forEach((cluster) => {
|
||||
|
||||
+4
-7
@@ -1,7 +1,6 @@
|
||||
import { ipcMain } from "electron";
|
||||
import type { ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../common/cluster-store";
|
||||
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
||||
import type { WorkspaceId } from "../common/workspace-store";
|
||||
import { action, comparer, computed, observable, reaction, toJS, when } from "mobx";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import { broadcastMessage, InvalidKubeconfigChannel, ClusterListNamespaceForbiddenChannel } from "../common/ipc";
|
||||
@@ -104,18 +103,16 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
* @observable
|
||||
*/
|
||||
@observable contextName: string;
|
||||
/**
|
||||
* Workspace id
|
||||
*
|
||||
* @observable
|
||||
*/
|
||||
@observable workspace: WorkspaceId;
|
||||
/**
|
||||
* Path to kubeconfig
|
||||
*
|
||||
* @observable
|
||||
*/
|
||||
@observable kubeConfigPath: string;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@observable workspace: string;
|
||||
/**
|
||||
* Kubernetes API server URL
|
||||
*
|
||||
|
||||
+5
-2
@@ -17,7 +17,6 @@ import { registerFileProtocol } from "../common/register-protocol";
|
||||
import logger from "./logger";
|
||||
import { clusterStore } from "../common/cluster-store";
|
||||
import { userStore } from "../common/user-store";
|
||||
import { workspaceStore } from "../common/workspace-store";
|
||||
import { appEventBus } from "../common/event-bus";
|
||||
import { extensionLoader } from "../extensions/extension-loader";
|
||||
import { extensionsStore } from "../extensions/extensions-store";
|
||||
@@ -30,6 +29,9 @@ import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
||||
import { bindBroadcastHandlers } from "../common/ipc";
|
||||
import { startUpdateChecking } from "./app-updater";
|
||||
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
||||
import { CatalogPusher } from "./catalog-pusher";
|
||||
import { catalogEntityRegistry } from "../common/catalog-entity-registry";
|
||||
import { hotbarStore } from "../common/hotbar-store";
|
||||
|
||||
const workingDir = path.join(app.getPath("appData"), appName);
|
||||
let proxyPort: number;
|
||||
@@ -107,7 +109,7 @@ app.on("ready", async () => {
|
||||
await Promise.all([
|
||||
userStore.load(),
|
||||
clusterStore.load(),
|
||||
workspaceStore.load(),
|
||||
hotbarStore.load(),
|
||||
extensionsStore.load(),
|
||||
filesystemProvisionerStore.load(),
|
||||
]);
|
||||
@@ -164,6 +166,7 @@ app.on("ready", async () => {
|
||||
}
|
||||
|
||||
ipcMain.on(IpcRendererNavigationEvents.LOADED, () => {
|
||||
CatalogPusher.init(catalogEntityRegistry);
|
||||
startUpdateChecking();
|
||||
LensProtocolRouterMain
|
||||
.getInstance<LensProtocolRouterMain>()
|
||||
|
||||
@@ -7,6 +7,7 @@ import { preferencesURL } from "../renderer/components/+preferences/preferences.
|
||||
import { whatsNewURL } from "../renderer/components/+whats-new/whats-new.route";
|
||||
import { clusterSettingsURL } from "../renderer/components/+cluster-settings/cluster-settings.route";
|
||||
import { extensionsURL } from "../renderer/components/+extensions/extensions.route";
|
||||
import { catalogURL } from "../renderer/components/+catalog/catalog.route";
|
||||
import { menuRegistry } from "../extensions/registries/menu-registry";
|
||||
import logger from "./logger";
|
||||
import { exitApp } from "./exit-app";
|
||||
@@ -175,6 +176,13 @@ export function buildMenu(windowManager: WindowManager) {
|
||||
const viewMenu: MenuItemConstructorOptions = {
|
||||
label: "View",
|
||||
submenu: [
|
||||
{
|
||||
label: "Catalog",
|
||||
accelerator: "Shift+CmdOrCtrl+C",
|
||||
click() {
|
||||
navigate(catalogURL());
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "Command Palette...",
|
||||
accelerator: "Shift+CmdOrCtrl+P",
|
||||
|
||||
@@ -5,10 +5,7 @@ import { autorun } from "mobx";
|
||||
import { showAbout } from "./menu";
|
||||
import { checkForUpdates } from "./app-updater";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { clusterStore } from "../common/cluster-store";
|
||||
import { workspaceStore } from "../common/workspace-store";
|
||||
import { preferencesURL } from "../renderer/components/+preferences/preferences.route";
|
||||
import { clusterViewURL } from "../renderer/components/cluster-manager/cluster-view.route";
|
||||
import logger from "./logger";
|
||||
import { isDevelopment, isWindows } from "../common/vars";
|
||||
import { exitApp } from "./exit-app";
|
||||
@@ -78,28 +75,6 @@ function createTrayMenu(windowManager: WindowManager): Menu {
|
||||
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to nativate to Preferences`, { error }));
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Clusters",
|
||||
submenu: workspaceStore.enabledWorkspacesList
|
||||
.map(workspace => [workspace, clusterStore.getByWorkspaceId(workspace.id)] as const)
|
||||
.map(([workspace, clusters]) => ({
|
||||
label: workspace.name,
|
||||
toolTip: workspace.description,
|
||||
enabled: clusters.length > 0,
|
||||
submenu: clusters.map(({ id: clusterId, name: label, online, workspace }) => ({
|
||||
checked: online,
|
||||
type: "checkbox",
|
||||
label,
|
||||
toolTip: clusterId,
|
||||
click() {
|
||||
workspaceStore.setActive(workspace);
|
||||
windowManager
|
||||
.navigate(clusterViewURL({ params: { clusterId } }))
|
||||
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to nativate to cluster`, { clusterId, error }));
|
||||
}
|
||||
}))
|
||||
})),
|
||||
},
|
||||
{
|
||||
label: "Check for updates",
|
||||
click() {
|
||||
|
||||
Reference in new issue
Block a user