diff --git a/src/common/ipc/index.ts b/src/common/ipc/index.ts index a34890472e..c5e864dc75 100644 --- a/src/common/ipc/index.ts +++ b/src/common/ipc/index.ts @@ -1,3 +1,4 @@ export * from "./ipc"; +export * from "./invalid-kubeconfig"; export * from "./update-available"; export * from "./type-enforced-ipc"; diff --git a/src/common/ipc/invalid-kubeconfig/index.ts b/src/common/ipc/invalid-kubeconfig/index.ts new file mode 100644 index 0000000000..9e8e7921d7 --- /dev/null +++ b/src/common/ipc/invalid-kubeconfig/index.ts @@ -0,0 +1,3 @@ +export const InvalidKubeconfigChannel = "invalid-kubeconfig"; + +export type InvalidKubeConfigArgs = [clusterId: string]; diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 93083cf7cb..198d24c2f9 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -4,7 +4,7 @@ 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 } from "../common/ipc"; +import { broadcastMessage, InvalidKubeconfigChannel } from "../common/ipc"; import { ContextHandler } from "./context-handler"; import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node"; import { Kubectl } from "./kubectl"; @@ -266,6 +266,7 @@ export class Cluster implements ClusterModel, ClusterState { } catch(err) { logger.error(err); logger.error(`[CLUSTER] Failed to load kubeconfig for the cluster '${this.name || this.contextName}' (context: ${this.contextName}, kubeconfig: ${this.kubeConfigPath}).`); + broadcastMessage(InvalidKubeconfigChannel, model.id); } } diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index 8ee859d2cf..01e6002309 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -52,7 +52,7 @@ export class CreateResource extends React.Component { ); if (errors.length) { - errors.forEach(Notifications.error); + errors.forEach(error => Notifications.error(error)); if (!createdResources.length) throw errors[0]; } const successMessage = ( diff --git a/src/renderer/components/notifications/notifications.tsx b/src/renderer/components/notifications/notifications.tsx index 0c1ac692cf..206102b1a3 100644 --- a/src/renderer/components/notifications/notifications.tsx +++ b/src/renderer/components/notifications/notifications.tsx @@ -21,11 +21,12 @@ export class Notifications extends React.Component { }); } - static error(message: NotificationMessage) { + static error(message: NotificationMessage, customOpts: Partial = {}) { notificationsStore.add({ message, timeout: 10000, - status: NotificationStatus.ERROR + status: NotificationStatus.ERROR, + ...customOpts }); } diff --git a/src/renderer/ipc/index.tsx b/src/renderer/ipc/index.tsx index b9644f7404..544cefbf78 100644 --- a/src/renderer/ipc/index.tsx +++ b/src/renderer/ipc/index.tsx @@ -5,6 +5,7 @@ import { Notifications, notificationsStore } from "../components/notifications"; import { Button } from "../components/button"; import { isMac } from "../../common/vars"; import * as uuid from "uuid"; +import { invalidKubeconfigHandler } from "./invalid-kubeconfig-handler"; function sendToBackchannel(backchannel: string, notificationId: string, data: BackchannelArg): void { notificationsStore.remove(notificationId); @@ -58,4 +59,5 @@ export function registerIpcHandlers() { listener: UpdateAvailableHandler, verifier: areArgsUpdateAvailableFromMain, }); + onCorrect(invalidKubeconfigHandler); } diff --git a/src/renderer/ipc/invalid-kubeconfig-handler.tsx b/src/renderer/ipc/invalid-kubeconfig-handler.tsx new file mode 100644 index 0000000000..cadf7e4e3f --- /dev/null +++ b/src/renderer/ipc/invalid-kubeconfig-handler.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { ipcRenderer, IpcRendererEvent, shell } from "electron"; +import { clusterStore } from "../../common/cluster-store"; +import { InvalidKubeConfigArgs, InvalidKubeconfigChannel } from "../../common/ipc/invalid-kubeconfig"; +import { Notifications, notificationsStore } from "../components/notifications"; +import { Button } from "../components/button"; + +export const invalidKubeconfigHandler = { + source: ipcRenderer, + channel: InvalidKubeconfigChannel, + listener: InvalidKubeconfigListener, + verifier: (args: [unknown]): args is InvalidKubeConfigArgs => { + return args.length === 1 && typeof args[0] === "string" && !!clusterStore.getById(args[0]); + }, +}; + +function InvalidKubeconfigListener(event: IpcRendererEvent, ...[clusterId]: InvalidKubeConfigArgs): void { + const notificationId = `invalid-kubeconfig:${clusterId}`; + const cluster = clusterStore.getById(clusterId); + const contextName = cluster.name !== cluster.contextName ? `(context: ${cluster.contextName})` : ""; + + Notifications.error( + ( +
+ Cluster with Invalid Kubeconfig Detected! +

Cluster {cluster.name} has invalid kubeconfig {contextName} and cannot be displayed. + Please fix the { e.preventDefault(); shell.showItemInFolder(cluster.kubeConfigPath); }}>kubeconfig manually and restart Lens + or remove the cluster.

+

Do you want to remove the cluster now?

+
+
+
+ ), + { + id: notificationId, + timeout: 0 + } + ); +} + +