mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge remote-tracking branch 'origin/master' into eliminate-gst-from-app-paths
Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
52 files changed
+1168
-409
No files matched your search
+10
-14
@@ -55,28 +55,23 @@ import { WeblinkStore } from "../common/weblink-store";
|
||||
import { SentryInit } from "../common/sentry";
|
||||
import { ensureDir } from "fs-extra";
|
||||
import { initMenu } from "./menu/menu";
|
||||
import { initTray } from "./tray";
|
||||
import { kubeApiRequest } from "./proxy-functions";
|
||||
import { initTray } from "./tray/tray";
|
||||
import { ShellSession } from "./shell-session/shell-session";
|
||||
import { getDi } from "./getDi";
|
||||
import extensionLoaderInjectable from "../extensions/extension-loader/extension-loader.injectable";
|
||||
import lensProtocolRouterMainInjectable from "./protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable";
|
||||
import extensionDiscoveryInjectable
|
||||
from "../extensions/extension-discovery/extension-discovery.injectable";
|
||||
import directoryForExesInjectable
|
||||
from "../common/app-paths/directory-for-exes/directory-for-exes.injectable";
|
||||
import initIpcMainHandlersInjectable
|
||||
from "./initializers/init-ipc-main-handlers/init-ipc-main-handlers.injectable";
|
||||
import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable";
|
||||
import directoryForExesInjectable from "../common/app-paths/directory-for-exes/directory-for-exes.injectable";
|
||||
import initIpcMainHandlersInjectable from "./initializers/init-ipc-main-handlers/init-ipc-main-handlers.injectable";
|
||||
import electronMenuItemsInjectable from "./menu/electron-menu-items.injectable";
|
||||
import directoryForKubeConfigsInjectable
|
||||
from "../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import kubeconfigSyncManagerInjectable
|
||||
from "./catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable";
|
||||
import directoryForKubeConfigsInjectable from "../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||
import kubeconfigSyncManagerInjectable from "./catalog-sources/kubeconfig-sync-manager/kubeconfig-sync-manager.injectable";
|
||||
import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable";
|
||||
import routerInjectable from "./router/router.injectable";
|
||||
import shellApiRequestInjectable
|
||||
from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||
import userStoreInjectable from "../common/user-store/user-store.injectable";
|
||||
import trayMenuItemsInjectable from "./tray/tray-menu-items.injectable";
|
||||
|
||||
const di = getDi();
|
||||
|
||||
@@ -255,10 +250,11 @@ di.runSetups().then(() => {
|
||||
const windowManager = WindowManager.createInstance();
|
||||
|
||||
const menuItems = di.inject(electronMenuItemsInjectable);
|
||||
const trayMenuItems = di.inject(trayMenuItemsInjectable);
|
||||
|
||||
onQuitCleanup.push(
|
||||
initMenu(windowManager, menuItems),
|
||||
initTray(windowManager),
|
||||
initTray(windowManager, trayMenuItems),
|
||||
() => ShellSession.cleanup(),
|
||||
);
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ const electronMenuItemsInjectable = getInjectable({
|
||||
const extensions = di.inject(mainExtensionsInjectable);
|
||||
|
||||
return computed(() =>
|
||||
extensions.get().flatMap((extension) => extension.appMenus),
|
||||
);
|
||||
extensions.get().flatMap((extension) => extension.appMenus));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "cluster":
|
||||
switch (queryName) {
|
||||
case "memoryUsage":
|
||||
return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes))`.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`);
|
||||
return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes))`.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`);
|
||||
case "workloadMemoryUsage":
|
||||
return `sum(container_memory_working_set_bytes{container!="", instance=~"${opts.nodes}"}) by (component)`;
|
||||
case "memoryRequests":
|
||||
@@ -50,7 +50,7 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="memory"})`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`;
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`;
|
||||
case "cpuRequests":
|
||||
return `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"})`;
|
||||
case "cpuLimits":
|
||||
@@ -66,31 +66,31 @@ export class PrometheusOperator extends PrometheusProvider {
|
||||
case "podAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="pods"})`;
|
||||
case "fsSize":
|
||||
return `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`;
|
||||
return `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`;
|
||||
case "fsUsage":
|
||||
return `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`;
|
||||
return `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`;
|
||||
}
|
||||
break;
|
||||
case "nodes":
|
||||
switch (queryName) {
|
||||
case "memoryUsage":
|
||||
return `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (node)`;
|
||||
return `sum((node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) * on (pod, namespace) group_left(node) kube_pod_info) by (node)`;
|
||||
case "workloadMemoryUsage":
|
||||
return `sum(container_memory_working_set_bytes{container!=""}) by (node)`;
|
||||
return `sum(container_memory_working_set_bytes{container!="POD", container!=""}) by (node)`;
|
||||
case "memoryCapacity":
|
||||
return `sum(kube_node_status_capacity{resource="memory"}) by (node)`;
|
||||
case "memoryAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="memory"}) by (node)`;
|
||||
case "cpuUsage":
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(node)`;
|
||||
return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}]) * on (pod, namespace) group_left(node) kube_pod_info) by (node)`;
|
||||
case "cpuCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`;
|
||||
case "cpuAllocatableCapacity":
|
||||
return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`;
|
||||
case "fsSize":
|
||||
return `sum(node_filesystem_size_bytes{mountpoint="/"}) by (node)`;
|
||||
return `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info) by (node)`;
|
||||
case "fsUsage":
|
||||
return `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (node)`;
|
||||
return `sum((node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) * on (pod, namespace) group_left(node) kube_pod_info) by (node)`;
|
||||
}
|
||||
break;
|
||||
case "pods":
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||
import { computed } from "mobx";
|
||||
import mainExtensionsInjectable from "../../extensions/main-extensions.injectable";
|
||||
|
||||
const trayItemsInjectable = getInjectable({
|
||||
lifecycle: lifecycleEnum.singleton,
|
||||
|
||||
instantiate: (di) => {
|
||||
const extensions = di.inject(mainExtensionsInjectable);
|
||||
|
||||
return computed(() =>
|
||||
extensions.get().flatMap(extension => extension.trayMenus));
|
||||
},
|
||||
});
|
||||
|
||||
export default trayItemsInjectable;
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable";
|
||||
import { LensMainExtension } from "../../extensions/lens-main-extension";
|
||||
import trayItemsInjectable from "./tray-menu-items.injectable";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { computed, ObservableMap, runInAction } from "mobx";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import mainExtensionsInjectable from "../../extensions/main-extensions.injectable";
|
||||
import type { TrayMenuRegistration } from "./tray-menu-registration";
|
||||
|
||||
describe("tray-menu-items", () => {
|
||||
let di: ConfigurableDependencyInjectionContainer;
|
||||
let trayMenuItems: IComputedValue<TrayMenuRegistration[]>;
|
||||
let extensionsStub: ObservableMap<string, LensMainExtension>;
|
||||
|
||||
beforeEach(() => {
|
||||
di = getDiForUnitTesting();
|
||||
|
||||
extensionsStub = new ObservableMap();
|
||||
|
||||
di.override(
|
||||
mainExtensionsInjectable,
|
||||
() => computed(() => [...extensionsStub.values()]),
|
||||
);
|
||||
|
||||
trayMenuItems = di.inject(trayItemsInjectable);
|
||||
});
|
||||
|
||||
it("does not have any items yet", () => {
|
||||
expect(trayMenuItems.get()).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe("when extension is enabled", () => {
|
||||
beforeEach(() => {
|
||||
const someExtension = new SomeTestExtension({
|
||||
id: "some-extension-id",
|
||||
trayMenus: [{ label: "tray-menu-from-some-extension" }],
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
extensionsStub.set("some-extension-id", someExtension);
|
||||
});
|
||||
});
|
||||
|
||||
it("has tray menu items", () => {
|
||||
expect(trayMenuItems.get()).toEqual([
|
||||
{
|
||||
label: "tray-menu-from-some-extension",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("when disabling extension, does not have tray menu items", () => {
|
||||
runInAction(() => {
|
||||
extensionsStub.delete("some-extension-id");
|
||||
});
|
||||
|
||||
expect(trayMenuItems.get()).toHaveLength(0);
|
||||
});
|
||||
|
||||
describe("when other extension is enabled", () => {
|
||||
beforeEach(() => {
|
||||
const someOtherExtension = new SomeTestExtension({
|
||||
id: "some-extension-id",
|
||||
trayMenus: [{ label: "some-label-from-second-extension" }],
|
||||
});
|
||||
|
||||
runInAction(() => {
|
||||
extensionsStub.set("some-other-extension-id", someOtherExtension);
|
||||
});
|
||||
});
|
||||
|
||||
it("has tray menu items for both extensions", () => {
|
||||
expect(trayMenuItems.get()).toEqual([
|
||||
{
|
||||
label: "tray-menu-from-some-extension",
|
||||
},
|
||||
|
||||
{
|
||||
label: "some-label-from-second-extension",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("when extension is disabled, still returns tray menu items for extensions that are enabled", () => {
|
||||
runInAction(() => {
|
||||
extensionsStub.delete("some-other-extension-id");
|
||||
});
|
||||
|
||||
expect(trayMenuItems.get()).toEqual([
|
||||
{
|
||||
label: "tray-menu-from-some-extension",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class SomeTestExtension extends LensMainExtension {
|
||||
constructor({ id, trayMenus }: {
|
||||
id: string;
|
||||
trayMenus: TrayMenuRegistration[];
|
||||
}) {
|
||||
super({
|
||||
id,
|
||||
absolutePath: "irrelevant",
|
||||
isBundled: false,
|
||||
isCompatible: false,
|
||||
isEnabled: false,
|
||||
manifest: { name: id, version: "some-version" },
|
||||
manifestPath: "irrelevant",
|
||||
});
|
||||
|
||||
this.trayMenus = trayMenus;
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export interface TrayMenuRegistration {
|
||||
label?: string;
|
||||
click?: (menuItem: TrayMenuRegistration) => void;
|
||||
id?: string;
|
||||
type?: "normal" | "separator" | "submenu"
|
||||
toolTip?: string;
|
||||
enabled?: boolean;
|
||||
submenu?: TrayMenuRegistration[]
|
||||
}
|
||||
@@ -20,16 +20,18 @@
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import packageInfo from "../../package.json";
|
||||
import packageInfo from "../../../package.json";
|
||||
import { Menu, Tray } from "electron";
|
||||
import { autorun } from "mobx";
|
||||
import { showAbout } from "./menu/menu";
|
||||
import { checkForUpdates, isAutoUpdateEnabled } from "./app-updater";
|
||||
import type { WindowManager } from "./window-manager";
|
||||
import logger from "./logger";
|
||||
import { isDevelopment, isWindows, productName } from "../common/vars";
|
||||
import { exitApp } from "./exit-app";
|
||||
import { preferencesURL } from "../common/routes";
|
||||
import { autorun, IComputedValue } from "mobx";
|
||||
import { showAbout } from "../menu/menu";
|
||||
import { checkForUpdates, isAutoUpdateEnabled } from "../app-updater";
|
||||
import type { WindowManager } from "../window-manager";
|
||||
import logger from "../logger";
|
||||
import { isDevelopment, isWindows, productName } from "../../common/vars";
|
||||
import { exitApp } from "../exit-app";
|
||||
import { preferencesURL } from "../../common/routes";
|
||||
import { toJS } from "../../common/utils";
|
||||
import type { TrayMenuRegistration } from "./tray-menu-registration";
|
||||
|
||||
const TRAY_LOG_PREFIX = "[TRAY]";
|
||||
|
||||
@@ -44,7 +46,10 @@ export function getTrayIcon(): string {
|
||||
);
|
||||
}
|
||||
|
||||
export function initTray(windowManager: WindowManager) {
|
||||
export function initTray(
|
||||
windowManager: WindowManager,
|
||||
trayMenuItems: IComputedValue<TrayMenuRegistration[]>,
|
||||
) {
|
||||
const icon = getTrayIcon();
|
||||
|
||||
tray = new Tray(icon);
|
||||
@@ -62,7 +67,7 @@ export function initTray(windowManager: WindowManager) {
|
||||
const disposers = [
|
||||
autorun(() => {
|
||||
try {
|
||||
const menu = createTrayMenu(windowManager);
|
||||
const menu = createTrayMenu(windowManager, toJS(trayMenuItems.get()));
|
||||
|
||||
tray.setContextMenu(menu);
|
||||
} catch (error) {
|
||||
@@ -78,8 +83,21 @@ export function initTray(windowManager: WindowManager) {
|
||||
};
|
||||
}
|
||||
|
||||
function createTrayMenu(windowManager: WindowManager): Menu {
|
||||
const template: Electron.MenuItemConstructorOptions[] = [
|
||||
function getMenuItemConstructorOptions(trayItem: TrayMenuRegistration): Electron.MenuItemConstructorOptions {
|
||||
return {
|
||||
...trayItem,
|
||||
submenu: trayItem.submenu ? trayItem.submenu.map(getMenuItemConstructorOptions) : undefined,
|
||||
click: trayItem.click ? () => {
|
||||
trayItem.click(trayItem);
|
||||
} : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function createTrayMenu(
|
||||
windowManager: WindowManager,
|
||||
extensionTrayItems: TrayMenuRegistration[],
|
||||
): Menu {
|
||||
let template: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: `Open ${productName}`,
|
||||
click() {
|
||||
@@ -108,6 +126,8 @@ function createTrayMenu(windowManager: WindowManager): Menu {
|
||||
});
|
||||
}
|
||||
|
||||
template = template.concat(extensionTrayItems.map(getMenuItemConstructorOptions));
|
||||
|
||||
return Menu.buildFromTemplate(template.concat([
|
||||
{
|
||||
label: `About ${productName}`,
|
||||
Reference in new issue
Block a user