mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Remove multiple usages of shared global state
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
8204dc5a9a
commit
4eb4edaed9
@ -15,8 +15,8 @@ import { loadConfigFromFile, loadConfigFromFileSync, validateKubeConfig } from "
|
|||||||
import type { KubeApiResource, KubeResource } from "../rbac";
|
import type { KubeApiResource, KubeResource } from "../rbac";
|
||||||
import { apiResourceRecord, apiResources } from "../rbac";
|
import { apiResourceRecord, apiResources } from "../rbac";
|
||||||
import logger from "../../main/logger";
|
import logger from "../../main/logger";
|
||||||
import { VersionDetector } from "../../main/cluster-detectors/version-detector";
|
import type { VersionDetector } from "../../main/cluster-detectors/version-detector";
|
||||||
import { DetectorRegistry } from "../../main/cluster-detectors/detector-registry";
|
import type { DetectorRegistry } from "../../main/cluster-detectors/detector-registry";
|
||||||
import plimit from "p-limit";
|
import plimit from "p-limit";
|
||||||
import type { ClusterState, ClusterRefreshOptions, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate } from "../cluster-types";
|
import type { ClusterState, ClusterRefreshOptions, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate } from "../cluster-types";
|
||||||
import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus } from "../cluster-types";
|
import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus } from "../cluster-types";
|
||||||
@ -33,6 +33,8 @@ export interface ClusterDependencies {
|
|||||||
createKubectl: (clusterVersion: string) => Kubectl;
|
createKubectl: (clusterVersion: string) => Kubectl;
|
||||||
createAuthorizationReview: (config: KubeConfig) => CanI;
|
createAuthorizationReview: (config: KubeConfig) => CanI;
|
||||||
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
||||||
|
detectorRegistry: DetectorRegistry;
|
||||||
|
createVersionDetector: (cluster: Cluster) => VersionDetector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -425,7 +427,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
@action
|
@action
|
||||||
async refreshMetadata() {
|
async refreshMetadata() {
|
||||||
logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta());
|
logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta());
|
||||||
const metadata = await DetectorRegistry.getInstance().detectForCluster(this);
|
const metadata = await this.dependencies.detectorRegistry.detectForCluster(this);
|
||||||
const existingMetadata = this.metadata;
|
const existingMetadata = this.metadata;
|
||||||
|
|
||||||
this.metadata = Object.assign(existingMetadata, metadata);
|
this.metadata = Object.assign(existingMetadata, metadata);
|
||||||
@ -488,7 +490,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
|
|
||||||
protected async getConnectionStatus(): Promise<ClusterStatus> {
|
protected async getConnectionStatus(): Promise<ClusterStatus> {
|
||||||
try {
|
try {
|
||||||
const versionDetector = new VersionDetector(this);
|
const versionDetector = this.dependencies.createVersionDetector(this);
|
||||||
const versionData = await versionDetector.detect();
|
const versionData = await versionDetector.detect();
|
||||||
|
|
||||||
this.metadata.version = versionData.value;
|
this.metadata.version = versionData.value;
|
||||||
|
|||||||
18
src/common/fs/ensure-dir.injectable.ts
Normal file
18
src/common/fs/ensure-dir.injectable.ts
Normal 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 fsInjectable from "./fs.injectable";
|
||||||
|
|
||||||
|
const ensureDirInjectable = getInjectable({
|
||||||
|
id: "ensure-dir",
|
||||||
|
|
||||||
|
// TODO: Remove usages of ensureDir from business logic.
|
||||||
|
// TODO: Read, Write, Watch etc. operations should do this internally.
|
||||||
|
instantiate: (di) => di.inject(fsInjectable).ensureDir,
|
||||||
|
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ensureDirInjectable;
|
||||||
18
src/common/weblink-store.injectable.ts
Normal file
18
src/common/weblink-store.injectable.ts
Normal 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 { WeblinkStore } from "./weblink-store";
|
||||||
|
|
||||||
|
const weblinkStoreInjectable = getInjectable({
|
||||||
|
id: "weblink-store",
|
||||||
|
|
||||||
|
instantiate: () => {
|
||||||
|
WeblinkStore.resetInstance();
|
||||||
|
|
||||||
|
return WeblinkStore.createInstance();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default weblinkStoreInjectable;
|
||||||
@ -4,13 +4,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { LensExtension } from "./lens-extension";
|
import { LensExtension } from "./lens-extension";
|
||||||
import { WindowManager } from "../main/window-manager";
|
|
||||||
import { catalogEntityRegistry } from "../main/catalog";
|
import { catalogEntityRegistry } from "../main/catalog";
|
||||||
import type { CatalogEntity } from "../common/catalog";
|
import type { CatalogEntity } from "../common/catalog";
|
||||||
import type { IObservableArray } from "mobx";
|
import type { IObservableArray } from "mobx";
|
||||||
import type { MenuRegistration } from "../main/menu/menu-registration";
|
import type { MenuRegistration } from "../main/menu/menu-registration";
|
||||||
import type { TrayMenuRegistration } from "../main/tray/tray-menu-registration";
|
import type { TrayMenuRegistration } from "../main/tray/tray-menu-registration";
|
||||||
import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/shell-env-modifier-registration";
|
import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/shell-env-modifier-registration";
|
||||||
|
import {
|
||||||
|
Environments,
|
||||||
|
getEnvironmentSpecificLegacyGlobalDiForExtensionApi,
|
||||||
|
} from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
|
import windowManagerInjectable from "../main/window-manager.injectable";
|
||||||
|
|
||||||
export class LensMainExtension extends LensExtension {
|
export class LensMainExtension extends LensExtension {
|
||||||
appMenus: MenuRegistration[] = [];
|
appMenus: MenuRegistration[] = [];
|
||||||
@ -18,21 +22,25 @@ export class LensMainExtension extends LensExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* implement this to modify the shell environment that Lens terminals are opened with. The ShellEnvModifier type has the signature
|
* implement this to modify the shell environment that Lens terminals are opened with. The ShellEnvModifier type has the signature
|
||||||
*
|
*
|
||||||
* (ctx: ShellEnvContext, env: Record<string, string | undefined>) => Record<string, string | undefined>
|
* (ctx: ShellEnvContext, env: Record<string, string | undefined>) => Record<string, string | undefined>
|
||||||
*
|
*
|
||||||
* @param ctx the shell environment context, specifically the relevant catalog entity for the terminal. This can be used, for example, to get
|
* @param ctx the shell environment context, specifically the relevant catalog entity for the terminal. This can be used, for example, to get
|
||||||
* cluster-specific information that can be made available in the shell environment by the implementation of terminalShellEnvModifier
|
* cluster-specific information that can be made available in the shell environment by the implementation of terminalShellEnvModifier
|
||||||
*
|
*
|
||||||
* @param env the current shell environment that the terminal will be opened with. The implementation should modify this as desired.
|
* @param env the current shell environment that the terminal will be opened with. The implementation should modify this as desired.
|
||||||
*
|
*
|
||||||
* @returns the modified shell environment that the terminal will be opened with. The implementation must return env as passed in, if it
|
* @returns the modified shell environment that the terminal will be opened with. The implementation must return env as passed in, if it
|
||||||
* does not modify the shell environment
|
* does not modify the shell environment
|
||||||
*/
|
*/
|
||||||
terminalShellEnvModifier?: ShellEnvModifier;
|
terminalShellEnvModifier?: ShellEnvModifier;
|
||||||
|
|
||||||
async navigate(pageId?: string, params?: Record<string, any>, frameId?: number) {
|
async navigate(pageId?: string, params?: Record<string, any>, frameId?: number) {
|
||||||
return WindowManager.getInstance().navigateExtension(this.id, pageId, params, frameId);
|
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main);
|
||||||
|
|
||||||
|
const windowManager = di.inject(windowManagerInjectable);
|
||||||
|
|
||||||
|
return windowManager.navigateExtension(this.id, pageId, params, frameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
addCatalogSource(id: string, source: IObservableArray<CatalogEntity>) {
|
addCatalogSource(id: string, source: IObservableArray<CatalogEntity>) {
|
||||||
|
|||||||
@ -3,8 +3,17 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { WindowManager } from "../../main/window-manager";
|
import {
|
||||||
|
Environments,
|
||||||
|
getEnvironmentSpecificLegacyGlobalDiForExtensionApi,
|
||||||
|
} from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
|
|
||||||
|
import windowManagerInjectable from "../../main/window-manager.injectable";
|
||||||
|
|
||||||
export function navigate(url: string) {
|
export function navigate(url: string) {
|
||||||
return WindowManager.getInstance().navigate(url);
|
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main);
|
||||||
|
|
||||||
|
const windowManager = di.inject(windowManagerInjectable);
|
||||||
|
|
||||||
|
return windowManager.navigate(url);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
|||||||
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
import createContextHandlerInjectable from "../context-handler/create-context-handler.injectable";
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||||
|
import prometheusProviderRegistryInjectable
|
||||||
|
from "../prometheus/prometheus-provider-registry.injectable";
|
||||||
|
|
||||||
jest.mock("electron", () => ({
|
jest.mock("electron", () => ({
|
||||||
app: {
|
app: {
|
||||||
@ -74,6 +76,7 @@ const clusterStub = {
|
|||||||
|
|
||||||
describe("ContextHandler", () => {
|
describe("ContextHandler", () => {
|
||||||
let createContextHandler: (cluster: Cluster) => ContextHandler;
|
let createContextHandler: (cluster: Cluster) => ContextHandler;
|
||||||
|
let prometheusProviderRegistry: PrometheusProviderRegistry;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
@ -84,13 +87,12 @@ describe("ContextHandler", () => {
|
|||||||
|
|
||||||
di.override(createKubeAuthProxyInjectable, () => ({} as any));
|
di.override(createKubeAuthProxyInjectable, () => ({} as any));
|
||||||
|
|
||||||
createContextHandler = di.inject(createContextHandlerInjectable);
|
prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable);
|
||||||
|
|
||||||
PrometheusProviderRegistry.createInstance();
|
createContextHandler = di.inject(createContextHandlerInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
PrometheusProviderRegistry.resetInstance();
|
|
||||||
UserStore.resetInstance();
|
UserStore.resetInstance();
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
@ -102,17 +104,16 @@ describe("ContextHandler", () => {
|
|||||||
[0, 2],
|
[0, 2],
|
||||||
[0, 3],
|
[0, 3],
|
||||||
])("should throw from %d success(es) after %d failure(s)", async (successes, failures) => {
|
])("should throw from %d success(es) after %d failure(s)", async (successes, failures) => {
|
||||||
const reg = PrometheusProviderRegistry.getInstance();
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for (let i = 0; i < failures; i += 1) {
|
for (let i = 0; i < failures; i += 1) {
|
||||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||||
|
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < successes; i += 1) {
|
for (let i = 0; i < successes; i += 1) {
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
@ -133,17 +134,16 @@ describe("ContextHandler", () => {
|
|||||||
[2, 2],
|
[2, 2],
|
||||||
[2, 3],
|
[2, 3],
|
||||||
])("should pick the first provider of %d success(es) after %d failure(s)", async (successes, failures) => {
|
])("should pick the first provider of %d success(es) after %d failure(s)", async (successes, failures) => {
|
||||||
const reg = PrometheusProviderRegistry.getInstance();
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for (let i = 0; i < failures; i += 1) {
|
for (let i = 0; i < failures; i += 1) {
|
||||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||||
|
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < successes; i += 1) {
|
for (let i = 0; i < successes; i += 1) {
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Unit test shouldn't access protected or private methods
|
// TODO: Unit test shouldn't access protected or private methods
|
||||||
@ -164,17 +164,16 @@ describe("ContextHandler", () => {
|
|||||||
[2, 2],
|
[2, 2],
|
||||||
[2, 3],
|
[2, 3],
|
||||||
])("should pick the first provider of %d success(es) before %d failure(s)", async (successes, failures) => {
|
])("should pick the first provider of %d success(es) before %d failure(s)", async (successes, failures) => {
|
||||||
const reg = PrometheusProviderRegistry.getInstance();
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
for (let i = 0; i < successes; i += 1) {
|
for (let i = 0; i < successes; i += 1) {
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < failures; i += 1) {
|
for (let i = 0; i < failures; i += 1) {
|
||||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||||
|
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Unit test shouldn't access protected or private methods
|
// TODO: Unit test shouldn't access protected or private methods
|
||||||
@ -195,23 +194,22 @@ describe("ContextHandler", () => {
|
|||||||
[2, 2],
|
[2, 2],
|
||||||
[2, 3],
|
[2, 3],
|
||||||
])("should pick the first provider of %d success(es) between %d failure(s)", async (successes, failures) => {
|
])("should pick the first provider of %d success(es) between %d failure(s)", async (successes, failures) => {
|
||||||
const reg = PrometheusProviderRegistry.getInstance();
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const beforeSuccesses = Math.floor(successes / 2);
|
const beforeSuccesses = Math.floor(successes / 2);
|
||||||
const afterSuccesses = successes - beforeSuccesses;
|
const afterSuccesses = successes - beforeSuccesses;
|
||||||
|
|
||||||
for (let i = 0; i < beforeSuccesses; i += 1) {
|
for (let i = 0; i < beforeSuccesses; i += 1) {
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < failures; i += 1) {
|
for (let i = 0; i < failures; i += 1) {
|
||||||
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
const serviceResult = i % 2 === 0 ? ServiceResult.Failure : ServiceResult.Undefined;
|
||||||
|
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, serviceResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < afterSuccesses; i += 1) {
|
for (let i = 0; i < afterSuccesses; i += 1) {
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Unit test shouldn't access protected or private methods
|
// TODO: Unit test shouldn't access protected or private methods
|
||||||
@ -223,12 +221,11 @@ describe("ContextHandler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("shouldn't pick the second provider of 2 success(es) after 1 failure(s)", async () => {
|
it("shouldn't pick the second provider of 2 success(es) after 1 failure(s)", async () => {
|
||||||
const reg = PrometheusProviderRegistry.getInstance();
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Failure));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Failure));
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||||
reg.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
prometheusProviderRegistry.registerProvider(new TestProvider(`id_${count++}`, ServiceResult.Success));
|
||||||
|
|
||||||
// TODO: Unit test shouldn't access protected or private methods
|
// TODO: Unit test shouldn't access protected or private methods
|
||||||
const contextHandler = createContextHandler(clusterStub) as any;
|
const contextHandler = createContextHandler(clusterStub) as any;
|
||||||
|
|||||||
@ -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 electronAppInjectable from "./electron-app.injectable";
|
||||||
|
|
||||||
|
const exitAppInjectable = getInjectable({
|
||||||
|
id: "exit-app",
|
||||||
|
|
||||||
|
instantiate: (di) => () => {
|
||||||
|
const app = di.inject(electronAppInjectable);
|
||||||
|
|
||||||
|
app.exit(0);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default exitAppInjectable;
|
||||||
@ -10,7 +10,6 @@ import type { Cluster } from "../../../common/cluster/cluster";
|
|||||||
import { computeDiff as computeDiffFor, configToModels } from "../kubeconfig-sync-manager/kubeconfig-sync-manager";
|
import { computeDiff as computeDiffFor, configToModels } from "../kubeconfig-sync-manager/kubeconfig-sync-manager";
|
||||||
import mockFs from "mock-fs";
|
import mockFs from "mock-fs";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { ClusterManager } from "../../cluster-manager";
|
|
||||||
import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable";
|
import clusterStoreInjectable from "../../../common/cluster-store/cluster-store.injectable";
|
||||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
|
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
|
||||||
@ -19,6 +18,7 @@ import { ClusterStore } from "../../../common/cluster-store/cluster-store";
|
|||||||
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||||
import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
|
import appVersionInjectable from "../../../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||||
import { runSetups } from "../../../common/setupable-injection-token/run-setups";
|
import { runSetups } from "../../../common/setupable-injection-token/run-setups";
|
||||||
|
import clusterManagerInjectable from "../../cluster-manager.injectable";
|
||||||
|
|
||||||
jest.mock("electron", () => ({
|
jest.mock("electron", () => ({
|
||||||
app: {
|
app: {
|
||||||
@ -53,19 +53,17 @@ describe("kubeconfig-sync.source tests", () => {
|
|||||||
computeDiff = computeDiffFor({
|
computeDiff = computeDiffFor({
|
||||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||||
createCluster: di.inject(createClusterInjectionToken),
|
createCluster: di.inject(createClusterInjectionToken),
|
||||||
|
clusterManager: di.inject(clusterManagerInjectable),
|
||||||
});
|
});
|
||||||
|
|
||||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||||
di.permitSideEffects(appVersionInjectable);
|
di.permitSideEffects(appVersionInjectable);
|
||||||
|
|
||||||
di.inject(clusterStoreInjectable);
|
di.inject(clusterStoreInjectable);
|
||||||
|
|
||||||
ClusterManager.createInstance();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
ClusterManager.resetInstance();
|
|
||||||
ClusterStore.resetInstance();
|
ClusterStore.resetInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
|
||||||
import { KubeconfigSyncManager } from "./kubeconfig-sync-manager";
|
import { KubeconfigSyncManager } from "./kubeconfig-sync-manager";
|
||||||
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
|
import { createClusterInjectionToken } from "../../../common/cluster/create-cluster-injection-token";
|
||||||
|
import clusterManagerInjectable from "../../cluster-manager.injectable";
|
||||||
|
|
||||||
const kubeconfigSyncManagerInjectable = getInjectable({
|
const kubeconfigSyncManagerInjectable = getInjectable({
|
||||||
id: "kubeconfig-sync-manager",
|
id: "kubeconfig-sync-manager",
|
||||||
@ -13,6 +14,7 @@ const kubeconfigSyncManagerInjectable = getInjectable({
|
|||||||
instantiate: (di) => new KubeconfigSyncManager({
|
instantiate: (di) => new KubeconfigSyncManager({
|
||||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||||
createCluster: di.inject(createClusterInjectionToken),
|
createCluster: di.inject(createClusterInjectionToken),
|
||||||
|
clusterManager: di.inject(clusterManagerInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -53,6 +53,7 @@ const fileSyncMaxAllowedFileReadSize = 16 * folderSyncMaxAllowedFileReadSize; //
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
directoryForKubeConfigs: string;
|
directoryForKubeConfigs: string;
|
||||||
createCluster: (model: ClusterModel) => Cluster;
|
createCluster: (model: ClusterModel) => Cluster;
|
||||||
|
clusterManager: ClusterManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
const kubeConfigSyncName = "lens:kube-sync";
|
const kubeConfigSyncName = "lens:kube-sync";
|
||||||
@ -164,7 +165,7 @@ type RootSourceValue = [Cluster, CatalogEntity];
|
|||||||
type RootSource = ObservableMap<string, RootSourceValue>;
|
type RootSource = ObservableMap<string, RootSourceValue>;
|
||||||
|
|
||||||
// exported for testing
|
// exported for testing
|
||||||
export const computeDiff = ({ directoryForKubeConfigs, createCluster }: Dependencies) => (contents: string, source: RootSource, filePath: string): void => {
|
export const computeDiff = ({ directoryForKubeConfigs, createCluster, clusterManager }: Dependencies) => (contents: string, source: RootSource, filePath: string): void => {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
try {
|
try {
|
||||||
const { config, error } = loadConfigFromString(contents);
|
const { config, error } = loadConfigFromString(contents);
|
||||||
@ -184,7 +185,7 @@ export const computeDiff = ({ directoryForKubeConfigs, createCluster }: Dependen
|
|||||||
// remove and disconnect clusters that were removed from the config
|
// remove and disconnect clusters that were removed from the config
|
||||||
if (!model) {
|
if (!model) {
|
||||||
// remove from the deleting set, so that if a new context of the same name is added, it isn't marked as deleting
|
// remove from the deleting set, so that if a new context of the same name is added, it isn't marked as deleting
|
||||||
ClusterManager.getInstance().deleting.delete(value[0].id);
|
clusterManager.deleting.delete(value[0].id);
|
||||||
|
|
||||||
value[0].disconnect();
|
value[0].disconnect();
|
||||||
source.delete(contextName);
|
source.delete(contextName);
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed, observable, reaction } from "mobx";
|
import { computed, observable, reaction } from "mobx";
|
||||||
import { WeblinkStore } from "../../common/weblink-store";
|
import type { WeblinkStore } from "../../common/weblink-store";
|
||||||
import { WebLink } from "../../common/catalog-entities";
|
import { WebLink } from "../../common/catalog-entities";
|
||||||
import { catalogEntityRegistry } from "../catalog";
|
import { catalogEntityRegistry } from "../catalog";
|
||||||
import got from "got";
|
import got from "got";
|
||||||
@ -29,8 +29,7 @@ async function validateLink(link: WebLink) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function syncWeblinks() {
|
export function syncWeblinks(weblinkStore: WeblinkStore) {
|
||||||
const weblinkStore = WeblinkStore.getInstance();
|
|
||||||
const webLinkEntities = observable.map<string, [WebLink, Disposer]>();
|
const webLinkEntities = observable.map<string, [WebLink, Disposer]>();
|
||||||
|
|
||||||
function periodicallyCheckLink(link: WebLink): Disposer {
|
function periodicallyCheckLink(link: WebLink): Disposer {
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import type { RequestPromiseOptions } from "request-promise-native";
|
import type { RequestPromiseOptions } from "request-promise-native";
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import { k8sRequest } from "../k8s-request";
|
import type { K8sRequest } from "../k8s-request.injectable";
|
||||||
|
|
||||||
export interface ClusterDetectionResult {
|
export interface ClusterDetectionResult {
|
||||||
value: string | number | boolean;
|
value: string | number | boolean;
|
||||||
@ -15,7 +15,7 @@ export interface ClusterDetectionResult {
|
|||||||
export class BaseClusterDetector {
|
export class BaseClusterDetector {
|
||||||
key: string;
|
key: string;
|
||||||
|
|
||||||
constructor(public cluster: Cluster) {
|
constructor(public cluster: Cluster, private _k8sRequest: K8sRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
detect(): Promise<ClusterDetectionResult> {
|
detect(): Promise<ClusterDetectionResult> {
|
||||||
@ -23,6 +23,6 @@ export class BaseClusterDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
protected async k8sRequest<T = any>(path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||||
return k8sRequest(this.cluster, path, options);
|
return this._k8sRequest(this.cluster, path, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 { VersionDetector } from "./version-detector";
|
||||||
|
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||||
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
|
|
||||||
|
const createVersionDetectorInjectable = getInjectable({
|
||||||
|
id: "create-version-detector",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const k8sRequest = di.inject(k8sRequestInjectable);
|
||||||
|
|
||||||
|
return (cluster: Cluster) =>
|
||||||
|
new VersionDetector(cluster, k8sRequest);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createVersionDetectorInjectable;
|
||||||
16
src/main/cluster-detectors/detector-registry.injectable.ts
Normal file
16
src/main/cluster-detectors/detector-registry.injectable.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 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 { DetectorRegistry } from "./detector-registry";
|
||||||
|
import k8sRequestInjectable from "../k8s-request.injectable";
|
||||||
|
|
||||||
|
const detectorRegistryInjectable = getInjectable({
|
||||||
|
id: "detector-registry",
|
||||||
|
|
||||||
|
instantiate: (di) =>
|
||||||
|
new DetectorRegistry({ k8sRequest: di.inject(k8sRequestInjectable) }),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default detectorRegistryInjectable;
|
||||||
@ -5,11 +5,17 @@
|
|||||||
|
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import type { ClusterMetadata } from "../../common/cluster-types";
|
import type { ClusterMetadata } from "../../common/cluster-types";
|
||||||
import { Singleton } from "../../common/utils";
|
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import type { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector";
|
import type { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector";
|
||||||
|
import type { K8sRequest } from "../k8s-request.injectable";
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
k8sRequest: K8sRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DetectorRegistry {
|
||||||
|
constructor(private dependencies: Dependencies) {}
|
||||||
|
|
||||||
export class DetectorRegistry extends Singleton {
|
|
||||||
registry = observable.array<typeof BaseClusterDetector>([], { deep: false });
|
registry = observable.array<typeof BaseClusterDetector>([], { deep: false });
|
||||||
|
|
||||||
add(detectorClass: typeof BaseClusterDetector): this {
|
add(detectorClass: typeof BaseClusterDetector): this {
|
||||||
@ -22,7 +28,7 @@ export class DetectorRegistry extends Singleton {
|
|||||||
const results: { [key: string]: ClusterDetectionResult } = {};
|
const results: { [key: string]: ClusterDetectionResult } = {};
|
||||||
|
|
||||||
for (const detectorClass of this.registry) {
|
for (const detectorClass of this.registry) {
|
||||||
const detector = new detectorClass(cluster);
|
const detector = new detectorClass(cluster, this.dependencies.k8sRequest);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await detector.detect();
|
const data = await detector.detect();
|
||||||
|
|||||||
23
src/main/cluster-manager.injectable.ts
Normal file
23
src/main/cluster-manager.injectable.ts
Normal 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 { ClusterManager } from "./cluster-manager";
|
||||||
|
import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable";
|
||||||
|
|
||||||
|
const clusterManagerInjectable = getInjectable({
|
||||||
|
id: "cluster-manager",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const clusterManager = new ClusterManager({
|
||||||
|
store: di.inject(clusterStoreInjectable),
|
||||||
|
});
|
||||||
|
|
||||||
|
clusterManager.init();
|
||||||
|
|
||||||
|
return clusterManager;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterManagerInjectable;
|
||||||
@ -9,42 +9,44 @@ import { action, makeObservable, observable, observe, reaction, toJS } from "mob
|
|||||||
import type { Cluster } from "../common/cluster/cluster";
|
import type { Cluster } from "../common/cluster/cluster";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { apiKubePrefix } from "../common/vars";
|
import { apiKubePrefix } from "../common/vars";
|
||||||
import { getClusterIdFromHost, Singleton } from "../common/utils";
|
import { getClusterIdFromHost } from "../common/utils";
|
||||||
import { catalogEntityRegistry } from "./catalog";
|
import { catalogEntityRegistry } from "./catalog";
|
||||||
import type { KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster";
|
import type { KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster";
|
||||||
import { KubernetesCluster, LensKubernetesClusterStatus } from "../common/catalog-entities/kubernetes-cluster";
|
import { KubernetesCluster, LensKubernetesClusterStatus } from "../common/catalog-entities/kubernetes-cluster";
|
||||||
import { ipcMainOn } from "../common/ipc";
|
import { ipcMainOn } from "../common/ipc";
|
||||||
import { once } from "lodash";
|
import { once } from "lodash";
|
||||||
import { ClusterStore } from "../common/cluster-store/cluster-store";
|
import type { ClusterStore } from "../common/cluster-store/cluster-store";
|
||||||
import type { ClusterId } from "../common/cluster-types";
|
import type { ClusterId } from "../common/cluster-types";
|
||||||
|
|
||||||
const logPrefix = "[CLUSTER-MANAGER]:";
|
const logPrefix = "[CLUSTER-MANAGER]:";
|
||||||
|
|
||||||
const lensSpecificClusterStatuses: Set<string> = new Set(Object.values(LensKubernetesClusterStatus));
|
const lensSpecificClusterStatuses: Set<string> = new Set(Object.values(LensKubernetesClusterStatus));
|
||||||
|
|
||||||
export class ClusterManager extends Singleton {
|
interface Dependencies {
|
||||||
private store = ClusterStore.getInstance();
|
store: ClusterStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ClusterManager {
|
||||||
deleting = observable.set<ClusterId>();
|
deleting = observable.set<ClusterId>();
|
||||||
|
|
||||||
@observable visibleCluster: ClusterId | undefined = undefined;
|
@observable visibleCluster: ClusterId | undefined = undefined;
|
||||||
|
|
||||||
constructor() {
|
constructor(private dependencies: Dependencies) {
|
||||||
super();
|
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
init = once(() => {
|
init = once(() => {
|
||||||
// reacting to every cluster's state change and total amount of items
|
// reacting to every cluster's state change and total amount of items
|
||||||
reaction(
|
reaction(
|
||||||
() => this.store.clustersList.map(c => c.getState()),
|
() => this.dependencies.store.clustersList.map(c => c.getState()),
|
||||||
() => this.updateCatalog(this.store.clustersList),
|
() => this.updateCatalog(this.dependencies.store.clustersList),
|
||||||
{ fireImmediately: false },
|
{ fireImmediately: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
// reacting to every cluster's preferences change and total amount of items
|
// reacting to every cluster's preferences change and total amount of items
|
||||||
reaction(
|
reaction(
|
||||||
() => this.store.clustersList.map(c => toJS(c.preferences)),
|
() => this.dependencies.store.clustersList.map(c => toJS(c.preferences)),
|
||||||
() => this.updateCatalog(this.store.clustersList),
|
() => this.updateCatalog(this.dependencies.store.clustersList),
|
||||||
{ fireImmediately: false },
|
{ fireImmediately: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -180,7 +182,7 @@ export class ClusterManager extends Singleton {
|
|||||||
@action
|
@action
|
||||||
protected syncClustersFromCatalog(entities: KubernetesCluster[]) {
|
protected syncClustersFromCatalog(entities: KubernetesCluster[]) {
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
const cluster = this.store.getById(entity.getId());
|
const cluster = this.dependencies.store.getById(entity.getId());
|
||||||
|
|
||||||
if (!cluster) {
|
if (!cluster) {
|
||||||
const model = {
|
const model = {
|
||||||
@ -195,7 +197,7 @@ export class ClusterManager extends Singleton {
|
|||||||
* Add the bare minimum of data to ClusterStore. And especially no
|
* Add the bare minimum of data to ClusterStore. And especially no
|
||||||
* preferences, as those might be configured by the entity's source
|
* preferences, as those might be configured by the entity's source
|
||||||
*/
|
*/
|
||||||
this.store.addCluster(model);
|
this.dependencies.store.addCluster(model);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) {
|
if (error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) {
|
||||||
logger.warn(`${logPrefix} kubeconfig file disappeared`, model);
|
logger.warn(`${logPrefix} kubeconfig file disappeared`, model);
|
||||||
@ -234,7 +236,7 @@ export class ClusterManager extends Singleton {
|
|||||||
|
|
||||||
protected onNetworkOffline = () => {
|
protected onNetworkOffline = () => {
|
||||||
logger.info(`${logPrefix} network is offline`);
|
logger.info(`${logPrefix} network is offline`);
|
||||||
this.store.clustersList.forEach((cluster) => {
|
this.dependencies.store.clustersList.forEach((cluster) => {
|
||||||
if (!cluster.disconnected) {
|
if (!cluster.disconnected) {
|
||||||
cluster.online = false;
|
cluster.online = false;
|
||||||
cluster.accessible = false;
|
cluster.accessible = false;
|
||||||
@ -245,7 +247,7 @@ export class ClusterManager extends Singleton {
|
|||||||
|
|
||||||
protected onNetworkOnline = () => {
|
protected onNetworkOnline = () => {
|
||||||
logger.info(`${logPrefix} network is online`);
|
logger.info(`${logPrefix} network is online`);
|
||||||
this.store.clustersList.forEach((cluster) => {
|
this.dependencies.store.clustersList.forEach((cluster) => {
|
||||||
if (!cluster.disconnected) {
|
if (!cluster.disconnected) {
|
||||||
cluster.refreshConnectionStatus().catch((e) => e);
|
cluster.refreshConnectionStatus().catch((e) => e);
|
||||||
}
|
}
|
||||||
@ -253,16 +255,16 @@ export class ClusterManager extends Singleton {
|
|||||||
};
|
};
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
this.store.clusters.forEach((cluster: Cluster) => {
|
this.dependencies.store.clusters.forEach((cluster: Cluster) => {
|
||||||
cluster.disconnect();
|
cluster.disconnect();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getClusterForRequest(req: http.IncomingMessage): Cluster {
|
getClusterForRequest = (req: http.IncomingMessage): Cluster => {
|
||||||
// lens-server is connecting to 127.0.0.1:<port>/<uid>
|
// lens-server is connecting to 127.0.0.1:<port>/<uid>
|
||||||
if (req.headers.host.startsWith("127.0.0.1")) {
|
if (req.headers.host.startsWith("127.0.0.1")) {
|
||||||
const clusterId = req.url.split("/")[1];
|
const clusterId = req.url.split("/")[1];
|
||||||
const cluster = this.store.getById(clusterId);
|
const cluster = this.dependencies.store.getById(clusterId);
|
||||||
|
|
||||||
if (cluster) {
|
if (cluster) {
|
||||||
// we need to swap path prefix so that request is proxied to kube api
|
// we need to swap path prefix so that request is proxied to kube api
|
||||||
@ -272,8 +274,8 @@ export class ClusterManager extends Singleton {
|
|||||||
return cluster;
|
return cluster;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.store.getById(getClusterIdFromHost(req.headers.host));
|
return this.dependencies.store.getById(getClusterIdFromHost(req.headers.host));
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function catalogEntityFromCluster(cluster: Cluster) {
|
export function catalogEntityFromCluster(cluster: Cluster) {
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { PrometheusProvider, PrometheusService } from "../prometheus/provider-registry";
|
import type { PrometheusProvider, PrometheusService, PrometheusProviderRegistry } from "../prometheus/provider-registry";
|
||||||
import { PrometheusProviderRegistry } from "../prometheus/provider-registry";
|
|
||||||
import type { ClusterPrometheusPreferences } from "../../common/cluster-types";
|
import type { ClusterPrometheusPreferences } from "../../common/cluster-types";
|
||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import type httpProxy from "http-proxy";
|
import type httpProxy from "http-proxy";
|
||||||
@ -30,6 +29,7 @@ interface PrometheusServicePreferences {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
createKubeAuthProxy: CreateKubeAuthProxy;
|
createKubeAuthProxy: CreateKubeAuthProxy;
|
||||||
authProxyCa: string;
|
authProxyCa: string;
|
||||||
|
prometheusProviderRegistry: PrometheusProviderRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ContextHandler {
|
export class ContextHandler {
|
||||||
@ -67,11 +67,11 @@ export class ContextHandler {
|
|||||||
this.prometheusProvider = service.id;
|
this.prometheusProvider = service.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return PrometheusProviderRegistry.getInstance().getByKind(this.prometheusProvider);
|
return this.dependencies.prometheusProviderRegistry.getByKind(this.prometheusProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected listPotentialProviders(): PrometheusProvider[] {
|
protected listPotentialProviders(): PrometheusProvider[] {
|
||||||
const registry = PrometheusProviderRegistry.getInstance();
|
const registry = this.dependencies.prometheusProviderRegistry;
|
||||||
const provider = this.prometheusProvider && registry.getByKind(this.prometheusProvider);
|
const provider = this.prometheusProvider && registry.getByKind(this.prometheusProvider);
|
||||||
|
|
||||||
if (provider) {
|
if (provider) {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { ContextHandler } from "./context-handler";
|
|||||||
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable";
|
||||||
import { getKubeAuthProxyCertificate } from "../kube-auth-proxy/get-kube-auth-proxy-certificate";
|
import { getKubeAuthProxyCertificate } from "../kube-auth-proxy/get-kube-auth-proxy-certificate";
|
||||||
import URLParse from "url-parse";
|
import URLParse from "url-parse";
|
||||||
|
import prometheusProviderRegistryInjectable from "../prometheus/prometheus-provider-registry.injectable";
|
||||||
|
|
||||||
const createContextHandlerInjectable = getInjectable({
|
const createContextHandlerInjectable = getInjectable({
|
||||||
id: "create-context-handler",
|
id: "create-context-handler",
|
||||||
@ -20,6 +21,7 @@ const createContextHandlerInjectable = getInjectable({
|
|||||||
const dependencies = {
|
const dependencies = {
|
||||||
createKubeAuthProxy: di.inject(createKubeAuthProxyInjectable),
|
createKubeAuthProxy: di.inject(createKubeAuthProxyInjectable),
|
||||||
authProxyCa: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate).cert,
|
authProxyCa: getKubeAuthProxyCertificate(clusterUrl.hostname, selfsigned.generate).cert,
|
||||||
|
prometheusProviderRegistry: di.inject(prometheusProviderRegistryInjectable),
|
||||||
};
|
};
|
||||||
|
|
||||||
return new ContextHandler(dependencies, cluster);
|
return new ContextHandler(dependencies, cluster);
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import createContextHandlerInjectable from "../context-handler/create-context-ha
|
|||||||
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
import { createClusterInjectionToken } from "../../common/cluster/create-cluster-injection-token";
|
||||||
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
import authorizationReviewInjectable from "../../common/cluster/authorization-review.injectable";
|
||||||
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
import listNamespacesInjectable from "../../common/cluster/list-namespaces.injectable";
|
||||||
|
import detectorRegistryInjectable from "../cluster-detectors/detector-registry.injectable";
|
||||||
|
import createVersionDetectorInjectable from "../cluster-detectors/create-version-detector.injectable";
|
||||||
|
|
||||||
const createClusterInjectable = getInjectable({
|
const createClusterInjectable = getInjectable({
|
||||||
id: "create-cluster",
|
id: "create-cluster",
|
||||||
@ -24,6 +26,8 @@ const createClusterInjectable = getInjectable({
|
|||||||
createContextHandler: di.inject(createContextHandlerInjectable),
|
createContextHandler: di.inject(createContextHandlerInjectable),
|
||||||
createAuthorizationReview: di.inject(authorizationReviewInjectable),
|
createAuthorizationReview: di.inject(authorizationReviewInjectable),
|
||||||
createListNamespaces: di.inject(listNamespacesInjectable),
|
createListNamespaces: di.inject(listNamespacesInjectable),
|
||||||
|
detectorRegistry: di.inject(detectorRegistryInjectable),
|
||||||
|
createVersionDetector: di.inject(createVersionDetectorInjectable),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (model) => new Cluster(dependencies, model);
|
return (model) => new Cluster(dependencies, model);
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import logger from "./logger";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Installs Electron developer tools in the development build.
|
|
||||||
* The dependency is not bundled to the production build.
|
|
||||||
*/
|
|
||||||
export const installDeveloperTools = () => {
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
logger.info("🤓 Installing developer tools");
|
|
||||||
import("electron-devtools-installer")
|
|
||||||
.then(({ default: devToolsInstaller, REACT_DEVELOPER_TOOLS }) => devToolsInstaller([REACT_DEVELOPER_TOOLS]))
|
|
||||||
.then((name) => logger.info(`[DEVTOOLS-INSTALLER]: installed ${name}`))
|
|
||||||
.catch(error => logger.error(`[DEVTOOLS-INSTALLER]: failed`, { error }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { app } from "electron";
|
|
||||||
import { WindowManager } from "./window-manager";
|
|
||||||
import { appEventBus } from "../common/app-event-bus/event-bus";
|
|
||||||
import { ClusterManager } from "./cluster-manager";
|
|
||||||
import logger from "./logger";
|
|
||||||
|
|
||||||
export function exitApp() {
|
|
||||||
const windowManager = WindowManager.getInstance(false);
|
|
||||||
const clusterManager = ClusterManager.getInstance(false);
|
|
||||||
|
|
||||||
appEventBus.emit({ name: "service", action: "close" });
|
|
||||||
windowManager?.hide();
|
|
||||||
clusterManager?.stop();
|
|
||||||
logger.info("SERVICE:QUIT");
|
|
||||||
setTimeout(() => {
|
|
||||||
app.exit();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
37
src/main/get-metrics.injectable.ts
Normal file
37
src/main/get-metrics.injectable.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 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 { Cluster } from "../common/cluster/cluster";
|
||||||
|
import type { IMetricsReqParams } from "../common/k8s-api/endpoints/metrics.api";
|
||||||
|
import k8sRequestInjectable from "./k8s-request.injectable";
|
||||||
|
|
||||||
|
export type GetMetrics = (cluster: Cluster, prometheusPath: string, queryParams: IMetricsReqParams & { query: string }) => Promise<any>;
|
||||||
|
|
||||||
|
const getMetricsInjectable = getInjectable({
|
||||||
|
id: "get-metrics",
|
||||||
|
|
||||||
|
instantiate: (di): GetMetrics => {
|
||||||
|
const k8sRequest = di.inject(k8sRequestInjectable);
|
||||||
|
|
||||||
|
return async (
|
||||||
|
cluster,
|
||||||
|
prometheusPath,
|
||||||
|
queryParams,
|
||||||
|
) => {
|
||||||
|
const prometheusPrefix = cluster.preferences.prometheus?.prefix || "";
|
||||||
|
const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`;
|
||||||
|
|
||||||
|
return k8sRequest(cluster, metricsPath, {
|
||||||
|
timeout: 0,
|
||||||
|
resolveWithFullResponse: false,
|
||||||
|
json: true,
|
||||||
|
method: "POST",
|
||||||
|
form: queryParams,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getMetricsInjectable;
|
||||||
18
src/main/helm/helm-repo-manager.injectable.ts
Normal file
18
src/main/helm/helm-repo-manager.injectable.ts
Normal 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 { HelmRepoManager } from "./helm-repo-manager";
|
||||||
|
|
||||||
|
const helmRepoManagerInjectable = getInjectable({
|
||||||
|
id: "helm-repo-manager",
|
||||||
|
|
||||||
|
instantiate: () => {
|
||||||
|
HelmRepoManager.resetInstance();
|
||||||
|
|
||||||
|
return HelmRepoManager.createInstance();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default helmRepoManagerInjectable;
|
||||||
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ClusterIdDetector } from "../cluster-detectors/cluster-id-detector";
|
|
||||||
import { DetectorRegistry } from "../cluster-detectors/detector-registry";
|
|
||||||
import { DistributionDetector } from "../cluster-detectors/distribution-detector";
|
|
||||||
import { LastSeenDetector } from "../cluster-detectors/last-seen-detector";
|
|
||||||
import { NodesCountDetector } from "../cluster-detectors/nodes-count-detector";
|
|
||||||
import { VersionDetector } from "../cluster-detectors/version-detector";
|
|
||||||
|
|
||||||
export function initClusterMetadataDetectors() {
|
|
||||||
DetectorRegistry.createInstance()
|
|
||||||
.add(ClusterIdDetector)
|
|
||||||
.add(LastSeenDetector)
|
|
||||||
.add(VersionDetector)
|
|
||||||
.add(DistributionDetector)
|
|
||||||
.add(NodesCountDetector);
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
export * from "./metrics-providers";
|
|
||||||
export * from "./cluster-metadata-detectors";
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { PrometheusProviderRegistry } from "../prometheus";
|
|
||||||
import { PrometheusHelm } from "../prometheus/helm";
|
|
||||||
import { PrometheusLens } from "../prometheus/lens";
|
|
||||||
import { PrometheusOperator } from "../prometheus/operator";
|
|
||||||
import { PrometheusStacklight } from "../prometheus/stacklight";
|
|
||||||
|
|
||||||
export function initPrometheusProviderRegistry() {
|
|
||||||
PrometheusProviderRegistry
|
|
||||||
.getInstance()
|
|
||||||
.registerProvider(new PrometheusLens())
|
|
||||||
.registerProvider(new PrometheusHelm())
|
|
||||||
.registerProvider(new PrometheusOperator())
|
|
||||||
.registerProvider(new PrometheusStacklight());
|
|
||||||
}
|
|
||||||
36
src/main/k8s-request.injectable.ts
Normal file
36
src/main/k8s-request.injectable.ts
Normal 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 request, { RequestPromiseOptions } from "request-promise-native";
|
||||||
|
import { apiKubePrefix } from "../common/vars";
|
||||||
|
import type { Cluster } from "../common/cluster/cluster";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable";
|
||||||
|
|
||||||
|
export type K8sRequest = (cluster: Cluster, path: string, options?: RequestPromiseOptions) => Promise<any>;
|
||||||
|
|
||||||
|
const k8SRequestInjectable = getInjectable({
|
||||||
|
id: "k8s-request",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const lensProxyPortNumberState = di.inject(lensProxyPortNumberStateInjectable);
|
||||||
|
|
||||||
|
return async (
|
||||||
|
cluster: Cluster,
|
||||||
|
path: string,
|
||||||
|
options: RequestPromiseOptions = {},
|
||||||
|
) => {
|
||||||
|
const kubeProxyUrl = `http://localhost:${lensProxyPortNumberState.get()}${apiKubePrefix}`;
|
||||||
|
|
||||||
|
options.headers ??= {};
|
||||||
|
options.json ??= true;
|
||||||
|
options.timeout ??= 30000;
|
||||||
|
options.headers.Host = `${cluster.id}.${new URL(kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest()
|
||||||
|
|
||||||
|
return request(kubeProxyUrl + path, options);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default k8SRequestInjectable;
|
||||||
@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { RequestPromiseOptions } from "request-promise-native";
|
|
||||||
import request from "request-promise-native";
|
|
||||||
import { apiKubePrefix } from "../common/vars";
|
|
||||||
import type { IMetricsReqParams } from "../common/k8s-api/endpoints/metrics.api";
|
|
||||||
import { LensProxy } from "./lens-proxy";
|
|
||||||
import type { Cluster } from "../common/cluster/cluster";
|
|
||||||
|
|
||||||
export async function k8sRequest<T = any>(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
|
||||||
const kubeProxyUrl = `http://localhost:${LensProxy.getInstance().port}${apiKubePrefix}`;
|
|
||||||
|
|
||||||
options.headers ??= {};
|
|
||||||
options.json ??= true;
|
|
||||||
options.timeout ??= 30000;
|
|
||||||
options.headers.Host = `${cluster.id}.${new URL(kubeProxyUrl).host}`; // required in ClusterManager.getClusterForRequest()
|
|
||||||
|
|
||||||
return request(kubeProxyUrl + path, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getMetrics(cluster: Cluster, prometheusPath: string, queryParams: IMetricsReqParams & { query: string }): Promise<any> {
|
|
||||||
const prometheusPrefix = cluster.preferences.prometheus?.prefix || "";
|
|
||||||
const metricsPath = `/api/v1/namespaces/${prometheusPath}/proxy${prometheusPrefix}/api/v1/query_range`;
|
|
||||||
|
|
||||||
return k8sRequest(cluster, metricsPath, {
|
|
||||||
timeout: 0,
|
|
||||||
resolveWithFullResponse: false,
|
|
||||||
json: true,
|
|
||||||
method: "POST",
|
|
||||||
form: queryParams,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import type { Cluster } from "../../common/cluster/cluster";
|
import type { Cluster } from "../../common/cluster/cluster";
|
||||||
import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
|
import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable";
|
||||||
import { KubeconfigManager } from "./kubeconfig-manager";
|
import { KubeconfigManager } from "./kubeconfig-manager";
|
||||||
|
import lensProxyPortNumberStateInjectable from "../lens-proxy-port-number-state.injectable";
|
||||||
|
|
||||||
export interface KubeConfigManagerInstantiationParameter {
|
export interface KubeConfigManagerInstantiationParameter {
|
||||||
cluster: Cluster;
|
cluster: Cluster;
|
||||||
@ -17,6 +18,7 @@ const createKubeconfigManagerInjectable = getInjectable({
|
|||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const dependencies = {
|
const dependencies = {
|
||||||
directoryForTemp: di.inject(directoryForTempInjectable),
|
directoryForTemp: di.inject(directoryForTempInjectable),
|
||||||
|
lensProxyPortNumberState: di.inject(lensProxyPortNumberStateInjectable),
|
||||||
};
|
};
|
||||||
|
|
||||||
return (cluster: Cluster) => new KubeconfigManager(dependencies, cluster);
|
return (cluster: Cluster) => new KubeconfigManager(dependencies, cluster);
|
||||||
|
|||||||
@ -10,10 +10,10 @@ import path from "path";
|
|||||||
import fs from "fs-extra";
|
import fs from "fs-extra";
|
||||||
import { dumpConfigYaml } from "../../common/kube-helpers";
|
import { dumpConfigYaml } from "../../common/kube-helpers";
|
||||||
import logger from "../logger";
|
import logger from "../logger";
|
||||||
import { LensProxy } from "../lens-proxy";
|
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
directoryForTemp: string;
|
directoryForTemp: string;
|
||||||
|
lensProxyPortNumberState: { get: () => number };
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KubeconfigManager {
|
export class KubeconfigManager {
|
||||||
@ -79,7 +79,7 @@ export class KubeconfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get resolveProxyUrl() {
|
get resolveProxyUrl() {
|
||||||
return `http://127.0.0.1:${LensProxy.getInstance().port}/${this.cluster.id}`;
|
return `http://127.0.0.1:${this.dependencies.lensProxyPortNumberState.get()}/${this.cluster.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
36
src/main/lens-proxy-port-number-state.injectable.ts
Normal file
36
src/main/lens-proxy-port-number-state.injectable.ts
Normal 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";
|
||||||
|
|
||||||
|
const lensProxyPortNumberStateInjectable = getInjectable({
|
||||||
|
id: "lens-proxy-port-number-state",
|
||||||
|
|
||||||
|
instantiate: () => {
|
||||||
|
let _portNumber: number;
|
||||||
|
|
||||||
|
return {
|
||||||
|
get: () => {
|
||||||
|
if (!_portNumber) {
|
||||||
|
throw new Error(
|
||||||
|
"Tried to access port number of LensProxy while it has not been set yet.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _portNumber;
|
||||||
|
},
|
||||||
|
set: (portNumber: number) => {
|
||||||
|
if (_portNumber) {
|
||||||
|
throw new Error(
|
||||||
|
"Tried to set port number for LensProxy when it has already been set.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_portNumber = portNumber;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default lensProxyPortNumberStateInjectable;
|
||||||
14
src/main/lens-proxy-port-number.injectable.ts
Normal file
14
src/main/lens-proxy-port-number.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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 lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable";
|
||||||
|
|
||||||
|
const lensProxyPortNumberInjectable = getInjectable({
|
||||||
|
id: "lens-proxy-port-number",
|
||||||
|
|
||||||
|
instantiate: (di) => di.inject(lensProxyPortNumberStateInjectable).get(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default lensProxyPortNumberInjectable;
|
||||||
35
src/main/lens-proxy.injectable.ts
Normal file
35
src/main/lens-proxy.injectable.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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 { LensProxy } from "./lens-proxy";
|
||||||
|
import { kubeApiUpgradeRequest } from "./proxy-functions";
|
||||||
|
import routerInjectable from "./router/router.injectable";
|
||||||
|
import httpProxy from "http-proxy";
|
||||||
|
import clusterManagerInjectable from "./cluster-manager.injectable";
|
||||||
|
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||||
|
import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable";
|
||||||
|
|
||||||
|
const lensProxyInjectable = getInjectable({
|
||||||
|
id: "lens-proxy",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const clusterManager = di.inject(clusterManagerInjectable);
|
||||||
|
const router = di.inject(routerInjectable);
|
||||||
|
const shellApiRequest = di.inject(shellApiRequestInjectable);
|
||||||
|
const proxy = httpProxy.createProxy();
|
||||||
|
const lensProxyPortNumberState = di.inject(lensProxyPortNumberStateInjectable);
|
||||||
|
|
||||||
|
return new LensProxy({
|
||||||
|
router,
|
||||||
|
proxy,
|
||||||
|
kubeApiUpgradeRequest,
|
||||||
|
shellApiRequest,
|
||||||
|
getClusterForRequest: clusterManager.getClusterForRequest,
|
||||||
|
lensProxyPortNumberState,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default lensProxyInjectable;
|
||||||
@ -11,7 +11,6 @@ import { apiPrefix, apiKubePrefix } from "../common/vars";
|
|||||||
import type { Router } from "./router/router";
|
import type { Router } from "./router/router";
|
||||||
import type { ContextHandler } from "./context-handler/context-handler";
|
import type { ContextHandler } from "./context-handler/context-handler";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { Singleton } from "../common/utils";
|
|
||||||
import type { Cluster } from "../common/cluster/cluster";
|
import type { Cluster } from "../common/cluster/cluster";
|
||||||
import type { ProxyApiRequestArgs } from "./proxy-functions";
|
import type { ProxyApiRequestArgs } from "./proxy-functions";
|
||||||
import { appEventBus } from "../common/app-event-bus/event-bus";
|
import { appEventBus } from "../common/app-event-bus/event-bus";
|
||||||
@ -19,10 +18,13 @@ import { getBoolean } from "./utils/parse-query";
|
|||||||
|
|
||||||
type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | null;
|
type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | null;
|
||||||
|
|
||||||
export interface LensProxyFunctions {
|
interface Dependencies {
|
||||||
getClusterForRequest: GetClusterForRequest;
|
getClusterForRequest: GetClusterForRequest;
|
||||||
shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
||||||
kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
||||||
|
router: Router;
|
||||||
|
proxy: httpProxy;
|
||||||
|
lensProxyPortNumberState: { set: (portNumber: number) => void };
|
||||||
}
|
}
|
||||||
|
|
||||||
const watchParam = "watch";
|
const watchParam = "watch";
|
||||||
@ -52,21 +54,14 @@ const disallowedPorts = new Set([
|
|||||||
10080,
|
10080,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export class LensProxy extends Singleton {
|
export class LensProxy {
|
||||||
protected origin: string;
|
protected origin: string;
|
||||||
protected proxyServer: http.Server;
|
protected proxyServer: http.Server;
|
||||||
protected closed = false;
|
protected closed = false;
|
||||||
protected retryCounters = new Map<string, number>();
|
protected retryCounters = new Map<string, number>();
|
||||||
protected getClusterForRequest: GetClusterForRequest;
|
|
||||||
|
|
||||||
public port: number;
|
constructor(private dependencies: Dependencies) {
|
||||||
|
this.configureProxy(dependencies.proxy);
|
||||||
constructor(protected router: Router, protected proxy: httpProxy, { shellApiRequest, kubeApiUpgradeRequest, getClusterForRequest }: LensProxyFunctions) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.configureProxy(proxy);
|
|
||||||
|
|
||||||
this.getClusterForRequest = getClusterForRequest;
|
|
||||||
|
|
||||||
this.proxyServer = spdy.createServer({
|
this.proxyServer = spdy.createServer({
|
||||||
spdy: {
|
spdy: {
|
||||||
@ -79,14 +74,14 @@ export class LensProxy extends Singleton {
|
|||||||
|
|
||||||
this.proxyServer
|
this.proxyServer
|
||||||
.on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
|
.on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
|
||||||
const cluster = getClusterForRequest(req);
|
const cluster = dependencies.getClusterForRequest(req);
|
||||||
|
|
||||||
if (!cluster) {
|
if (!cluster) {
|
||||||
logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`);
|
logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`);
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
} else {
|
} else {
|
||||||
const isInternal = req.url.startsWith(`${apiPrefix}?`);
|
const isInternal = req.url.startsWith(`${apiPrefix}?`);
|
||||||
const reqHandler = isInternal ? shellApiRequest : kubeApiUpgradeRequest;
|
const reqHandler = isInternal ? dependencies.shellApiRequest : dependencies.kubeApiUpgradeRequest;
|
||||||
|
|
||||||
(async () => reqHandler({ req, socket, head, cluster }))()
|
(async () => reqHandler({ req, socket, head, cluster }))()
|
||||||
.catch(error => logger.error("[LENS-PROXY]: failed to handle proxy upgrade", error));
|
.catch(error => logger.error("[LENS-PROXY]: failed to handle proxy upgrade", error));
|
||||||
@ -110,13 +105,14 @@ export class LensProxy extends Singleton {
|
|||||||
|
|
||||||
const { address, port } = this.proxyServer.address() as net.AddressInfo;
|
const { address, port } = this.proxyServer.address() as net.AddressInfo;
|
||||||
|
|
||||||
|
this.dependencies.lensProxyPortNumberState.set(port);
|
||||||
|
|
||||||
logger.info(`[LENS-PROXY]: Proxy server has started at ${address}:${port}`);
|
logger.info(`[LENS-PROXY]: Proxy server has started at ${address}:${port}`);
|
||||||
|
|
||||||
this.proxyServer.on("error", (error) => {
|
this.proxyServer.on("error", (error) => {
|
||||||
logger.info(`[LENS-PROXY]: Subsequent error: ${error}`);
|
logger.info(`[LENS-PROXY]: Subsequent error: ${error}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.port = port;
|
|
||||||
appEventBus.emit({ name: "lens-proxy", action: "listen", params: { port }});
|
appEventBus.emit({ name: "lens-proxy", action: "listen", params: { port }});
|
||||||
resolve(port);
|
resolve(port);
|
||||||
})
|
})
|
||||||
@ -228,15 +224,15 @@ export class LensProxy extends Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
|
protected async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
|
||||||
const cluster = this.getClusterForRequest(req);
|
const cluster = this.dependencies.getClusterForRequest(req);
|
||||||
|
|
||||||
if (cluster) {
|
if (cluster) {
|
||||||
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler);
|
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler);
|
||||||
|
|
||||||
if (proxyTarget) {
|
if (proxyTarget) {
|
||||||
return this.proxy.web(req, res, proxyTarget);
|
return this.dependencies.proxy.web(req, res, proxyTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.router.route(cluster, req, res);
|
this.dependencies.router.route(cluster, req, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* 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 { PrometheusProviderRegistry } from "./provider-registry";
|
||||||
|
|
||||||
|
const prometheusProviderRegistryInjectable = getInjectable({
|
||||||
|
id: "prometheus-provider-registry",
|
||||||
|
instantiate: () => new PrometheusProviderRegistry(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default prometheusProviderRegistryInjectable;
|
||||||
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||||
import { inspect } from "util";
|
import { inspect } from "util";
|
||||||
import { Singleton } from "../../common/utils";
|
|
||||||
|
|
||||||
export interface PrometheusService {
|
export interface PrometheusService {
|
||||||
id: string;
|
id: string;
|
||||||
@ -65,7 +64,7 @@ export abstract class PrometheusProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PrometheusProviderRegistry extends Singleton {
|
export class PrometheusProviderRegistry {
|
||||||
public providers = new Map<string, PrometheusProvider>();
|
public providers = new Map<string, PrometheusProvider>();
|
||||||
|
|
||||||
getByKind(kind: string): PrometheusProvider {
|
getByKind(kind: string): PrometheusProvider {
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable";
|
import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable";
|
||||||
import { LensProtocolRouterMain } from "./lens-protocol-router-main";
|
import { LensProtocolRouterMain } from "./lens-protocol-router-main";
|
||||||
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
|
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
|
||||||
|
import windowManagerInjectable from "../../window-manager.injectable";
|
||||||
|
|
||||||
const lensProtocolRouterMainInjectable = getInjectable({
|
const lensProtocolRouterMainInjectable = getInjectable({
|
||||||
id: "lens-protocol-router-main",
|
id: "lens-protocol-router-main",
|
||||||
@ -14,6 +15,7 @@ const lensProtocolRouterMainInjectable = getInjectable({
|
|||||||
new LensProtocolRouterMain({
|
new LensProtocolRouterMain({
|
||||||
extensionLoader: di.inject(extensionLoaderInjectable),
|
extensionLoader: di.inject(extensionLoaderInjectable),
|
||||||
extensionsStore: di.inject(extensionsStoreInjectable),
|
extensionsStore: di.inject(extensionsStoreInjectable),
|
||||||
|
windowManager: di.inject(windowManagerInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { observable, when, makeObservable } from "mobx";
|
|||||||
import type { RouteAttempt } from "../../../common/protocol-handler";
|
import type { RouteAttempt } from "../../../common/protocol-handler";
|
||||||
import { ProtocolHandlerInvalid } from "../../../common/protocol-handler";
|
import { ProtocolHandlerInvalid } from "../../../common/protocol-handler";
|
||||||
import { disposer, noop } from "../../../common/utils";
|
import { disposer, noop } from "../../../common/utils";
|
||||||
import { WindowManager } from "../../window-manager";
|
import type { WindowManager } from "../../window-manager";
|
||||||
import type { ExtensionLoader } from "../../../extensions/extension-loader";
|
import type { ExtensionLoader } from "../../../extensions/extension-loader";
|
||||||
import type { ExtensionsStore } from "../../../extensions/extensions-store/extensions-store";
|
import type { ExtensionsStore } from "../../../extensions/extensions-store/extensions-store";
|
||||||
|
|
||||||
@ -40,6 +40,7 @@ function checkHost<Query>(url: URLParse<Query>): boolean {
|
|||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
extensionLoader: ExtensionLoader;
|
extensionLoader: ExtensionLoader;
|
||||||
extensionsStore: ExtensionsStore;
|
extensionsStore: ExtensionsStore;
|
||||||
|
windowManager: WindowManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||||
@ -73,7 +74,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
|||||||
throw new proto.RoutingError(proto.RoutingErrorType.INVALID_PROTOCOL, url);
|
throw new proto.RoutingError(proto.RoutingErrorType.INVALID_PROTOCOL, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowManager.getInstance(false)?.ensureMainWindow().catch(noop);
|
this.dependencies.windowManager.ensureMainWindow().catch(noop);
|
||||||
const routeInternally = checkHost(url);
|
const routeInternally = checkHost(url);
|
||||||
|
|
||||||
logger.info(`${proto.LensProtocolRouter.LoggingPrefix}: routing ${url.toString()}`);
|
logger.info(`${proto.LensProtocolRouter.LoggingPrefix}: routing ${url.toString()}`);
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
|
|
||||||
const switchValue = app.commandLine.getSwitchValue("proxy-server");
|
|
||||||
|
|
||||||
export function mangleProxyEnv() {
|
export function mangleProxyEnv() {
|
||||||
|
const switchValue = app.commandLine.getSwitchValue("proxy-server");
|
||||||
|
|
||||||
let httpsProxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || "";
|
let httpsProxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY || "";
|
||||||
|
|
||||||
delete process.env.HTTPS_PROXY;
|
delete process.env.HTTPS_PROXY;
|
||||||
@ -20,4 +20,8 @@ export function mangleProxyEnv() {
|
|||||||
if (httpsProxy !== "") {
|
if (httpsProxy !== "") {
|
||||||
process.env.APP_HTTPS_PROXY = httpsProxy;
|
process.env.APP_HTTPS_PROXY = httpsProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
|
||||||
|
process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shellApiRequest } from "./shell-api-request";
|
import { shellApiRequest } from "./shell-api-request";
|
||||||
import createShellSessionInjectable from "../../shell-session/create-shell-session.injectable";
|
import createShellSessionInjectable from "../../shell-session/create-shell-session.injectable";
|
||||||
import shellRequestAuthenticatorInjectable
|
import shellRequestAuthenticatorInjectable from "./shell-request-authenticator/shell-request-authenticator.injectable";
|
||||||
from "./shell-request-authenticator/shell-request-authenticator.injectable";
|
import clusterManagerInjectable from "../../cluster-manager.injectable";
|
||||||
|
|
||||||
const shellApiRequestInjectable = getInjectable({
|
const shellApiRequestInjectable = getInjectable({
|
||||||
id: "shell-api-request",
|
id: "shell-api-request",
|
||||||
@ -14,6 +14,7 @@ const shellApiRequestInjectable = getInjectable({
|
|||||||
instantiate: (di) => shellApiRequest({
|
instantiate: (di) => shellApiRequest({
|
||||||
createShellSession: di.inject(createShellSessionInjectable),
|
createShellSession: di.inject(createShellSessionInjectable),
|
||||||
authenticateRequest: di.inject(shellRequestAuthenticatorInjectable).authenticate,
|
authenticateRequest: di.inject(shellRequestAuthenticatorInjectable).authenticate,
|
||||||
|
clusterManager: di.inject(clusterManagerInjectable),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import logger from "../../logger";
|
|||||||
import type WebSocket from "ws";
|
import type WebSocket from "ws";
|
||||||
import { Server as WebSocketServer } from "ws";
|
import { Server as WebSocketServer } from "ws";
|
||||||
import type { ProxyApiRequestArgs } from "../types";
|
import type { ProxyApiRequestArgs } from "../types";
|
||||||
import { ClusterManager } from "../../cluster-manager";
|
import type { ClusterManager } from "../../cluster-manager";
|
||||||
import URLParse from "url-parse";
|
import URLParse from "url-parse";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import type { ClusterId } from "../../../common/cluster-types";
|
import type { ClusterId } from "../../../common/cluster-types";
|
||||||
@ -21,10 +21,12 @@ interface Dependencies {
|
|||||||
tabId: string;
|
tabId: string;
|
||||||
nodeName?: string;
|
nodeName?: string;
|
||||||
}) => { open: () => Promise<void> };
|
}) => { open: () => Promise<void> };
|
||||||
|
|
||||||
|
clusterManager: ClusterManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const shellApiRequest = ({ createShellSession, authenticateRequest }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => {
|
export const shellApiRequest = ({ createShellSession, authenticateRequest, clusterManager }: Dependencies) => ({ req, socket, head }: ProxyApiRequestArgs): void => {
|
||||||
const cluster = ClusterManager.getInstance().getClusterForRequest(req);
|
const cluster = clusterManager.getClusterForRequest(req);
|
||||||
const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true);
|
const { query: { node: nodeName, shellToken, id: tabId }} = new URLParse(req.url, true);
|
||||||
|
|
||||||
if (!cluster || !authenticateRequest(cluster.id, tabId, shellToken)) {
|
if (!cluster || !authenticateRequest(cluster.id, tabId, shellToken)) {
|
||||||
|
|||||||
@ -11,13 +11,14 @@ import type { ClusterPrometheusMetadata } from "../../../common/cluster-types";
|
|||||||
import { ClusterMetadataKey } from "../../../common/cluster-types";
|
import { ClusterMetadataKey } from "../../../common/cluster-types";
|
||||||
import logger from "../../logger";
|
import logger from "../../logger";
|
||||||
import type { Cluster } from "../../../common/cluster/cluster";
|
import type { Cluster } from "../../../common/cluster/cluster";
|
||||||
import { getMetrics } from "../../k8s-request";
|
|
||||||
import type { IMetricsQuery } from "./metrics-query";
|
import type { IMetricsQuery } from "./metrics-query";
|
||||||
|
import type { GetMetrics } from "../../get-metrics.injectable";
|
||||||
|
import getMetricsInjectable from "../../get-metrics.injectable";
|
||||||
|
|
||||||
// This is used for backoff retry tracking.
|
// This is used for backoff retry tracking.
|
||||||
const ATTEMPTS = [false, false, false, false, true];
|
const ATTEMPTS = [false, false, false, false, true];
|
||||||
|
|
||||||
async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record<string, string>): Promise<any[]> {
|
const loadMetricsFor = (getMetrics: GetMetrics) => async (promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record<string, string>): Promise<any[]> => {
|
||||||
const queries = promQueries.map(p => p.trim());
|
const queries = promQueries.map(p => p.trim());
|
||||||
const loaders = new Map<string, Promise<any>>();
|
const loaders = new Map<string, Promise<any>>();
|
||||||
|
|
||||||
@ -41,9 +42,11 @@ async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(queries.map(loadMetric));
|
return Promise.all(queries.map(loadMetric));
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const addMetricsRoute = (getMetrics: GetMetrics) => async ({ cluster, payload, query }: LensApiRequest) => {
|
||||||
|
const loadMetrics = loadMetricsFor(getMetrics);
|
||||||
|
|
||||||
const addMetricsRoute = async ({ cluster, payload, query }: LensApiRequest) => {
|
|
||||||
const queryParams: IMetricsQuery = Object.fromEntries(query.entries());
|
const queryParams: IMetricsQuery = Object.fromEntries(query.entries());
|
||||||
const prometheusMetadata: ClusterPrometheusMetadata = {};
|
const prometheusMetadata: ClusterPrometheusMetadata = {};
|
||||||
|
|
||||||
@ -97,11 +100,15 @@ const addMetricsRoute = async ({ cluster, payload, query }: LensApiRequest) => {
|
|||||||
const addMetricsRouteInjectable = getInjectable({
|
const addMetricsRouteInjectable = getInjectable({
|
||||||
id: "add-metrics-route",
|
id: "add-metrics-route",
|
||||||
|
|
||||||
instantiate: (): Route<any> => ({
|
instantiate: (di): Route<any> => {
|
||||||
method: "post",
|
const getMetrics = di.inject(getMetricsInjectable);
|
||||||
path: `${apiPrefix}/metrics`,
|
|
||||||
handler: addMetricsRoute,
|
return {
|
||||||
}),
|
method: "post",
|
||||||
|
path: `${apiPrefix}/metrics`,
|
||||||
|
handler: addMetricsRoute(getMetrics),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
injectionToken: routeInjectionToken,
|
injectionToken: routeInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,26 +7,34 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { apiPrefix } from "../../../common/vars";
|
import { apiPrefix } from "../../../common/vars";
|
||||||
import type { Route } from "../../router/router";
|
import type { Route } from "../../router/router";
|
||||||
import { routeInjectionToken } from "../../router/router.injectable";
|
import { routeInjectionToken } from "../../router/router.injectable";
|
||||||
import { PrometheusProviderRegistry } from "../../prometheus";
|
|
||||||
import type { MetricProviderInfo } from "../../../common/k8s-api/endpoints/metrics.api";
|
import type { MetricProviderInfo } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||||
|
import prometheusProviderRegistryInjectable from "../../prometheus/prometheus-provider-registry.injectable";
|
||||||
|
|
||||||
const getMetricProvidersRouteInjectable = getInjectable({
|
const getMetricProvidersRouteInjectable = getInjectable({
|
||||||
id: "get-metric-providers-route",
|
id: "get-metric-providers-route",
|
||||||
|
|
||||||
instantiate: (): Route<MetricProviderInfo[]> => ({
|
instantiate: (di): Route<MetricProviderInfo[]> => {
|
||||||
method: "get",
|
const prometheusProviderRegistry = di.inject(prometheusProviderRegistryInjectable);
|
||||||
path: `${apiPrefix}/metrics/providers`,
|
|
||||||
|
|
||||||
handler: () => {
|
return {
|
||||||
const providers: MetricProviderInfo[] = [];
|
method: "get",
|
||||||
|
path: `${apiPrefix}/metrics/providers`,
|
||||||
|
|
||||||
for (const { name, id, isConfigurable } of PrometheusProviderRegistry.getInstance().providers.values()) {
|
handler: () => {
|
||||||
providers.push({ name, id, isConfigurable });
|
const providers: MetricProviderInfo[] = [];
|
||||||
}
|
|
||||||
|
|
||||||
return { response: providers };
|
for (const {
|
||||||
},
|
name,
|
||||||
}),
|
id,
|
||||||
|
isConfigurable,
|
||||||
|
} of prometheusProviderRegistry.providers.values()) {
|
||||||
|
providers.push({ name, id, isConfigurable });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { response: providers };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
injectionToken: routeInjectionToken,
|
injectionToken: routeInjectionToken,
|
||||||
});
|
});
|
||||||
|
|||||||
32
src/main/stop-services-and-exit-app.injectable.ts
Normal file
32
src/main/stop-services-and-exit-app.injectable.ts
Normal 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 exitAppInjectable from "./app-paths/get-electron-app-path/electron-app/exit-app.injectable";
|
||||||
|
import clusterManagerInjectable from "./cluster-manager.injectable";
|
||||||
|
import windowManagerInjectable from "./window-manager.injectable";
|
||||||
|
import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable";
|
||||||
|
import loggerInjectable from "../common/logger.injectable";
|
||||||
|
|
||||||
|
const stopServicesAndExitAppInjectable = getInjectable({
|
||||||
|
id: "stop-services-and-exit-app",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const exitApp = di.inject(exitAppInjectable);
|
||||||
|
const windowManager = di.inject(windowManagerInjectable);
|
||||||
|
const clusterManager = di.inject(clusterManagerInjectable);
|
||||||
|
const appEventBus = di.inject(appEventBusInjectable);
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
appEventBus.emit({ name: "service", action: "close" });
|
||||||
|
windowManager.hide();
|
||||||
|
clusterManager.stop();
|
||||||
|
logger.info("SERVICE:QUIT");
|
||||||
|
setTimeout(exitApp, 1000);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default stopServicesAndExitAppInjectable;
|
||||||
@ -4,15 +4,15 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { WindowManager } from "./window-manager";
|
import { WindowManager } from "./window-manager";
|
||||||
|
import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable";
|
||||||
|
|
||||||
const windowManagerInjectable = getInjectable({
|
const windowManagerInjectable = getInjectable({
|
||||||
id: "window-manager",
|
id: "window-manager",
|
||||||
|
|
||||||
instantiate: () => {
|
instantiate: (di) =>
|
||||||
WindowManager.resetInstance();
|
new WindowManager({
|
||||||
|
lensProxyPortNumberState: di.inject(lensProxyPortNumberStateInjectable),
|
||||||
return WindowManager.createInstance();
|
}),
|
||||||
},
|
|
||||||
|
|
||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,13 +9,12 @@ import { app, BrowserWindow, dialog, ipcMain, webContents } from "electron";
|
|||||||
import windowStateKeeper from "electron-window-state";
|
import windowStateKeeper from "electron-window-state";
|
||||||
import { appEventBus } from "../common/app-event-bus/event-bus";
|
import { appEventBus } from "../common/app-event-bus/event-bus";
|
||||||
import { ipcMainOn } from "../common/ipc";
|
import { ipcMainOn } from "../common/ipc";
|
||||||
import { delay, iter, Singleton, openBrowser } from "../common/utils";
|
import { delay, iter, openBrowser } from "../common/utils";
|
||||||
import type { ClusterFrameInfo } from "../common/cluster-frames";
|
import type { ClusterFrameInfo } from "../common/cluster-frames";
|
||||||
import { clusterFrameMap } from "../common/cluster-frames";
|
import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames";
|
||||||
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { isMac, productName } from "../common/vars";
|
import { isMac, productName } from "../common/vars";
|
||||||
import { LensProxy } from "./lens-proxy";
|
|
||||||
import { bundledExtensionsLoaded } from "../common/ipc/extension-handling";
|
import { bundledExtensionsLoaded } from "../common/ipc/extension-handling";
|
||||||
|
|
||||||
function isHideable(window: BrowserWindow | null): boolean {
|
function isHideable(window: BrowserWindow | null): boolean {
|
||||||
@ -28,9 +27,11 @@ export interface SendToViewArgs {
|
|||||||
data?: any[];
|
data?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WindowManager extends Singleton {
|
interface Dependencies {
|
||||||
public mainContentUrl = `http://localhost:${LensProxy.getInstance().port}`;
|
lensProxyPortNumberState: { get: () => number };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WindowManager {
|
||||||
protected mainWindow: BrowserWindow;
|
protected mainWindow: BrowserWindow;
|
||||||
protected splashWindow: BrowserWindow;
|
protected splashWindow: BrowserWindow;
|
||||||
protected windowState: windowStateKeeper.State;
|
protected windowState: windowStateKeeper.State;
|
||||||
@ -38,12 +39,15 @@ export class WindowManager extends Singleton {
|
|||||||
|
|
||||||
@observable activeClusterId: ClusterId;
|
@observable activeClusterId: ClusterId;
|
||||||
|
|
||||||
constructor() {
|
constructor(private dependencies: Dependencies) {
|
||||||
super();
|
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get mainContentUrl() {
|
||||||
|
return `http://localhost:${this.dependencies.lensProxyPortNumberState.get()}`;
|
||||||
|
}
|
||||||
|
|
||||||
private async initMainWindow(showSplash: boolean) {
|
private async initMainWindow(showSplash: boolean) {
|
||||||
// Manage main window size and position with state persistence
|
// Manage main window size and position with state persistence
|
||||||
if (!this.windowState) {
|
if (!this.windowState) {
|
||||||
|
|||||||
@ -14,11 +14,15 @@ const createClusterInjectable = getInjectable({
|
|||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const dependencies: ClusterDependencies = {
|
const dependencies: ClusterDependencies = {
|
||||||
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
|
||||||
|
|
||||||
|
// TODO: Dismantle wrong abstraction
|
||||||
createKubeconfigManager: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
createKubeconfigManager: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");},
|
createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");},
|
||||||
createContextHandler: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
createContextHandler: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
|
detectorRegistry: null,
|
||||||
|
createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); },
|
||||||
};
|
};
|
||||||
|
|
||||||
return (model) => new Cluster(dependencies, model);
|
return (model) => new Cluster(dependencies, model);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user