mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into typesafe-catalog-category-events
This commit is contained in:
commit
048394222b
@ -50,6 +50,7 @@ module.exports = {
|
||||
plugins: [
|
||||
"header",
|
||||
"unused-imports",
|
||||
"react-hooks"
|
||||
],
|
||||
rules: {
|
||||
"header/header": [2, "./license-header"],
|
||||
@ -210,7 +211,9 @@ module.exports = {
|
||||
{ "blankLine": "always", "prev": "*", "next": "class" },
|
||||
{ "blankLine": "always", "prev": ["const", "let", "var"], "next": "*" },
|
||||
{ "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"]},
|
||||
]
|
||||
],
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "off"
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
module.exports = {
|
||||
export default {
|
||||
Trans: ({ children }: { children: React.ReactNode }) => children,
|
||||
t: (message: string) => message
|
||||
};
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
module.exports = {
|
||||
export default {
|
||||
require: jest.fn(),
|
||||
match: jest.fn(),
|
||||
app: {
|
||||
|
||||
@ -18,4 +18,5 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
module.exports = {};
|
||||
|
||||
export default {};
|
||||
|
||||
@ -18,4 +18,4 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
module.exports = {};
|
||||
export default {};
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube
|
||||
cluster and vice versa.
|
||||
*/
|
||||
import { Application } from "spectron";
|
||||
import type { Application } from "spectron";
|
||||
import * as utils from "../helpers/utils";
|
||||
import { listHelmRepositories } from "../helpers/utils";
|
||||
import { fail } from "assert";
|
||||
|
||||
@ -18,7 +18,14 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { Application } from "spectron";
|
||||
|
||||
/*
|
||||
Cluster tests are run if there is a pre-existing minikube cluster. Before running cluster tests the TEST_NAMESPACE
|
||||
namespace is removed, if it exists, from the minikube cluster. Resources are created as part of the cluster tests in the
|
||||
TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube
|
||||
cluster and vice versa.
|
||||
*/
|
||||
import type { Application } from "spectron";
|
||||
import * as utils from "../helpers/utils";
|
||||
import { minikubeReady, waitForMinikubeDashboard } from "../helpers/minikube";
|
||||
import { exec } from "child_process";
|
||||
|
||||
@ -18,7 +18,8 @@
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { Application } from "spectron";
|
||||
|
||||
import type { Application } from "spectron";
|
||||
import * as utils from "../helpers/utils";
|
||||
|
||||
jest.setTimeout(60000);
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { spawnSync } from "child_process";
|
||||
import { Application } from "spectron";
|
||||
import type { Application } from "spectron";
|
||||
|
||||
export function minikubeReady(testNamespace: string): boolean {
|
||||
// determine if minikube is running
|
||||
|
||||
@ -62,11 +62,11 @@ theme:
|
||||
- scheme: slate
|
||||
toggle:
|
||||
icon: material/toggle-switch-off-outline
|
||||
name: Switch to light mode
|
||||
name: Switch to dark mode
|
||||
- scheme: default
|
||||
toggle:
|
||||
icon: material/toggle-switch
|
||||
name: Switch to dark mode
|
||||
name: Switch to light mode
|
||||
features:
|
||||
- toc.autohide
|
||||
- search.suggest
|
||||
|
||||
@ -316,6 +316,7 @@
|
||||
"eslint": "^7.7.0",
|
||||
"eslint-plugin-header": "^3.1.1",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-unused-imports": "^1.0.1",
|
||||
"file-loader": "^6.0.0",
|
||||
"flex.box": "^3.4.4",
|
||||
|
||||
@ -23,6 +23,12 @@ import { SearchStore } from "../search-store";
|
||||
import { Console } from "console";
|
||||
import { stdout, stderr } from "process";
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getPath: () => "/foo",
|
||||
},
|
||||
}));
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
let searchStore: SearchStore = null;
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import path from "path";
|
||||
import Config from "conf";
|
||||
import { Options as ConfOptions } from "conf/dist/source/types";
|
||||
import type { Options as ConfOptions } from "conf/dist/source/types";
|
||||
import { app, ipcMain, IpcMainEvent, ipcRenderer, IpcRendererEvent, remote } from "electron";
|
||||
import { IReactionOptions, observable, reaction, runInAction, when } from "mobx";
|
||||
import Singleton from "./utils/singleton";
|
||||
|
||||
@ -19,26 +19,36 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, computed, observable, toJS } from "mobx";
|
||||
import { action, computed, observable } from "mobx";
|
||||
import { Disposer, ExtendedMap } from "../utils";
|
||||
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
|
||||
|
||||
export class CatalogCategoryRegistry {
|
||||
@observable protected categories: CatalogCategory[] = [];
|
||||
protected categories = observable.set<CatalogCategory>();
|
||||
protected groupKinds = new ExtendedMap<string, ExtendedMap<string, CatalogCategory>>();
|
||||
|
||||
@action add(category: CatalogCategory) {
|
||||
this.categories.push(category);
|
||||
@action add(category: CatalogCategory): Disposer {
|
||||
this.categories.add(category);
|
||||
this.updateGroupKinds(category);
|
||||
|
||||
return () => {
|
||||
this.categories.delete(category);
|
||||
this.groupKinds.clear();
|
||||
};
|
||||
}
|
||||
|
||||
@action remove(category: CatalogCategory) {
|
||||
this.categories = this.categories.filter((cat) => cat.apiVersion !== category.apiVersion && cat.kind !== category.kind);
|
||||
private updateGroupKinds(category: CatalogCategory) {
|
||||
this.groupKinds
|
||||
.getOrInsert(category.spec.group, ExtendedMap.new)
|
||||
.strictSet(category.spec.names.kind, category);
|
||||
}
|
||||
|
||||
@computed get items() {
|
||||
return toJS(this.categories);
|
||||
return Array.from(this.categories);
|
||||
}
|
||||
|
||||
getForGroupKind<T extends CatalogCategory>(group: string, kind: string) {
|
||||
return this.categories.find((c) => c.spec.group === group && c.spec.names.kind === kind) as T;
|
||||
getForGroupKind<T extends CatalogCategory>(group: string, kind: string): T | undefined {
|
||||
return this.groupKinds.get(group)?.get(kind) as T;
|
||||
}
|
||||
|
||||
getEntityForData(data: CatalogEntityData & CatalogEntityKindData) {
|
||||
@ -60,17 +70,11 @@ export class CatalogCategoryRegistry {
|
||||
return new specVersion.entityClass(data);
|
||||
}
|
||||
|
||||
getCategoryForEntity<T extends CatalogCategory>(data: CatalogEntityData & CatalogEntityKindData) {
|
||||
getCategoryForEntity<T extends CatalogCategory>(data: CatalogEntityData & CatalogEntityKindData): T | undefined {
|
||||
const splitApiVersion = data.apiVersion.split("/");
|
||||
const group = splitApiVersion[0];
|
||||
|
||||
const category = this.categories.find((category) => {
|
||||
return category.spec.group === group && category.spec.names.kind === data.kind;
|
||||
});
|
||||
|
||||
if (!category) return null;
|
||||
|
||||
return category as T;
|
||||
return this.getForGroupKind(group, data.kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,4 +21,3 @@
|
||||
|
||||
export * from "./catalog-category-registry";
|
||||
export * from "./catalog-entity";
|
||||
export * from "./catalog-entity-registry";
|
||||
|
||||
@ -35,11 +35,9 @@ export const clusterKubectlDeleteAllHandler = "cluster:kubectl-delete-all";
|
||||
|
||||
if (ipcMain) {
|
||||
handleRequest(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
return cluster.activate(force);
|
||||
}
|
||||
return ClusterStore.getInstance()
|
||||
.getById(clusterId)
|
||||
?.activate(force);
|
||||
});
|
||||
|
||||
handleRequest(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => {
|
||||
@ -47,15 +45,14 @@ if (ipcMain) {
|
||||
|
||||
if (cluster) {
|
||||
clusterFrameMap.set(cluster.id, { frameId: event.frameId, processId: event.processId });
|
||||
|
||||
return cluster.pushState();
|
||||
cluster.pushState();
|
||||
}
|
||||
});
|
||||
|
||||
handleRequest(clusterRefreshHandler, (event, clusterId: ClusterId) => {
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) return cluster.refresh({ refreshMetadata: true });
|
||||
return ClusterStore.getInstance()
|
||||
.getById(clusterId)
|
||||
?.refresh({ refreshMetadata: true });
|
||||
});
|
||||
|
||||
handleRequest(clusterDisconnectHandler, (event, clusterId: ClusterId) => {
|
||||
|
||||
@ -30,9 +30,9 @@ import logger from "../main/logger";
|
||||
import { appEventBus } from "./event-bus";
|
||||
import { dumpConfigYaml } from "./kube-helpers";
|
||||
import { saveToAppFiles } from "./utils/saveToAppFiles";
|
||||
import { KubeConfig } from "@kubernetes/client-node";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { handleRequest, requestMain, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc";
|
||||
import { ResourceType } from "../renderer/components/cluster-settings/components/cluster-metrics-setting";
|
||||
import type { ResourceType } from "../renderer/components/cluster-settings/components/cluster-metrics-setting";
|
||||
import { disposer, noop } from "./utils";
|
||||
|
||||
export interface ClusterIconUpload {
|
||||
|
||||
@ -53,12 +53,11 @@ export class EventEmitter<D extends [...any[]]> {
|
||||
|
||||
emit(...data: D) {
|
||||
[...this.listeners].every(([callback, options]) => {
|
||||
if (options.once) this.removeListener(callback);
|
||||
const result = callback(...data);
|
||||
if (options.once) {
|
||||
this.removeListener(callback);
|
||||
}
|
||||
|
||||
if (result === false) return; // break cycle
|
||||
|
||||
return true;
|
||||
return callback(...data) !== false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,10 +19,10 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { EventEmitter } from "events";
|
||||
import { ipcMain } from "electron";
|
||||
import { EventEmitter } from "events";
|
||||
import logger from "../../main/logger";
|
||||
import { Disposer } from "../utils";
|
||||
import type { Disposer } from "../utils";
|
||||
|
||||
export type ListenerEvent<EM extends EventEmitter> = Parameters<Parameters<EM["on"]>[1]>[0];
|
||||
export type ListVerifier<T extends any[]> = (args: unknown[]) => args is T;
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { UpdateInfo } from "electron-updater";
|
||||
import type { UpdateInfo } from "electron-updater";
|
||||
|
||||
export const UpdateAvailableChannel = "update-available";
|
||||
export const AutoUpdateLogPrefix = "[UPDATE-CHECKER]";
|
||||
|
||||
@ -22,7 +22,7 @@ import fse from "fs-extra";
|
||||
import path from "path";
|
||||
import hb from "handlebars";
|
||||
import { ResourceApplier } from "../../main/resource-applier";
|
||||
import { KubernetesCluster } from "../catalog-entities";
|
||||
import type { KubernetesCluster } from "../catalog-entities";
|
||||
import logger from "../../main/logger";
|
||||
import { app } from "electron";
|
||||
import { requestMain } from "../ipc";
|
||||
|
||||
@ -230,7 +230,7 @@ export function getNodeWarningConditions(node: V1Node) {
|
||||
*
|
||||
* Note: This function returns an error instead of throwing it, returning `undefined` if the validation passes
|
||||
*/
|
||||
export function validateKubeConfig(config: KubeConfig, contextName: string, validationOpts: KubeConfigValidationOpts = {}): Error | undefined {
|
||||
export function validateKubeConfig(config: KubeConfig, contextName: string, validationOpts: KubeConfigValidationOpts = {}): Error | void {
|
||||
try {
|
||||
// we only receive a single context, cluster & user object here so lets validate them as this
|
||||
// will be called when we add a new cluster to Lens
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Url from "url-parse";
|
||||
import type Url from "url-parse";
|
||||
|
||||
export enum RoutingErrorType {
|
||||
INVALID_PROTOCOL = "invalid-protocol",
|
||||
@ -52,6 +52,8 @@ export class RoutingError extends Error {
|
||||
return "no extension ID";
|
||||
case RoutingErrorType.MISSING_EXTENSION:
|
||||
return "extension not found";
|
||||
default:
|
||||
return `unknown error: ${this.type}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,12 +24,12 @@ import { countBy } from "lodash";
|
||||
import { Singleton } from "../utils";
|
||||
import { pathToRegexp } from "path-to-regexp";
|
||||
import logger from "../../main/logger";
|
||||
import Url from "url-parse";
|
||||
import type Url from "url-parse";
|
||||
import { RoutingError, RoutingErrorType } from "./error";
|
||||
import { ExtensionsStore } from "../../extensions/extensions-store";
|
||||
import { ExtensionLoader } from "../../extensions/extension-loader";
|
||||
import { LensExtension } from "../../extensions/lens-extension";
|
||||
import { RouteHandler, RouteParams } from "../../extensions/registries/protocol-handler-registry";
|
||||
import type { LensExtension } from "../../extensions/lens-extension";
|
||||
import type { RouteHandler, RouteParams } from "../../extensions/registries/protocol-handler-registry";
|
||||
|
||||
// IPC channel for protocol actions. Main broadcasts the open-url events to this channel.
|
||||
export const ProtocolHandlerIpcPrefix = "protocol-handler";
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { AbortController } from "abort-controller";
|
||||
import type { AbortController } from "abort-controller";
|
||||
|
||||
/**
|
||||
* Return a promise that will be resolved after at least `timeout` ms have
|
||||
|
||||
@ -22,19 +22,17 @@
|
||||
import { action, IEnhancer, IObservableMapInitialValues, ObservableMap } from "mobx";
|
||||
|
||||
export class ExtendedMap<K, V> extends Map<K, V> {
|
||||
constructor(protected getDefault: () => V, entries?: readonly (readonly [K, V])[] | null) {
|
||||
super(entries);
|
||||
static new<K, V>(entries?: readonly (readonly [K, V])[] | null): ExtendedMap<K, V> {
|
||||
return new ExtendedMap<K, V>(entries);
|
||||
}
|
||||
|
||||
getOrInsert(key: K, val: V): V {
|
||||
if (this.has(key)) {
|
||||
return this.get(key);
|
||||
}
|
||||
|
||||
return this.set(key, val).get(key);
|
||||
}
|
||||
|
||||
getOrInsertWith(key: K, getVal: () => V): V {
|
||||
/**
|
||||
* Get the value behind `key`. If it was not pressent, first insert the value returned by `getVal`
|
||||
* @param key The key to insert into the map with
|
||||
* @param getVal A function that returns a new instance of `V`.
|
||||
* @returns The value in the map
|
||||
*/
|
||||
getOrInsert(key: K, getVal: () => V): V {
|
||||
if (this.has(key)) {
|
||||
return this.get(key);
|
||||
}
|
||||
@ -42,12 +40,29 @@ export class ExtendedMap<K, V> extends Map<K, V> {
|
||||
return this.set(key, getVal()).get(key);
|
||||
}
|
||||
|
||||
getOrDefault(key: K): V {
|
||||
/**
|
||||
* Set the value associated with `key` iff there was not a previous value
|
||||
* @throws if `key` already in map
|
||||
* @returns `this` so that `strictSet` can be chained
|
||||
*/
|
||||
strictSet(key: K, val: V): this {
|
||||
if (this.has(key)) {
|
||||
return this.get(key);
|
||||
throw new TypeError("Duplicate key in map");
|
||||
}
|
||||
|
||||
return this.set(key, this.getDefault()).get(key);
|
||||
return this.set(key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value associated with `key`
|
||||
* @throws if `key` did not a value associated with it
|
||||
*/
|
||||
strictGet(key: K): V {
|
||||
if (!this.has(key)) {
|
||||
throw new TypeError("key not in map");
|
||||
}
|
||||
|
||||
return this.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export type Falsey = false | 0 | "" | null | undefined;
|
||||
|
||||
/**
|
||||
* Create a new type safe empty Iterable
|
||||
* @returns An `Iterable` that yields 0 items
|
||||
@ -57,6 +59,36 @@ export function* map<T, U>(src: Iterable<T>, fn: (from: T) => U): Iterable<U> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The single layer flattening of an iterator, discarding `Falsey` values.
|
||||
* @param src A type that can be iterated over
|
||||
* @param fn The function that returns either an iterable over items that should be filtered out or a `Falsey` value indicating that it should be ignored
|
||||
*/
|
||||
export function* filterFlatMap<T, U>(src: Iterable<T>, fn: (from: T) => Iterable<U | Falsey> | Falsey): Iterable<U> {
|
||||
for (const from of src) {
|
||||
if (!from) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const mapping = fn(from);
|
||||
|
||||
if (!mapping) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const mapped of mapping) {
|
||||
if (mapped) {
|
||||
yield mapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new iterator that yields the items that each call to `fn` would produce
|
||||
* @param src A type that can be iterated over
|
||||
* @param fn A function that returns an iterator
|
||||
*/
|
||||
export function* flatMap<T, U>(src: Iterable<T>, fn: (from: T) => Iterable<U>): Iterable<U> {
|
||||
for (const from of src) {
|
||||
yield* fn(from);
|
||||
@ -83,7 +115,7 @@ export function* filter<T>(src: Iterable<T>, fn: (from: T) => any): Iterable<T>
|
||||
* @param src A type that can be iterated over
|
||||
* @param fn The function that is called for each value
|
||||
*/
|
||||
export function* filterMap<T, U>(src: Iterable<T>, fn: (from: T) => U): Iterable<U> {
|
||||
export function* filterMap<T, U>(src: Iterable<T>, fn: (from: T) => U | Falsey): Iterable<U> {
|
||||
for (const from of src) {
|
||||
const res = fn(from);
|
||||
|
||||
@ -99,7 +131,7 @@ export function* filterMap<T, U>(src: Iterable<T>, fn: (from: T) => U): Iterable
|
||||
* @param src A type that can be iterated over
|
||||
* @param fn The function that is called for each value
|
||||
*/
|
||||
export function* filterMapStrict<T, U>(src: Iterable<T>, fn: (from: T) => U): Iterable<U> {
|
||||
export function* filterMapStrict<T, U>(src: Iterable<T>, fn: (from: T) => U | null | undefined): Iterable<U> {
|
||||
for (const from of src) {
|
||||
const res = fn(from);
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
import path from "path";
|
||||
import { app, remote } from "electron";
|
||||
import { ensureDirSync, writeFileSync } from "fs-extra";
|
||||
import { WriteFileOptions } from "fs";
|
||||
import type { WriteFileOptions } from "fs";
|
||||
|
||||
export function saveToAppFiles(filePath: string, contents: any, options?: WriteFileOptions): string {
|
||||
const absPath = path.resolve((app || remote.app).getPath("userData"), filePath);
|
||||
|
||||
@ -77,6 +77,8 @@ jest.mock(
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}),
|
||||
on: jest.fn(
|
||||
(channel: string, listener: (event: any, ...args: any[]) => void) => {
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
*/
|
||||
|
||||
|
||||
import { CatalogEntity, catalogEntityRegistry as registry } from "../../common/catalog";
|
||||
import type { CatalogEntity } from "../../common/catalog";
|
||||
import { catalogEntityRegistry as registry } from "../../main/catalog";
|
||||
|
||||
export { catalogCategoryRegistry as catalogCategories } from "../../common/catalog/catalog-category-registry";
|
||||
export * from "../../common/catalog-entities";
|
||||
|
||||
@ -325,6 +325,8 @@ export class ExtensionLoader extends Singleton {
|
||||
} catch (error) {
|
||||
logger.error(`${logModule}: can't load extension main at ${extAbsolutePath}: ${error}`, { extension, error });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
getExtension(extId: LensExtensionId): InstalledExtension {
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import { BaseStore } from "../common/base-store";
|
||||
import * as path from "path";
|
||||
import { LensExtension } from "./lens-extension";
|
||||
import type { LensExtension } from "./lens-extension";
|
||||
|
||||
export abstract class ExtensionStore<T> extends BaseStore<T> {
|
||||
protected extension: LensExtension;
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
import { Singleton } from "../common/utils";
|
||||
import { LensExtension } from "./lens-extension";
|
||||
import type { LensExtension } from "./lens-extension";
|
||||
import { createHash } from "crypto";
|
||||
import { broadcastMessage } from "../common/ipc";
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import type { InstalledExtension } from "./extension-discovery";
|
||||
import { action, observable, reaction } from "mobx";
|
||||
import { FilesystemProvisionerStore } from "../main/extension-filesystem";
|
||||
import logger from "../main/logger";
|
||||
import { ProtocolHandlerRegistration } from "./registries";
|
||||
import type { ProtocolHandlerRegistration } from "./registries";
|
||||
import { disposer } from "../common/utils";
|
||||
|
||||
export type LensExtensionId = string; // path to manifest (package.json)
|
||||
|
||||
@ -22,9 +22,10 @@
|
||||
import { LensExtension } from "./lens-extension";
|
||||
import { WindowManager } from "../main/window-manager";
|
||||
import { getExtensionPageUrl } from "./registries/page-registry";
|
||||
import { CatalogEntity, catalogEntityRegistry } from "../common/catalog";
|
||||
import { IObservableArray } from "mobx";
|
||||
import { MenuRegistration } from "./registries";
|
||||
import { catalogEntityRegistry } from "../main/catalog";
|
||||
import type { CatalogEntity } from "../common/catalog";
|
||||
import type { IObservableArray } from "mobx";
|
||||
import type { MenuRegistration } from "./registries";
|
||||
|
||||
export class LensMainExtension extends LensExtension {
|
||||
appMenus: MenuRegistration[] = [];
|
||||
|
||||
@ -26,8 +26,8 @@ import type {
|
||||
import type { Cluster } from "../main/cluster";
|
||||
import { LensExtension } from "./lens-extension";
|
||||
import { getExtensionPageUrl } from "./registries/page-registry";
|
||||
import { CommandRegistration } from "./registries/command-registry";
|
||||
import { EntitySettingRegistration } from "./registries/entity-setting-registry";
|
||||
import type { CommandRegistration } from "./registries/command-registry";
|
||||
import type { EntitySettingRegistration } from "./registries/entity-setting-registry";
|
||||
|
||||
export class LensRendererExtension extends LensExtension {
|
||||
globalPages: PageRegistration[] = [];
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { IpcPrefix, IpcStore } from "./ipc-store";
|
||||
import { Disposers } from "./lens-extension";
|
||||
import { LensMainExtension } from "./lens-main-extension";
|
||||
import type { LensMainExtension } from "./lens-main-extension";
|
||||
|
||||
export abstract class MainIpcStore extends IpcStore {
|
||||
constructor(extension: LensMainExtension) {
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import type React from "react";
|
||||
import { CatalogEntity } from "../../common/catalog";
|
||||
import type { CatalogEntity } from "../../common/catalog";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface EntitySettingViewProps {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import type React from "react";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface KubeObjectDetailComponents {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import type React from "react";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface KubeObjectMenuComponents {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { KubeObject, KubeObjectStatus } from "../renderer-api/k8s-api";
|
||||
import type { KubeObject, KubeObjectStatus } from "../renderer-api/k8s-api";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
export interface KubeObjectStatusRegistration {
|
||||
|
||||
@ -25,7 +25,7 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
import { LensExtension, sanitizeExtensionName } from "../lens-extension";
|
||||
import { PageParam, PageParamInit } from "../../renderer/navigation/page-param";
|
||||
import type { PageParam, PageParamInit } from "../../renderer/navigation/page-param";
|
||||
import { createPageParam } from "../../renderer/navigation/helpers";
|
||||
|
||||
export interface PageRegistration {
|
||||
@ -119,9 +119,9 @@ export class PageRegistry extends BaseRegistry<PageRegistration, RegisteredPage>
|
||||
return components;
|
||||
}
|
||||
|
||||
protected normalizeParams(params?: PageParams<string | ExtensionPageParamInit>): PageParams<PageParam> {
|
||||
protected normalizeParams(params?: PageParams<string | ExtensionPageParamInit>): PageParams<PageParam> | undefined {
|
||||
if (!params) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
Object.entries(params).forEach(([name, value]) => {
|
||||
const paramInit: PageParamInit = typeof value === "object"
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
// Extensions API -> Status bar customizations
|
||||
|
||||
import React from "react";
|
||||
import type React from "react";
|
||||
import { BaseRegistry } from "./base-registry";
|
||||
|
||||
interface StatusBarComponents {
|
||||
|
||||
@ -23,9 +23,9 @@ export { isAllowedResource } from "../../common/rbac";
|
||||
export { ResourceStack } from "../../common/k8s/resource-stack";
|
||||
export { apiManager } from "../../renderer/api/api-manager";
|
||||
export { KubeObjectStore } from "../../renderer/kube-object.store";
|
||||
export { KubeApi, forCluster, IKubeApiCluster } from "../../renderer/api/kube-api";
|
||||
export { KubeApi, forCluster } from "../../renderer/api/kube-api";
|
||||
export { KubeObject } from "../../renderer/api/kube-object";
|
||||
export { Pod, podsApi, PodsApi, IPodContainer, IPodContainerStatus } from "../../renderer/api/endpoints";
|
||||
export { Pod, podsApi, PodsApi } from "../../renderer/api/endpoints";
|
||||
export { Node, nodesApi, NodesApi } from "../../renderer/api/endpoints";
|
||||
export { Deployment, deploymentApi, DeploymentApi } from "../../renderer/api/endpoints";
|
||||
export { DaemonSet, daemonSetApi } from "../../renderer/api/endpoints";
|
||||
@ -33,7 +33,7 @@ export { StatefulSet, statefulSetApi } from "../../renderer/api/endpoints";
|
||||
export { Job, jobApi } from "../../renderer/api/endpoints";
|
||||
export { CronJob, cronJobApi } from "../../renderer/api/endpoints";
|
||||
export { ConfigMap, configMapApi } from "../../renderer/api/endpoints";
|
||||
export { Secret, secretsApi, ISecretRef } from "../../renderer/api/endpoints";
|
||||
export { Secret, secretsApi } from "../../renderer/api/endpoints";
|
||||
export { ReplicaSet, replicaSetApi } from "../../renderer/api/endpoints";
|
||||
export { ResourceQuota, resourceQuotaApi } from "../../renderer/api/endpoints";
|
||||
export { LimitRange, limitRangeApi } from "../../renderer/api/endpoints";
|
||||
@ -54,7 +54,13 @@ export { RoleBinding, roleBindingApi } from "../../renderer/api/endpoints";
|
||||
export { ClusterRole, clusterRoleApi } from "../../renderer/api/endpoints";
|
||||
export { ClusterRoleBinding, clusterRoleBindingApi } from "../../renderer/api/endpoints";
|
||||
export { CustomResourceDefinition, crdApi } from "../../renderer/api/endpoints";
|
||||
export { KubeObjectStatus, KubeObjectStatusLevel } from "./kube-object-status";
|
||||
export { KubeObjectStatusLevel } from "./kube-object-status";
|
||||
|
||||
// types
|
||||
export type { IKubeApiCluster } from "../../renderer/api/kube-api";
|
||||
export type { IPodContainer, IPodContainerStatus } from "../../renderer/api/endpoints";
|
||||
export type { ISecretRef } from "../../renderer/api/endpoints";
|
||||
export type { KubeObjectStatus } from "./kube-object-status";
|
||||
|
||||
// stores
|
||||
export type { EventStore } from "../../renderer/components/+events/event.store";
|
||||
|
||||
@ -25,7 +25,7 @@ import { navigation } from "../../renderer/navigation";
|
||||
export type { PageParamInit, PageParam } from "../../renderer/navigation/page-param";
|
||||
export { navigate, isActiveRoute } from "../../renderer/navigation/helpers";
|
||||
export { hideDetails, showDetails, getDetailsUrl } from "../../renderer/components/kube-object/kube-object-details";
|
||||
export { IURLParams } from "../../common/utils/buildUrl";
|
||||
export type { IURLParams } from "../../common/utils/buildUrl";
|
||||
|
||||
// exporting to extensions-api version of helper without `isSystem` flag
|
||||
export function createPageParam<V = string>(init: PageParamInit<V>) {
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
import { ipcRenderer } from "electron";
|
||||
import { IpcPrefix, IpcStore } from "./ipc-store";
|
||||
import { Disposers } from "./lens-extension";
|
||||
import { LensRendererExtension } from "./lens-renderer-extension";
|
||||
import type { LensRendererExtension } from "./lens-renderer-extension";
|
||||
|
||||
export abstract class RendererIpcStore extends IpcStore {
|
||||
constructor(extension: LensRendererExtension) {
|
||||
|
||||
@ -44,6 +44,12 @@ jest.mock("winston", () => ({
|
||||
}
|
||||
}));
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getPath: () => "/foo",
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("../../common/ipc");
|
||||
jest.mock("child_process");
|
||||
jest.mock("tcp-port-used");
|
||||
@ -56,7 +62,7 @@ import { ChildProcess, spawn } from "child_process";
|
||||
import { bundledKubectlPath, Kubectl } from "../kubectl";
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
import { waitUntilUsed } from "tcp-port-used";
|
||||
import { Readable } from "stream";
|
||||
import type { Readable } from "stream";
|
||||
import { UserStore } from "../../common/user-store";
|
||||
import { Console } from "console";
|
||||
import { stdout, stderr } from "process";
|
||||
|
||||
@ -28,6 +28,12 @@ const logger = {
|
||||
crit: jest.fn(),
|
||||
};
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getPath: () => `/tmp`,
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock("winston", () => ({
|
||||
format: {
|
||||
colorize: jest.fn(),
|
||||
@ -47,7 +53,7 @@ jest.mock("winston", () => ({
|
||||
import { KubeconfigManager } from "../kubeconfig-manager";
|
||||
import mockFs from "mock-fs";
|
||||
import { Cluster } from "../cluster";
|
||||
import { ContextHandler } from "../context-handler";
|
||||
import type { ContextHandler } from "../context-handler";
|
||||
import fse from "fs-extra";
|
||||
import { loadYaml } from "@kubernetes/client-node";
|
||||
import { Console } from "console";
|
||||
@ -91,7 +97,9 @@ describe("kubeconfig manager tests", () => {
|
||||
contextName: "minikube",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
});
|
||||
contextHandler = jest.fn() as any;
|
||||
contextHandler = {
|
||||
ensureServer: () => Promise.resolve(),
|
||||
} as any;
|
||||
jest.spyOn(KubeconfigManager.prototype, "resolveProxyUrl", "get").mockReturnValue("http://127.0.0.1:9191/foo");
|
||||
});
|
||||
|
||||
@ -103,7 +111,7 @@ describe("kubeconfig manager tests", () => {
|
||||
const kubeConfManager = new KubeconfigManager(cluster, contextHandler);
|
||||
|
||||
expect(logger.error).not.toBeCalled();
|
||||
expect(await kubeConfManager.getPath()).toBe(`tmp${path.sep}kubeconfig-foo`);
|
||||
expect(await kubeConfManager.getPath()).toBe(`${path.sep}tmp${path.sep}kubeconfig-foo`);
|
||||
// this causes an intermittent "ENXIO: no such device or address, read" error
|
||||
// const file = await fse.readFile(await kubeConfManager.getPath());
|
||||
const file = fse.readFileSync(await kubeConfManager.getPath());
|
||||
|
||||
@ -20,33 +20,14 @@
|
||||
*/
|
||||
|
||||
import { reaction, toJS } from "mobx";
|
||||
import { broadcastMessage, subscribeToBroadcast, unsubscribeFromBroadcast } from "../common/ipc";
|
||||
import { CatalogEntityRegistry} from "../common/catalog";
|
||||
import { broadcastMessage } from "../common/ipc";
|
||||
import type { CatalogEntityRegistry} from "./catalog";
|
||||
import "../common/catalog-entities/kubernetes-cluster";
|
||||
import { Disposer } from "../common/utils";
|
||||
|
||||
export class CatalogPusher {
|
||||
static init(catalog: CatalogEntityRegistry) {
|
||||
new CatalogPusher(catalog).init();
|
||||
}
|
||||
|
||||
private constructor(private catalog: CatalogEntityRegistry) {}
|
||||
|
||||
init() {
|
||||
const disposers: Disposer[] = [];
|
||||
|
||||
disposers.push(reaction(() => toJS(this.catalog.items, { recurseEverything: true }), (items) => {
|
||||
broadcastMessage("catalog:items", items);
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
}));
|
||||
|
||||
const listener = subscribeToBroadcast("catalog:broadcast", () => {
|
||||
broadcastMessage("catalog:items", toJS(this.catalog.items, { recurseEverything: true }));
|
||||
});
|
||||
|
||||
disposers.push(() => unsubscribeFromBroadcast("catalog:broadcast", listener));
|
||||
|
||||
return disposers;
|
||||
}
|
||||
export function pushCatalogToRenderer(catalog: CatalogEntityRegistry) {
|
||||
return reaction(() => toJS(catalog.items, { recurseEverything: true }), (items) => {
|
||||
broadcastMessage("catalog:items", items);
|
||||
}, {
|
||||
fireImmediately: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -20,9 +20,9 @@
|
||||
*/
|
||||
|
||||
import { ObservableMap } from "mobx";
|
||||
import { CatalogEntity } from "../../../common/catalog";
|
||||
import type { CatalogEntity } from "../../../common/catalog";
|
||||
import { loadFromOptions } from "../../../common/kube-helpers";
|
||||
import { Cluster } from "../../cluster";
|
||||
import type { Cluster } from "../../cluster";
|
||||
import { computeDiff, configToModels } from "../kubeconfig-sync";
|
||||
import mockFs from "mock-fs";
|
||||
import fs from "fs";
|
||||
|
||||
@ -20,14 +20,15 @@
|
||||
*/
|
||||
|
||||
import { action, observable, IComputedValue, computed, ObservableMap, runInAction } from "mobx";
|
||||
import { CatalogEntity, catalogEntityRegistry } from "../../common/catalog";
|
||||
import type { CatalogEntity } from "../../common/catalog";
|
||||
import { catalogEntityRegistry } from "../../main/catalog";
|
||||
import { watch } from "chokidar";
|
||||
import fs from "fs";
|
||||
import fse from "fs-extra";
|
||||
import stream from "stream";
|
||||
import type stream from "stream";
|
||||
import { Disposer, ExtendedObservableMap, iter, Singleton } from "../../common/utils";
|
||||
import logger from "../logger";
|
||||
import { KubeConfig } from "@kubernetes/client-node";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { loadConfigFromString, splitConfig, validateKubeConfig } from "../../common/kube-helpers";
|
||||
import { Cluster } from "../cluster";
|
||||
import { catalogEntityFromCluster } from "../cluster-manager";
|
||||
|
||||
@ -20,8 +20,30 @@
|
||||
*/
|
||||
|
||||
import { observable, reaction } from "mobx";
|
||||
import { WebLink } from "../catalog-entities";
|
||||
import { CatalogEntityRegistry } from "../catalog";
|
||||
import { WebLink, WebLinkSpec, WebLinkStatus } from "../../../common/catalog-entities";
|
||||
import { catalogCategoryRegistry, CatalogEntity, CatalogEntityMetadata } from "../../../common/catalog";
|
||||
import { CatalogEntityRegistry } from "../catalog-entity-registry";
|
||||
|
||||
class InvalidEntity extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus, WebLinkSpec> {
|
||||
public readonly apiVersion = "entity.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "Invalid";
|
||||
|
||||
async onRun() {
|
||||
return;
|
||||
}
|
||||
|
||||
public onSettingsOpen(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
public onDetailsOpen(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
public onContextMenuOpen(): void {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
describe("CatalogEntityRegistry", () => {
|
||||
let registry: CatalogEntityRegistry;
|
||||
@ -39,9 +61,23 @@ describe("CatalogEntityRegistry", () => {
|
||||
phase: "valid"
|
||||
}
|
||||
});
|
||||
const invalidEntity = new InvalidEntity({
|
||||
metadata: {
|
||||
uid: "invalid",
|
||||
name: "test-link",
|
||||
source: "test",
|
||||
labels: {}
|
||||
},
|
||||
spec: {
|
||||
url: "https://k8slens.dev"
|
||||
},
|
||||
status: {
|
||||
phase: "valid"
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
registry = new CatalogEntityRegistry();
|
||||
registry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
});
|
||||
|
||||
describe("addSource", () => {
|
||||
@ -79,4 +115,22 @@ describe("CatalogEntityRegistry", () => {
|
||||
expect(registry.items.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("items", () => {
|
||||
it("returns added items", () => {
|
||||
expect(registry.items.length).toBe(0);
|
||||
|
||||
const source = observable.array([entity]);
|
||||
|
||||
registry.addObservableSource("test", source);
|
||||
expect(registry.items.length).toBe(1);
|
||||
});
|
||||
|
||||
it("does not return items without matching category", () => {
|
||||
const source = observable.array([invalidEntity]);
|
||||
|
||||
registry.addObservableSource("test", source);
|
||||
expect(registry.items.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -20,12 +20,14 @@
|
||||
*/
|
||||
|
||||
import { action, computed, observable, IComputedValue, IObservableArray } from "mobx";
|
||||
import { CatalogEntity } from "./catalog-entity";
|
||||
import { iter } from "../utils";
|
||||
import { CatalogCategoryRegistry, catalogCategoryRegistry, CatalogEntity } from "../../common/catalog";
|
||||
import { iter } from "../../common/utils";
|
||||
|
||||
export class CatalogEntityRegistry {
|
||||
protected sources = observable.map<string, IComputedValue<CatalogEntity[]>>([], { deep: true });
|
||||
|
||||
constructor(private categoryRegistry: CatalogCategoryRegistry) {}
|
||||
|
||||
@action addObservableSource(id: string, source: IObservableArray<CatalogEntity>) {
|
||||
this.sources.set(id, computed(() => source));
|
||||
}
|
||||
@ -39,7 +41,9 @@ export class CatalogEntityRegistry {
|
||||
}
|
||||
|
||||
@computed get items(): CatalogEntity[] {
|
||||
return Array.from(iter.flatMap(this.sources.values(), source => source.get()));
|
||||
const allItems = Array.from(iter.flatMap(this.sources.values(), source => source.get()));
|
||||
|
||||
return allItems.filter((entity) => this.categoryRegistry.getCategoryForEntity(entity) !== undefined);
|
||||
}
|
||||
|
||||
getItemsForApiKind<T extends CatalogEntity>(apiVersion: string, kind: string): T[] {
|
||||
@ -49,4 +53,4 @@ export class CatalogEntityRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
export const catalogEntityRegistry = new CatalogEntityRegistry();
|
||||
export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
22
src/main/catalog/index.ts
Normal file
22
src/main/catalog/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export * from "./catalog-entity-registry";
|
||||
@ -19,8 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { RequestPromiseOptions } from "request-promise-native";
|
||||
import { Cluster } from "../cluster";
|
||||
import type { RequestPromiseOptions } from "request-promise-native";
|
||||
import type { Cluster } from "../cluster";
|
||||
import { k8sRequest } from "../k8s-request";
|
||||
|
||||
export type ClusterDetectionResult = {
|
||||
|
||||
@ -20,9 +20,9 @@
|
||||
*/
|
||||
|
||||
import { observable } from "mobx";
|
||||
import { ClusterMetadata } from "../../common/cluster-store";
|
||||
import { Cluster } from "../cluster";
|
||||
import { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector";
|
||||
import type { ClusterMetadata } from "../../common/cluster-store";
|
||||
import type { Cluster } from "../cluster";
|
||||
import type { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector";
|
||||
import { ClusterIdDetector } from "./cluster-id-detector";
|
||||
import { DistributionDetector } from "./distribution-detector";
|
||||
import { LastSeenDetector } from "./last-seen-detector";
|
||||
|
||||
@ -24,11 +24,11 @@ import type http from "http";
|
||||
import { ipcMain } from "electron";
|
||||
import { action, autorun, reaction, toJS } from "mobx";
|
||||
import { ClusterStore, getClusterIdFromHost } from "../common/cluster-store";
|
||||
import { Cluster } from "./cluster";
|
||||
import type { Cluster } from "./cluster";
|
||||
import logger from "./logger";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import { Singleton } from "../common/utils";
|
||||
import { catalogEntityRegistry } from "../common/catalog";
|
||||
import { catalogEntityRegistry } from "./catalog";
|
||||
import { KubernetesCluster, KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster";
|
||||
|
||||
export class ClusterManager extends Singleton {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { PrometheusProvider, PrometheusService } from "./prometheus/provider-registry";
|
||||
import type { PrometheusService } from "./prometheus/provider-registry";
|
||||
import type { ClusterPrometheusPreferences } from "../common/cluster-store";
|
||||
import type { Cluster } from "./cluster";
|
||||
import type httpProxy from "http-proxy";
|
||||
@ -75,16 +75,15 @@ export class ContextHandler {
|
||||
return prometheusProviders.find(p => p.id === this.prometheusProvider);
|
||||
}
|
||||
|
||||
async getPrometheusService(): Promise<PrometheusService> {
|
||||
async getPrometheusService(): Promise<PrometheusService | void> {
|
||||
const providers = this.prometheusProvider ? prometheusProviders.filter(provider => provider.id == this.prometheusProvider) : prometheusProviders;
|
||||
const prometheusPromises: Promise<PrometheusService>[] = providers.map(async (provider: PrometheusProvider): Promise<PrometheusService> => {
|
||||
const prometheusPromises: Promise<PrometheusService | void>[] = providers.map(async provider => {
|
||||
const apiClient = (await this.cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api);
|
||||
|
||||
return await provider.getPrometheusService(apiClient);
|
||||
return provider.getPrometheusService(apiClient);
|
||||
});
|
||||
const resolvedPrometheusServices = await Promise.all(prometheusPromises);
|
||||
|
||||
return resolvedPrometheusServices.filter(n => n)[0];
|
||||
return (await Promise.all(prometheusPromises)).find(Boolean);
|
||||
}
|
||||
|
||||
async getPrometheusPath(): Promise<string> {
|
||||
|
||||
@ -26,7 +26,7 @@ import fse from "fs-extra";
|
||||
import { action, observable, toJS } from "mobx";
|
||||
import path from "path";
|
||||
import { BaseStore } from "../common/base-store";
|
||||
import { LensExtensionId } from "../extensions/lens-extension";
|
||||
import type { LensExtensionId } from "../extensions/lens-extension";
|
||||
|
||||
interface FSProvisionModel {
|
||||
extensions: Record<string, string>; // extension names to paths
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import { HelmRepo, HelmRepoManager } from "../helm-repo-manager";
|
||||
|
||||
export class HelmChartManager {
|
||||
private cache: any = {};
|
||||
cache: any = {};
|
||||
private repo: HelmRepo;
|
||||
|
||||
constructor(repo: HelmRepo){
|
||||
|
||||
@ -24,7 +24,7 @@ import fse from "fs-extra";
|
||||
import * as yaml from "js-yaml";
|
||||
import { promiseExec} from "../promise-exec";
|
||||
import { helmCli } from "./helm-cli";
|
||||
import { Cluster } from "../cluster";
|
||||
import type { Cluster } from "../cluster";
|
||||
import { toCamelCase } from "../../common/utils/camelCase";
|
||||
|
||||
export async function listReleases(pathToKubeconfig: string, namespace?: string) {
|
||||
|
||||
@ -20,11 +20,11 @@
|
||||
*/
|
||||
|
||||
import semver from "semver";
|
||||
import { Cluster } from "../cluster";
|
||||
import type { Cluster } from "../cluster";
|
||||
import logger from "../logger";
|
||||
import { HelmRepoManager } from "./helm-repo-manager";
|
||||
import { HelmChartManager } from "./helm-chart-manager";
|
||||
import { HelmChartList, RepoHelmChartList } from "../../renderer/api/endpoints/helm-charts.api";
|
||||
import type { HelmChartList, RepoHelmChartList } from "../../renderer/api/endpoints/helm-charts.api";
|
||||
import { deleteRelease, getHistory, getRelease, getValues, installChart, listReleases, rollback, upgradeRelease } from "./helm-release-manager";
|
||||
|
||||
class HelmService {
|
||||
|
||||
@ -45,18 +45,19 @@ import type { LensExtensionId } from "../extensions/lens-extension";
|
||||
import { FilesystemProvisionerStore } from "./extension-filesystem";
|
||||
import { installDeveloperTools } from "./developer-tools";
|
||||
import { LensProtocolRouterMain } from "./protocol-handler";
|
||||
import { getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
||||
import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
|
||||
import { bindBroadcastHandlers } from "../common/ipc";
|
||||
import { startUpdateChecking } from "./app-updater";
|
||||
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
||||
import { CatalogPusher } from "./catalog-pusher";
|
||||
import { catalogEntityRegistry } from "../common/catalog";
|
||||
import { pushCatalogToRenderer } from "./catalog-pusher";
|
||||
import { catalogEntityRegistry } from "./catalog";
|
||||
import { HotbarStore } from "../common/hotbar-store";
|
||||
import { HelmRepoManager } from "./helm/helm-repo-manager";
|
||||
import { KubeconfigSyncManager } from "./catalog-sources";
|
||||
import { handleWsUpgrade } from "./proxy/ws-upgrade";
|
||||
|
||||
const workingDir = path.join(app.getPath("appData"), appName);
|
||||
const cleanup = disposer();
|
||||
|
||||
app.setName(appName);
|
||||
|
||||
@ -142,7 +143,7 @@ app.on("ready", async () => {
|
||||
const lensProxy = LensProxy.createInstance(handleWsUpgrade);
|
||||
|
||||
ClusterManager.createInstance();
|
||||
KubeconfigSyncManager.createInstance().startSync();
|
||||
KubeconfigSyncManager.createInstance();
|
||||
|
||||
try {
|
||||
logger.info("🔌 Starting LensProxy");
|
||||
@ -187,7 +188,8 @@ app.on("ready", async () => {
|
||||
}
|
||||
|
||||
ipcMain.on(IpcRendererNavigationEvents.LOADED, () => {
|
||||
CatalogPusher.init(catalogEntityRegistry);
|
||||
cleanup.push(pushCatalogToRenderer(catalogEntityRegistry));
|
||||
KubeconfigSyncManager.getInstance().startSync();
|
||||
startUpdateChecking();
|
||||
LensProtocolRouterMain
|
||||
.getInstance()
|
||||
@ -251,6 +253,7 @@ app.on("will-quit", (event) => {
|
||||
appEventBus.emit({name: "app", action: "close"});
|
||||
ClusterManager.getInstance(false)?.stop(); // close cluster connections
|
||||
KubeconfigSyncManager.getInstance(false)?.stopSync();
|
||||
cleanup();
|
||||
|
||||
if (blockQuit) {
|
||||
event.preventDefault(); // prevent app's default shutdown (e.g. required for telemetry, etc.)
|
||||
|
||||
@ -21,9 +21,9 @@
|
||||
|
||||
import request, { RequestPromiseOptions } from "request-promise-native";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
||||
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
||||
import { LensProxy } from "./proxy/lens-proxy";
|
||||
import { Cluster } from "./cluster";
|
||||
import type { Cluster } from "./cluster";
|
||||
|
||||
export async function k8sRequest<T = any>(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||
const kubeProxyUrl = `http://localhost:${LensProxy.getInstance().port}${apiKubePrefix}`;
|
||||
|
||||
@ -71,6 +71,7 @@ export class KubeconfigManager {
|
||||
await this.contextHandler.ensureServer();
|
||||
this.tempFile = await this.createProxyKubeconfig();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
logger.error(`Failed to created temp config for auth-proxy`, { err });
|
||||
}
|
||||
}
|
||||
@ -86,7 +87,7 @@ export class KubeconfigManager {
|
||||
protected async createProxyKubeconfig(): Promise<string> {
|
||||
const { configDir, cluster } = this;
|
||||
const { contextName, kubeConfigPath, id } = cluster;
|
||||
const tempFile = path.join(configDir, `kubeconfig-${id}`);
|
||||
const tempFile = path.normalize(path.join(configDir, `kubeconfig-${id}`));
|
||||
const kubeConfig = loadConfig(kubeConfigPath);
|
||||
const proxyConfig: Partial<KubeConfig> = {
|
||||
currentContext: contextName,
|
||||
|
||||
@ -25,7 +25,7 @@ import request from "request";
|
||||
import { ensureDir, pathExists } from "fs-extra";
|
||||
import * as tar from "tar";
|
||||
import { isWindows } from "../common/vars";
|
||||
import winston from "winston";
|
||||
import type winston from "winston";
|
||||
|
||||
export type LensBinaryOpts = {
|
||||
version: string;
|
||||
@ -204,7 +204,7 @@ export class LensBinary {
|
||||
throw(error);
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
file.on("close", () => {
|
||||
this.logger.debug(`${this.originalBinaryName} binary download closed`);
|
||||
if (!this.tarPath) fs.chmod(binaryPath, 0o755, (err) => {
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import { app, BrowserWindow, dialog, ipcMain, IpcMainEvent, Menu, MenuItem, MenuItemConstructorOptions, webContents, shell } from "electron";
|
||||
import { autorun } from "mobx";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import type { WindowManager } from "./window-manager";
|
||||
import { appName, isMac, isWindows, isTestEnv, docsUrl, supportUrl, productName } from "../common/vars";
|
||||
import { addClusterURL } from "../renderer/components/+add-cluster/add-cluster.route";
|
||||
import { preferencesURL } from "../renderer/components/+preferences/preferences.route";
|
||||
|
||||
@ -20,8 +20,8 @@
|
||||
*/
|
||||
|
||||
import { PrometheusLens } from "./lens";
|
||||
import { CoreV1Api } from "@kubernetes/client-node";
|
||||
import { PrometheusService } from "./provider-registry";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import type { PrometheusService } from "./provider-registry";
|
||||
import logger from "../logger";
|
||||
|
||||
export class PrometheusHelm extends PrometheusLens {
|
||||
@ -29,7 +29,7 @@ export class PrometheusHelm extends PrometheusLens {
|
||||
name = "Helm";
|
||||
rateAccuracy = "5m";
|
||||
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService | void> {
|
||||
const labelSelector = "app=prometheus,component=server,heritage=Helm";
|
||||
|
||||
try {
|
||||
|
||||
@ -19,8 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry";
|
||||
import { CoreV1Api } from "@kubernetes/client-node";
|
||||
import type { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import logger from "../logger";
|
||||
|
||||
export class PrometheusLens implements PrometheusProvider {
|
||||
@ -28,7 +28,7 @@ export class PrometheusLens implements PrometheusProvider {
|
||||
name = "Lens";
|
||||
rateAccuracy = "1m";
|
||||
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService | void> {
|
||||
try {
|
||||
const resp = await client.readNamespacedService("prometheus", "lens-metrics");
|
||||
const service = resp.body;
|
||||
@ -44,7 +44,7 @@ export class PrometheusLens implements PrometheusProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery {
|
||||
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery | void {
|
||||
switch(opts.category) {
|
||||
case "cluster":
|
||||
return {
|
||||
|
||||
@ -19,8 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry";
|
||||
import { CoreV1Api, V1Service } from "@kubernetes/client-node";
|
||||
import type { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry";
|
||||
import type { CoreV1Api, V1Service } from "@kubernetes/client-node";
|
||||
import logger from "../logger";
|
||||
|
||||
export class PrometheusOperator implements PrometheusProvider {
|
||||
@ -28,7 +28,7 @@ export class PrometheusOperator implements PrometheusProvider {
|
||||
id = "operator";
|
||||
name = "Prometheus Operator";
|
||||
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService | void> {
|
||||
try {
|
||||
let service: V1Service;
|
||||
|
||||
@ -54,7 +54,7 @@ export class PrometheusOperator implements PrometheusProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery {
|
||||
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery | void {
|
||||
switch(opts.category) {
|
||||
case "cluster":
|
||||
return {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { CoreV1Api } from "@kubernetes/client-node";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
|
||||
export type PrometheusClusterQuery = {
|
||||
memoryUsage: string;
|
||||
@ -83,8 +83,8 @@ export type PrometheusService = {
|
||||
export interface PrometheusProvider {
|
||||
id: string;
|
||||
name: string;
|
||||
getQueries(opts: PrometheusQueryOpts): PrometheusQuery;
|
||||
getPrometheusService(client: CoreV1Api): Promise<PrometheusService>;
|
||||
getQueries(opts: PrometheusQueryOpts): PrometheusQuery | void;
|
||||
getPrometheusService(client: CoreV1Api): Promise<PrometheusService | void>;
|
||||
}
|
||||
|
||||
export type PrometheusProviderList = {
|
||||
|
||||
@ -19,8 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry";
|
||||
import { CoreV1Api } from "@kubernetes/client-node";
|
||||
import type { PrometheusProvider, PrometheusQueryOpts, PrometheusQuery, PrometheusService } from "./provider-registry";
|
||||
import type { CoreV1Api } from "@kubernetes/client-node";
|
||||
import logger from "../logger";
|
||||
|
||||
export class PrometheusStacklight implements PrometheusProvider {
|
||||
@ -28,7 +28,7 @@ export class PrometheusStacklight implements PrometheusProvider {
|
||||
name = "Stacklight";
|
||||
rateAccuracy = "1m";
|
||||
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService> {
|
||||
public async getPrometheusService(client: CoreV1Api): Promise<PrometheusService | void> {
|
||||
try {
|
||||
const resp = await client.readNamespacedService("prometheus-server", "stacklight");
|
||||
const service = resp.body;
|
||||
@ -44,7 +44,7 @@ export class PrometheusStacklight implements PrometheusProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery {
|
||||
public getQueries(opts: PrometheusQueryOpts): PrometheusQuery | void {
|
||||
switch(opts.category) {
|
||||
case "cluster":
|
||||
return {
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import logger from "../logger";
|
||||
import * as proto from "../../common/protocol-handler";
|
||||
import Url from "url-parse";
|
||||
import { LensExtension } from "../../extensions/lens-extension";
|
||||
import type { LensExtension } from "../../extensions/lens-extension";
|
||||
import { broadcastMessage } from "../../common/ipc";
|
||||
import { observable, when } from "mobx";
|
||||
|
||||
|
||||
@ -21,3 +21,5 @@
|
||||
|
||||
// Don't export the contents here
|
||||
// It will break the extension webpack
|
||||
|
||||
export default {};
|
||||
|
||||
@ -20,13 +20,13 @@
|
||||
*/
|
||||
|
||||
import net from "net";
|
||||
import http from "http";
|
||||
import type http from "http";
|
||||
import spdy from "spdy";
|
||||
import httpProxy from "http-proxy";
|
||||
import url from "url";
|
||||
import { apiPrefix, apiKubePrefix } from "../../common/vars";
|
||||
import { Router } from "../router";
|
||||
import { ContextHandler } from "../context-handler";
|
||||
import type { ContextHandler } from "../context-handler";
|
||||
import logger from "../logger";
|
||||
import { Singleton } from "../../common/utils";
|
||||
import { ClusterManager } from "../cluster-manager";
|
||||
@ -205,13 +205,13 @@ export class LensProxy extends Singleton {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ContextHandler): Promise<httpProxy.ServerOptions> {
|
||||
protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ContextHandler): Promise<httpProxy.ServerOptions | void> {
|
||||
if (req.url.startsWith(apiKubePrefix)) {
|
||||
delete req.headers.authorization;
|
||||
req.url = req.url.replace(apiKubePrefix, "");
|
||||
const isWatchRequest = req.url.includes("watch=");
|
||||
|
||||
return await contextHandler.getApiTarget(isWatchRequest);
|
||||
return contextHandler.getApiTarget(isWatchRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,8 +26,8 @@
|
||||
*/
|
||||
|
||||
import * as WebSocket from "ws";
|
||||
import http from "http";
|
||||
import net from "net";
|
||||
import type http from "http";
|
||||
import type net from "net";
|
||||
import url from "url";
|
||||
import { NodeShellSession, LocalShellSession } from "../shell-session";
|
||||
import { ClusterManager } from "../cluster-manager";
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import type { Cluster } from "./cluster";
|
||||
import { KubernetesObject } from "@kubernetes/client-node";
|
||||
import type { KubernetesObject } from "@kubernetes/client-node";
|
||||
import { exec } from "child_process";
|
||||
import fs from "fs";
|
||||
import * as yaml from "js-yaml";
|
||||
|
||||
@ -21,10 +21,10 @@
|
||||
|
||||
import Call from "@hapi/call";
|
||||
import Subtext from "@hapi/subtext";
|
||||
import http from "http";
|
||||
import type http from "http";
|
||||
import path from "path";
|
||||
import { readFile } from "fs-extra";
|
||||
import { Cluster } from "./cluster";
|
||||
import type { Cluster } from "./cluster";
|
||||
import { apiPrefix, appName, publicPath, isDevelopment, webpackDevServerPort } from "../common/vars";
|
||||
import { HelmApiRoute, KubeconfigRoute, MetricsRoute, PortForwardRoute, ResourceApplierApiRoute, VersionRoute } from "./routes";
|
||||
import logger from "./logger";
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { LensApiRequest } from "../router";
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { helmService } from "../helm/helm-service";
|
||||
import { respondJson, respondText } from "../utils/http-responses";
|
||||
import logger from "../logger";
|
||||
|
||||
@ -19,9 +19,9 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { LensApiRequest } from "../router";
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import { Cluster } from "../cluster";
|
||||
import type { Cluster } from "../cluster";
|
||||
import { CoreV1Api, V1Secret } from "@kubernetes/client-node";
|
||||
|
||||
function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster) {
|
||||
|
||||
@ -20,10 +20,10 @@
|
||||
*/
|
||||
|
||||
import _ from "lodash";
|
||||
import { LensApiRequest } from "../router";
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import { Cluster, ClusterMetadataKey } from "../cluster";
|
||||
import { ClusterPrometheusMetadata } from "../../common/cluster-store";
|
||||
import type { ClusterPrometheusMetadata } from "../../common/cluster-store";
|
||||
import logger from "../logger";
|
||||
import { getMetrics } from "../k8s-request";
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { LensApiRequest } from "../router";
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { spawn, ChildProcessWithoutNullStreams } from "child_process";
|
||||
import { Kubectl } from "../kubectl";
|
||||
import { shell } from "electron";
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { LensApiRequest } from "../router";
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson, respondText } from "../utils/http-responses";
|
||||
import { ResourceApplier } from "../resource-applier";
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { LensApiRequest } from "../router";
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import { getAppVersion } from "../../common/utils";
|
||||
|
||||
|
||||
@ -19,11 +19,11 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import * as WebSocket from "ws";
|
||||
import type * as WebSocket from "ws";
|
||||
import { v4 as uuid } from "uuid";
|
||||
import * as k8s from "@kubernetes/client-node";
|
||||
import { KubeConfig } from "@kubernetes/client-node";
|
||||
import { Cluster } from "../cluster";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import type { Cluster } from "../cluster";
|
||||
import { ShellOpenError, ShellSession } from "./shell-session";
|
||||
|
||||
export class NodeShellSession extends ShellSession {
|
||||
|
||||
@ -19,9 +19,9 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Cluster } from "../cluster";
|
||||
import type { Cluster } from "../cluster";
|
||||
import { Kubectl } from "../kubectl";
|
||||
import * as WebSocket from "ws";
|
||||
import type * as WebSocket from "ws";
|
||||
import shellEnv from "shell-env";
|
||||
import { app } from "electron";
|
||||
import { clearKubeconfigEnvVars } from "../utils/clear-kube-env-vars";
|
||||
|
||||
@ -25,7 +25,7 @@ import { Menu, Tray } from "electron";
|
||||
import { autorun } from "mobx";
|
||||
import { showAbout } from "./menu";
|
||||
import { checkForUpdates, isAutoUpdateEnabled } from "./app-updater";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import type { WindowManager } from "./window-manager";
|
||||
import { preferencesURL } from "../renderer/components/+preferences/preferences.route";
|
||||
import logger from "./logger";
|
||||
import { isDevelopment, isWindows, productName } from "../common/vars";
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Readable } from "stream";
|
||||
import type { Readable } from "stream";
|
||||
import URLParse from "url-parse";
|
||||
|
||||
interface GetPortArgs {
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import http from "http";
|
||||
import type http from "http";
|
||||
|
||||
export function respondJson(res: http.ServerResponse, content: any, status = 200) {
|
||||
respond(res, JSON.stringify(content), "application/json", status);
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
// Fix embedded kubeconfig paths under snap config
|
||||
|
||||
import { migration } from "../migration-wrapper";
|
||||
import { ClusterModel } from "../../common/cluster-store";
|
||||
import type { ClusterModel } from "../../common/cluster-store";
|
||||
import { getAppVersion } from "../../common/utils/app-version";
|
||||
import fs from "fs";
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
// Cleans up a store that had the state related data stored
|
||||
import { Hotbar } from "../../common/hotbar-store";
|
||||
import type { Hotbar } from "../../common/hotbar-store";
|
||||
import { ClusterStore } from "../../common/cluster-store";
|
||||
import { migration } from "../migration-wrapper";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
// Cleans up a store that had the state related data stored
|
||||
import { Hotbar } from "../../common/hotbar-store";
|
||||
import type { Hotbar } from "../../common/hotbar-store";
|
||||
import { migration } from "../migration-wrapper";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Config from "conf";
|
||||
import type Config from "conf";
|
||||
import { isTestEnv } from "../common/vars";
|
||||
|
||||
export interface MigrationOpts {
|
||||
|
||||
@ -22,11 +22,18 @@
|
||||
import { CatalogEntityRegistry } from "../catalog-entity-registry";
|
||||
import "../../../common/catalog-entities";
|
||||
import { catalogCategoryRegistry } from "../../../common/catalog/catalog-category-registry";
|
||||
import type { CatalogEntityData, CatalogEntityKindData } from "../catalog-entity";
|
||||
|
||||
class TestCatalogEntityRegistry extends CatalogEntityRegistry {
|
||||
replaceItems(items: Array<CatalogEntityData & CatalogEntityKindData>) {
|
||||
this.rawItems.replace(items);
|
||||
}
|
||||
}
|
||||
|
||||
describe("CatalogEntityRegistry", () => {
|
||||
describe("updateItems", () => {
|
||||
it("adds new catalog item", () => {
|
||||
const catalog = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
@ -42,7 +49,7 @@ describe("CatalogEntityRegistry", () => {
|
||||
spec: {}
|
||||
}];
|
||||
|
||||
catalog.updateItems(items);
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
|
||||
items.push({
|
||||
@ -60,12 +67,12 @@ describe("CatalogEntityRegistry", () => {
|
||||
spec: {}
|
||||
});
|
||||
|
||||
catalog.updateItems(items);
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("updates existing items", () => {
|
||||
const catalog = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
@ -81,19 +88,19 @@ describe("CatalogEntityRegistry", () => {
|
||||
spec: {}
|
||||
}];
|
||||
|
||||
catalog.updateItems(items);
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
expect(catalog.items[0].status.phase).toEqual("disconnected");
|
||||
|
||||
items[0].status.phase = "connected";
|
||||
|
||||
catalog.updateItems(items);
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
expect(catalog.items[0].status.phase).toEqual("connected");
|
||||
});
|
||||
|
||||
it("removes deleted items", () => {
|
||||
const catalog = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [
|
||||
{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
@ -125,11 +132,51 @@ describe("CatalogEntityRegistry", () => {
|
||||
}
|
||||
];
|
||||
|
||||
catalog.updateItems(items);
|
||||
catalog.replaceItems(items);
|
||||
items.splice(0, 1);
|
||||
catalog.updateItems(items);
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
expect(catalog.items[0].metadata.uid).toEqual("456");
|
||||
});
|
||||
});
|
||||
|
||||
describe("items", () => {
|
||||
it("does not return items without matching category", () => {
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [
|
||||
{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
metadata: {
|
||||
uid: "123",
|
||||
name: "foobar",
|
||||
source: "test",
|
||||
labels: {}
|
||||
},
|
||||
status: {
|
||||
phase: "disconnected"
|
||||
},
|
||||
spec: {}
|
||||
},
|
||||
{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "FooBar",
|
||||
metadata: {
|
||||
uid: "456",
|
||||
name: "barbaz",
|
||||
source: "test",
|
||||
labels: {}
|
||||
},
|
||||
status: {
|
||||
phase: "disconnected"
|
||||
},
|
||||
spec: {}
|
||||
}
|
||||
];
|
||||
|
||||
catalog.replaceItems(items);
|
||||
|
||||
expect(catalog.items.length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { CustomResourceDefinition } from "../endpoints";
|
||||
import { IKubeObjectMetadata } from "../kube-object";
|
||||
import type { IKubeObjectMetadata } from "../kube-object";
|
||||
|
||||
describe("Crds", () => {
|
||||
describe("getVersion", () => {
|
||||
|
||||
@ -19,26 +19,22 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { action, observable } from "mobx";
|
||||
import { broadcastMessage, subscribeToBroadcast } from "../../common/ipc";
|
||||
import { computed, observable } from "mobx";
|
||||
import { subscribeToBroadcast } from "../../common/ipc";
|
||||
import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegistry, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog";
|
||||
import "../../common/catalog-entities";
|
||||
import { iter } from "../utils";
|
||||
|
||||
export class CatalogEntityRegistry {
|
||||
@observable protected _items: CatalogEntity[] = observable.array([], { deep: true });
|
||||
protected rawItems = observable.array<CatalogEntityData & CatalogEntityKindData>([], { deep: true });
|
||||
@observable protected _activeEntity: CatalogEntity;
|
||||
|
||||
constructor(private categoryRegistry: CatalogCategoryRegistry) {}
|
||||
|
||||
init() {
|
||||
subscribeToBroadcast("catalog:items", (ev, items: (CatalogEntityData & CatalogEntityKindData)[]) => {
|
||||
this.updateItems(items);
|
||||
this.rawItems.replace(items);
|
||||
});
|
||||
broadcastMessage("catalog:broadcast");
|
||||
}
|
||||
|
||||
@action updateItems(items: (CatalogEntityData & CatalogEntityKindData)[]) {
|
||||
this._items = items.map(data => this.categoryRegistry.getEntityForData(data));
|
||||
}
|
||||
|
||||
set activeEntity(entity: CatalogEntity) {
|
||||
@ -49,23 +45,27 @@ export class CatalogEntityRegistry {
|
||||
return this._activeEntity;
|
||||
}
|
||||
|
||||
get items() {
|
||||
return this._items;
|
||||
@computed get items() {
|
||||
return Array.from(iter.filterMap(this.rawItems, rawItem => this.categoryRegistry.getEntityForData(rawItem)));
|
||||
}
|
||||
|
||||
@computed get entities(): Map<string, CatalogEntity> {
|
||||
return new Map(this.items.map(item => [item.metadata.uid, item]));
|
||||
}
|
||||
|
||||
getById(id: string) {
|
||||
return this._items.find((entity) => entity.metadata.uid === id);
|
||||
return this.entities.get(id);
|
||||
}
|
||||
|
||||
getItemsForApiKind<T extends CatalogEntity>(apiVersion: string, kind: string): T[] {
|
||||
const items = this._items.filter((item) => item.apiVersion === apiVersion && item.kind === kind);
|
||||
const items = this.items.filter((item) => item.apiVersion === apiVersion && item.kind === kind);
|
||||
|
||||
return items as T[];
|
||||
}
|
||||
|
||||
getItemsForCategory<T extends CatalogEntity>(category: CatalogCategory): T[] {
|
||||
const supportedVersions = category.spec.versions.map((v) => `${category.spec.group}/${v.name}`);
|
||||
const items = this._items.filter((item) => supportedVersions.includes(item.apiVersion) && item.kind === category.spec.names.kind);
|
||||
const items = this.items.filter((item) => supportedVersions.includes(item.apiVersion) && item.kind === category.spec.names.kind);
|
||||
|
||||
return items as T[];
|
||||
}
|
||||
|
||||
@ -21,18 +21,17 @@
|
||||
|
||||
import { navigate } from "../navigation";
|
||||
import { commandRegistry } from "../../extensions/registries";
|
||||
import { CatalogEntity } from "../../common/catalog";
|
||||
import type { CatalogEntity } from "../../common/catalog";
|
||||
|
||||
export {
|
||||
CatalogCategory,
|
||||
CatalogEntity,
|
||||
export { CatalogCategory, CatalogEntity } from "../../common/catalog";
|
||||
export type {
|
||||
CatalogEntityData,
|
||||
CatalogEntityKindData,
|
||||
CatalogEntityActionContext,
|
||||
CatalogEntityAddMenuContext,
|
||||
CatalogEntityAddMenu,
|
||||
CatalogEntityContextMenu,
|
||||
CatalogEntityContextMenuContext
|
||||
CatalogEntityContextMenuContext,
|
||||
} from "../../common/catalog";
|
||||
|
||||
export const catalogEntityRunContext = {
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeJsonApiData } from "../kube-json-api";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
import { autobind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import moment from "moment";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { IPodContainer } from "./pods.api";
|
||||
import type { IPodContainer } from "./pods.api";
|
||||
import { formatDuration } from "../../utils/formatDuration";
|
||||
import { autobind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import get from "lodash/get";
|
||||
import { IPodContainer } from "./pods.api";
|
||||
import type { IPodContainer } from "./pods.api";
|
||||
import { IAffinity, WorkloadKubeObject } from "../workload-kube-object";
|
||||
import { autobind } from "../../utils";
|
||||
import { KubeApi } from "../kube-api";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user