mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fixing lint issue
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
0377642cda
commit
f432994bd0
2
Makefile
2
Makefile
@ -35,7 +35,7 @@ dev: binaries/client build-extensions static/build/LensDev.html
|
||||
yarn dev
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
lint: node_modules
|
||||
yarn lint
|
||||
|
||||
.PHONY: release-version
|
||||
|
||||
36
extensions/.eslintrc.js
Normal file
36
extensions/.eslintrc.js
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
"overrides": [
|
||||
{
|
||||
files: [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
rules: {
|
||||
"import/no-unresolved": ["error", {
|
||||
ignore: ["@k8slens/extensions"]
|
||||
}],
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kube-object-event-status",
|
||||
"version": "0.1.0",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-metrics-cluster-feature",
|
||||
"version": "0.1.0",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
2
extensions/node-menu/package-lock.json
generated
2
extensions/node-menu/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-node-menu",
|
||||
"version": "0.1.0",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
2
extensions/pod-menu/package-lock.json
generated
2
extensions/pod-menu/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lens-pod-menu",
|
||||
"version": "0.1.0",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@ -25,9 +25,12 @@ import yaml from "js-yaml";
|
||||
import path from "path";
|
||||
import fse from "fs-extra";
|
||||
import { Cluster } from "../../main/cluster";
|
||||
import { ClusterId, ClusterStore, getClusterIdFromHost } from "../cluster-store";
|
||||
import { ClusterStore } from "../cluster-store";
|
||||
import { Console } from "console";
|
||||
import { stdout, stderr } from "process";
|
||||
import type { ClusterId } from "../cluster-types";
|
||||
import { getCustomKubeConfigPath } from "../utils";
|
||||
import { getClusterIdFromHost } from "../utils/cluster-id-url-parsing";
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
@ -57,7 +60,7 @@ users:
|
||||
`;
|
||||
|
||||
function embed(clusterId: ClusterId, contents: any): string {
|
||||
const absPath = ClusterStore.getCustomKubeConfigPath(clusterId);
|
||||
const absPath = getCustomKubeConfigPath(clusterId);
|
||||
|
||||
fse.ensureDirSync(path.dirname(absPath));
|
||||
fse.writeFileSync(absPath, contents, { encoding: "utf-8", mode: 0o600 });
|
||||
|
||||
@ -20,14 +20,15 @@
|
||||
*/
|
||||
|
||||
import { catalogCategoryRegistry } from "../catalog/catalog-category-registry";
|
||||
import { CatalogEntity, CatalogEntityActionContext, CatalogEntityAddMenuContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
||||
import { CatalogEntity, CatalogEntityActionContext, CatalogEntityAddMenuContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus , CatalogCategory, CatalogCategorySpec } from "../catalog";
|
||||
import { clusterActivateHandler, clusterDisconnectHandler } from "../cluster-ipc";
|
||||
import { ClusterStore } from "../cluster-store";
|
||||
import { requestMain } from "../ipc";
|
||||
import { productName } from "../vars";
|
||||
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
|
||||
|
||||
import { addClusterURL } from "../routes";
|
||||
import { app } from "electron";
|
||||
import { storedKubeConfigFolder } from "../utils";
|
||||
|
||||
export type KubernetesClusterPrometheusMetrics = {
|
||||
address?: {
|
||||
@ -109,7 +110,7 @@ export class KubernetesCluster extends CatalogEntity<CatalogEntityMetadata, Kube
|
||||
});
|
||||
}
|
||||
|
||||
if (this.metadata.labels["file"]?.startsWith(ClusterStore.storedKubeConfigFolder)) {
|
||||
if (this.metadata.labels["file"]?.startsWith(storedKubeConfigFolder())) {
|
||||
context.menuItems.push({
|
||||
title: "Delete",
|
||||
icon: "delete",
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
*/
|
||||
|
||||
import { action, computed, observable, makeObservable } from "mobx";
|
||||
import { Disposer, ExtendedMap } from "../utils";
|
||||
import { ExtendedMap } from "../utils";
|
||||
import type { Disposer } from "../utils";
|
||||
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
|
||||
|
||||
export class CatalogCategoryRegistry {
|
||||
|
||||
@ -19,107 +19,25 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import { app, ipcMain, ipcRenderer, remote, webFrame } from "electron";
|
||||
import { ipcMain, ipcRenderer, webFrame } from "electron";
|
||||
import { unlink } from "fs-extra";
|
||||
import { action, comparer, computed, makeObservable, observable, reaction } from "mobx";
|
||||
import { BaseStore } from "./base-store";
|
||||
import { Cluster, ClusterState } from "../main/cluster";
|
||||
import { Cluster } from "../main/cluster";
|
||||
import migrations from "../migrations/cluster-store";
|
||||
import * as uuid from "uuid";
|
||||
import logger from "../main/logger";
|
||||
import { appEventBus } from "./event-bus";
|
||||
import { ipcMainHandle, ipcMainOn, ipcRendererOn, requestMain } from "./ipc";
|
||||
import { disposer, noop, toJS } from "./utils";
|
||||
|
||||
export interface ClusterIconUpload {
|
||||
clusterId: string;
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface ClusterMetadata {
|
||||
[key: string]: string | number | boolean | object;
|
||||
}
|
||||
|
||||
export type ClusterPrometheusMetadata = {
|
||||
success?: boolean;
|
||||
provider?: string;
|
||||
autoDetected?: boolean;
|
||||
};
|
||||
import { disposer, getCustomKubeConfigPath, noop, toJS } from "./utils";
|
||||
import type { ClusterId, ClusterModel, ClusterState } from "./cluster-types";
|
||||
|
||||
export interface ClusterStoreModel {
|
||||
clusters?: ClusterModel[];
|
||||
}
|
||||
|
||||
export type ClusterId = string;
|
||||
|
||||
export interface UpdateClusterModel extends Omit<ClusterModel, "id"> {
|
||||
id?: ClusterId;
|
||||
}
|
||||
|
||||
export interface ClusterModel {
|
||||
/** Unique id for a cluster */
|
||||
id: ClusterId;
|
||||
|
||||
/** Path to cluster kubeconfig */
|
||||
kubeConfigPath: string;
|
||||
|
||||
/**
|
||||
* Workspace id
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
workspace?: string;
|
||||
|
||||
/** User context in kubeconfig */
|
||||
contextName?: string;
|
||||
|
||||
/** Preferences */
|
||||
preferences?: ClusterPreferences;
|
||||
|
||||
/** Metadata */
|
||||
metadata?: ClusterMetadata;
|
||||
|
||||
/** List of accessible namespaces */
|
||||
accessibleNamespaces?: string[];
|
||||
|
||||
/** @deprecated */
|
||||
kubeConfig?: string; // yaml
|
||||
}
|
||||
|
||||
export interface ClusterPreferences extends ClusterPrometheusPreferences {
|
||||
terminalCWD?: string;
|
||||
clusterName?: string;
|
||||
iconOrder?: number;
|
||||
icon?: string;
|
||||
httpsProxy?: string;
|
||||
hiddenMetrics?: string[];
|
||||
}
|
||||
|
||||
export interface ClusterPrometheusPreferences {
|
||||
prometheus?: {
|
||||
namespace: string;
|
||||
service: string;
|
||||
port: number;
|
||||
prefix: string;
|
||||
};
|
||||
prometheusProvider?: {
|
||||
type: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
private static StateChannel = "cluster:state";
|
||||
|
||||
static get storedKubeConfigFolder(): string {
|
||||
return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs");
|
||||
}
|
||||
|
||||
static getCustomKubeConfigPath(clusterId: ClusterId = uuid.v4()): string {
|
||||
return path.resolve(ClusterStore.storedKubeConfigFolder, clusterId);
|
||||
}
|
||||
|
||||
@observable clusters = observable.map<ClusterId, Cluster>();
|
||||
@observable removedClusters = observable.map<ClusterId, Cluster>();
|
||||
|
||||
@ -266,7 +184,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
this.clusters.delete(clusterId);
|
||||
|
||||
// remove only custom kubeconfigs (pasted as text)
|
||||
if (cluster.kubeConfigPath == ClusterStore.getCustomKubeConfigPath(clusterId)) {
|
||||
if (cluster.kubeConfigPath == getCustomKubeConfigPath(clusterId)) {
|
||||
await unlink(cluster.kubeConfigPath).catch(noop);
|
||||
}
|
||||
}
|
||||
@ -311,22 +229,3 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getClusterIdFromHost(host: string): ClusterId | undefined {
|
||||
// e.g host == "%clusterId.localhost:45345"
|
||||
const subDomains = host.split(":")[0].split(".");
|
||||
|
||||
return subDomains.slice(-2, -1)[0]; // ClusterId or undefined
|
||||
}
|
||||
|
||||
export function getClusterFrameUrl(clusterId: ClusterId) {
|
||||
return `//${clusterId}.${location.host}`;
|
||||
}
|
||||
|
||||
export function getHostedClusterId() {
|
||||
return getClusterIdFromHost(location.host);
|
||||
}
|
||||
|
||||
export function getHostedCluster(): Cluster {
|
||||
return ClusterStore.getInstance().getById(getHostedClusterId());
|
||||
}
|
||||
|
||||
161
src/common/cluster-types.ts
Normal file
161
src/common/cluster-types.ts
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* JSON serializable metadata type
|
||||
*/
|
||||
export type ClusterMetadata = Record<string, string | number | boolean | object>;
|
||||
|
||||
/**
|
||||
* Metadata for cluster's prometheus settings
|
||||
*/
|
||||
export interface ClusterPrometheusMetadata {
|
||||
success?: boolean;
|
||||
provider?: string;
|
||||
autoDetected?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A ClusterId is an opaque string
|
||||
*/
|
||||
export type ClusterId = string;
|
||||
|
||||
/**
|
||||
* The fields that are used for updating a cluster instance
|
||||
*/
|
||||
export type UpdateClusterModel = Omit<ClusterModel, "id">;
|
||||
|
||||
/**
|
||||
* The model for passing cluster data around, including to disk
|
||||
*/
|
||||
export interface ClusterModel {
|
||||
/** Unique id for a cluster */
|
||||
id: ClusterId;
|
||||
|
||||
/** Path to cluster kubeconfig */
|
||||
kubeConfigPath: string;
|
||||
|
||||
/**
|
||||
* Workspace id
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
workspace?: string;
|
||||
|
||||
/** User context in kubeconfig */
|
||||
contextName?: string;
|
||||
|
||||
/** Preferences */
|
||||
preferences?: ClusterPreferences;
|
||||
|
||||
/** Metadata */
|
||||
metadata?: ClusterMetadata;
|
||||
|
||||
/** List of accessible namespaces */
|
||||
accessibleNamespaces?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The complete set of cluster settings or preferences
|
||||
*/
|
||||
export interface ClusterPreferences extends ClusterPrometheusPreferences {
|
||||
terminalCWD?: string;
|
||||
clusterName?: string;
|
||||
iconOrder?: number;
|
||||
icon?: string;
|
||||
httpsProxy?: string;
|
||||
hiddenMetrics?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A cluster's prometheus settings (a subset of cluster settings)
|
||||
*/
|
||||
export interface ClusterPrometheusPreferences {
|
||||
prometheus?: {
|
||||
namespace: string;
|
||||
service: string;
|
||||
port: number;
|
||||
prefix: string;
|
||||
};
|
||||
prometheusProvider?: {
|
||||
type: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The options for the status of connection attempts to a cluster
|
||||
*/
|
||||
export enum ClusterStatus {
|
||||
AccessGranted = 2,
|
||||
AccessDenied = 1,
|
||||
Offline = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* The OpenLens known static metadata keys
|
||||
*/
|
||||
export enum ClusterMetadataKey {
|
||||
VERSION = "version",
|
||||
CLUSTER_ID = "id",
|
||||
DISTRIBUTION = "distribution",
|
||||
NODES_COUNT = "nodes",
|
||||
LAST_SEEN = "lastSeen",
|
||||
PROMETHEUS = "prometheus"
|
||||
}
|
||||
|
||||
/**
|
||||
* A shorthand enum for resource types that have metrics attached to them via OpenLens metrics stack
|
||||
*/
|
||||
export enum ClusterMetricsResourceType {
|
||||
Cluster = "Cluster",
|
||||
Node = "Node",
|
||||
Pod = "Pod",
|
||||
Deployment = "Deployment",
|
||||
StatefulSet = "StatefulSet",
|
||||
Container = "Container",
|
||||
Ingress = "Ingress",
|
||||
VolumeClaim = "VolumeClaim",
|
||||
ReplicaSet = "ReplicaSet",
|
||||
DaemonSet = "DaemonSet",
|
||||
}
|
||||
|
||||
/**
|
||||
* The arguments for requesting to refresh a cluster's metadata
|
||||
*/
|
||||
export interface ClusterRefreshOptions {
|
||||
refreshMetadata?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* The data representing a cluster's state, for passing between main and renderer
|
||||
*/
|
||||
export interface ClusterState {
|
||||
apiUrl: string;
|
||||
online: boolean;
|
||||
disconnected: boolean;
|
||||
accessible: boolean;
|
||||
ready: boolean;
|
||||
failureReason: string;
|
||||
isAdmin: boolean;
|
||||
allowedNamespaces: string[]
|
||||
allowedResources: string[]
|
||||
isGlobalWatchEnabled: boolean;
|
||||
}
|
||||
@ -19,7 +19,6 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { getHostedCluster } from "./cluster-store";
|
||||
|
||||
export type KubeResource =
|
||||
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" |
|
||||
@ -73,18 +72,3 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
|
||||
// TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7)
|
||||
export const apiResources: KubeApiResource[] = Object.entries(apiResourceRecord)
|
||||
.map(([apiName, data]) => ({ apiName: apiName as KubeResource, ...data }));
|
||||
|
||||
export function isAllowedResource(resources: KubeResource | KubeResource[]) {
|
||||
if (!Array.isArray(resources)) {
|
||||
resources = [resources];
|
||||
}
|
||||
const { allowedResources = [] } = getHostedCluster() || {};
|
||||
|
||||
for (const resource of resources) {
|
||||
if (!allowedResources.includes(resource)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -25,12 +25,11 @@ import semver from "semver";
|
||||
import { action, computed, observable, reaction, makeObservable } from "mobx";
|
||||
import moment from "moment-timezone";
|
||||
import { BaseStore } from "./base-store";
|
||||
import migrations from "../migrations/user-store";
|
||||
import migrations, { fileNameMigration } from "../migrations/user-store";
|
||||
import { getAppVersion } from "./utils/app-version";
|
||||
import { appEventBus } from "./event-bus";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import { fileNameMigration } from "../migrations/user-store";
|
||||
import { ObservableToggleSet, toJS } from "../renderer/utils";
|
||||
|
||||
export interface UserStoreModel {
|
||||
|
||||
39
src/common/utils/allowed-resource.ts
Normal file
39
src/common/utils/allowed-resource.ts
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { ClusterStore } from "../cluster-store";
|
||||
import type { KubeResource } from "../rbac";
|
||||
import { getHostedClusterId } from "./cluster-id-url-parsing";
|
||||
|
||||
export function isAllowedResource(resources: KubeResource | KubeResource[]) {
|
||||
if (!Array.isArray(resources)) {
|
||||
resources = [resources];
|
||||
}
|
||||
const { allowedResources = [] } = ClusterStore.getInstance().getById(getHostedClusterId()) || {};
|
||||
|
||||
for (const resource of resources) {
|
||||
if (!allowedResources.includes(resource)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
50
src/common/utils/cluster-id-url-parsing.ts
Normal file
50
src/common/utils/cluster-id-url-parsing.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { ClusterId } from "../cluster-types";
|
||||
|
||||
/**
|
||||
* Grab the `ClusterId` out of a host that was generated by `getClusterFrameUrl`, or nothing
|
||||
* @param host The host section of a URL
|
||||
* @returns The `ClusterId` part of the host, or `undefined`
|
||||
*/
|
||||
export function getClusterIdFromHost(host: string): ClusterId | undefined {
|
||||
// e.g host == "%clusterId.localhost:45345"
|
||||
const subDomains = host.split(":")[0].split(".");
|
||||
|
||||
return subDomains.slice(-2, -1)[0]; // ClusterId or undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OpenLens backend routing host for a given `ClusterId`
|
||||
* @param clusterId The ID to put in front of the current host
|
||||
* @returns a new URL host section
|
||||
*/
|
||||
export function getClusterFrameUrl(clusterId: ClusterId) {
|
||||
return `//${clusterId}.${location.host}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result of `getClusterIdFromHost` from the current `location.host`
|
||||
*/
|
||||
export function getHostedClusterId(): ClusterId | undefined {
|
||||
return getClusterIdFromHost(location.host);
|
||||
}
|
||||
@ -30,6 +30,7 @@ export * from "./autobind";
|
||||
export * from "./base64";
|
||||
export * from "./camelCase";
|
||||
export * from "./cloneJson";
|
||||
export * from "./cluster-id-url-parsing";
|
||||
export * from "./debouncePromise";
|
||||
export * from "./defineGlobal";
|
||||
export * from "./delay";
|
||||
@ -39,6 +40,7 @@ export * from "./escapeRegExp";
|
||||
export * from "./extended-map";
|
||||
export * from "./getRandId";
|
||||
export * from "./hash-set";
|
||||
export * from "./local-kubeconfig";
|
||||
export * from "./n-fircate";
|
||||
export * from "./openExternal";
|
||||
export * from "./paths";
|
||||
|
||||
33
src/common/utils/local-kubeconfig.ts
Normal file
33
src/common/utils/local-kubeconfig.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { app, remote } from "electron";
|
||||
import path from "path";
|
||||
import * as uuid from "uuid";
|
||||
import type { ClusterId } from "../cluster-types";
|
||||
|
||||
export function storedKubeConfigFolder(): string {
|
||||
return path.resolve((app || remote.app).getPath("userData"), "kubeconfigs");
|
||||
}
|
||||
|
||||
export function getCustomKubeConfigPath(clusterId: ClusterId = uuid.v4()): string {
|
||||
return path.resolve(storedKubeConfigFolder(), clusterId);
|
||||
}
|
||||
@ -35,8 +35,7 @@ import { ExtensionsStore } from "./extensions-store";
|
||||
import { ExtensionLoader } from "./extension-loader";
|
||||
import type { LensExtensionId, LensExtensionManifest } from "./lens-extension";
|
||||
import semver from "semver";
|
||||
import { appSemVer } from "../common/vars";
|
||||
import { isProduction } from "../common/vars";
|
||||
import { appSemVer, isProduction } from "../common/vars";
|
||||
|
||||
export interface InstalledExtension {
|
||||
id: LensExtensionId;
|
||||
|
||||
@ -24,9 +24,10 @@ import { EventEmitter } from "events";
|
||||
import { isEqual } from "lodash";
|
||||
import { action, computed, makeObservable, observable, reaction, when } from "mobx";
|
||||
import path from "path";
|
||||
import { getHostedCluster } from "../common/cluster-store";
|
||||
import { ClusterStore } from "../common/cluster-store";
|
||||
import { broadcastMessage, ipcMainOn, ipcRendererOn, requestMain, ipcMainHandle } from "../common/ipc";
|
||||
import { Disposer, Singleton, toJS } from "../common/utils";
|
||||
import { getHostedClusterId } from "../common/utils/cluster-id-url-parsing";
|
||||
import logger from "../main/logger";
|
||||
import type { InstalledExtension } from "./extension-discovery";
|
||||
import { ExtensionsStore } from "./extensions-store";
|
||||
@ -270,7 +271,7 @@ export class ExtensionLoader extends Singleton {
|
||||
|
||||
loadOnClusterRenderer() {
|
||||
logger.debug(`${logModule}: load on cluster renderer (dashboard)`);
|
||||
const cluster = getHostedCluster();
|
||||
const cluster = ClusterStore.getInstance().getById(getHostedClusterId());
|
||||
|
||||
this.autoInitExtensions(async (extension: LensRendererExtension) => {
|
||||
if ((await extension.isEnabledForCluster(cluster)) === false) {
|
||||
|
||||
@ -60,7 +60,11 @@ export * from "../../renderer/components/chart/bar-chart";
|
||||
export * from "../../renderer/components/chart/pie-chart";
|
||||
|
||||
// kube helpers
|
||||
export * from "../../renderer/components/kube-object";
|
||||
export * from "../../renderer/components/kube-details";
|
||||
export * from "../../renderer/components/kube-object-details";
|
||||
export * from "../../renderer/components/kube-object-list-layout";
|
||||
export * from "../../renderer/components/kube-object-menu";
|
||||
export * from "../../renderer/components/kube-object-meta";
|
||||
export * from "../../renderer/components/+events/kube-event-details";
|
||||
|
||||
// specific exports
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export { isAllowedResource } from "../../common/rbac";
|
||||
export { isAllowedResource } from "../../common/utils/allowed-resource";
|
||||
export { ResourceStack } from "../../common/k8s/resource-stack";
|
||||
export { apiManager } from "../../renderer/api/api-manager";
|
||||
export { KubeObjectStore } from "../../renderer/kube-object.store";
|
||||
|
||||
@ -23,7 +23,7 @@ import { navigation, PageParam, PageParamInit } 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 { hideDetails, showDetails, getDetailsUrl } from "../../renderer/components/kube-details";
|
||||
export type { URLParams } from "../../common/utils/buildUrl";
|
||||
|
||||
export function createPageParam<V>(init: PageParamInit<V>) {
|
||||
|
||||
@ -26,16 +26,17 @@ import { watch } from "chokidar";
|
||||
import fs from "fs";
|
||||
import fse from "fs-extra";
|
||||
import type stream from "stream";
|
||||
import { Disposer, ExtendedObservableMap, iter, Singleton } from "../../common/utils";
|
||||
import { Disposer, ExtendedObservableMap, iter, Singleton, storedKubeConfigFolder } from "../../common/utils";
|
||||
import logger from "../logger";
|
||||
import type { KubeConfig } from "@kubernetes/client-node";
|
||||
import { loadConfigFromString, splitConfig } from "../../common/kube-helpers";
|
||||
import { Cluster } from "../cluster";
|
||||
import { catalogEntityFromCluster } from "../cluster-manager";
|
||||
import { UserStore } from "../../common/user-store";
|
||||
import { ClusterStore, UpdateClusterModel } from "../../common/cluster-store";
|
||||
import { ClusterStore } from "../../common/cluster-store";
|
||||
import { createHash } from "crypto";
|
||||
import { homedir } from "os";
|
||||
import type { UpdateClusterModel } from "../../common/cluster-types";
|
||||
|
||||
const logPrefix = "[KUBECONFIG-SYNC]:";
|
||||
|
||||
@ -70,7 +71,7 @@ export class KubeconfigSyncManager extends Singleton {
|
||||
)));
|
||||
|
||||
// This must be done so that c&p-ed clusters are visible
|
||||
this.startNewSync(ClusterStore.storedKubeConfigFolder);
|
||||
this.startNewSync(storedKubeConfigFolder());
|
||||
|
||||
for (const filePath of UserStore.getInstance().syncKubeconfigEntries.keys()) {
|
||||
this.startNewSync(filePath);
|
||||
@ -198,7 +199,7 @@ export function computeDiff(contents: string, source: RootSource, filePath: stri
|
||||
|
||||
const entity = catalogEntityFromCluster(cluster);
|
||||
|
||||
if (!filePath.startsWith(ClusterStore.storedKubeConfigFolder)) {
|
||||
if (!filePath.startsWith(storedKubeConfigFolder())) {
|
||||
entity.metadata.labels.file = filePath.replace(homedir(), "~");
|
||||
}
|
||||
source.set(contextName, [cluster, entity]);
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { createHash } from "crypto";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
import { ClusterMetadataKey } from "../../common/cluster-types";
|
||||
|
||||
export class ClusterIdDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.CLUSTER_ID;
|
||||
|
||||
@ -20,20 +20,18 @@
|
||||
*/
|
||||
|
||||
import { observable } from "mobx";
|
||||
import type { ClusterMetadata } from "../../common/cluster-store";
|
||||
import type { ClusterMetadata } from "../../common/cluster-types";
|
||||
import { Singleton } from "../../common/utils";
|
||||
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";
|
||||
import { NodesCountDetector } from "./nodes-count-detector";
|
||||
import { VersionDetector } from "./version-detector";
|
||||
|
||||
export class DetectorRegistry {
|
||||
export class DetectorRegistry extends Singleton {
|
||||
registry = observable.array<typeof BaseClusterDetector>([], { deep: false });
|
||||
|
||||
add(detectorClass: typeof BaseClusterDetector) {
|
||||
add(detectorClass: typeof BaseClusterDetector): this {
|
||||
this.registry.push(detectorClass);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async detectForCluster(cluster: Cluster): Promise<ClusterMetadata> {
|
||||
@ -63,10 +61,3 @@ export class DetectorRegistry {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
export const detectorRegistry = new DetectorRegistry();
|
||||
detectorRegistry.add(ClusterIdDetector);
|
||||
detectorRegistry.add(LastSeenDetector);
|
||||
detectorRegistry.add(VersionDetector);
|
||||
detectorRegistry.add(DistributionDetector);
|
||||
detectorRegistry.add(NodesCountDetector);
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
import { ClusterMetadataKey } from "../../common/cluster-types";
|
||||
|
||||
export class DistributionDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.DISTRIBUTION;
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
import { ClusterMetadataKey } from "../../common/cluster-types";
|
||||
|
||||
export class LastSeenDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.LAST_SEEN;
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
import { ClusterMetadataKey } from "../../common/cluster-types";
|
||||
|
||||
export class NodesCountDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.NODES_COUNT;
|
||||
|
||||
@ -19,8 +19,8 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { ClusterMetadataKey } from "../../common/cluster-types";
|
||||
import { BaseClusterDetector } from "./base-cluster-detector";
|
||||
import { ClusterMetadataKey } from "../cluster";
|
||||
|
||||
export class VersionDetector extends BaseClusterDetector {
|
||||
key = ClusterMetadataKey.VERSION;
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import "../common/cluster-ipc";
|
||||
import type http from "http";
|
||||
import { action, autorun, makeObservable, reaction } from "mobx";
|
||||
import { ClusterStore, getClusterIdFromHost } from "../common/cluster-store";
|
||||
import { ClusterStore } from "../common/cluster-store";
|
||||
import type { Cluster } from "./cluster";
|
||||
import logger from "./logger";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
@ -30,10 +30,9 @@ import { Singleton } from "../common/utils";
|
||||
import { catalogEntityRegistry } from "./catalog";
|
||||
import { KubernetesCluster, KubernetesClusterPrometheusMetrics } from "../common/catalog-entities/kubernetes-cluster";
|
||||
import { ipcMainOn } from "../common/ipc";
|
||||
import { getClusterIdFromHost } from "../common/utils/cluster-id-url-parsing";
|
||||
|
||||
export class ClusterManager extends Singleton {
|
||||
private store = ClusterStore.getInstance();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeObservable(this);
|
||||
@ -43,8 +42,8 @@ export class ClusterManager extends Singleton {
|
||||
private bindEvents() {
|
||||
// reacting to every cluster's state change and total amount of items
|
||||
reaction(
|
||||
() => this.store.clustersList.map(c => c.getState()),
|
||||
() => this.updateCatalog(this.store.clustersList),
|
||||
() => ClusterStore.getInstance().clustersList.map(c => c.getState()),
|
||||
() => this.updateCatalog(ClusterStore.getInstance().clustersList),
|
||||
{ fireImmediately: true, }
|
||||
);
|
||||
|
||||
@ -54,14 +53,14 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
// auto-stop removed clusters
|
||||
autorun(() => {
|
||||
const removedClusters = Array.from(this.store.removedClusters.values());
|
||||
const removedClusters = Array.from(ClusterStore.getInstance().removedClusters.values());
|
||||
|
||||
if (removedClusters.length > 0) {
|
||||
const meta = removedClusters.map(cluster => cluster.getMeta());
|
||||
|
||||
logger.info(`[CLUSTER-MANAGER]: removing clusters`, meta);
|
||||
removedClusters.forEach(cluster => cluster.disconnect());
|
||||
this.store.removedClusters.clear();
|
||||
ClusterStore.getInstance().removedClusters.clear();
|
||||
}
|
||||
}, {
|
||||
delay: 250
|
||||
@ -106,10 +105,10 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
@action syncClustersFromCatalog(entities: KubernetesCluster[]) {
|
||||
for (const entity of entities) {
|
||||
const cluster = this.store.getById(entity.metadata.uid);
|
||||
const cluster = ClusterStore.getInstance().getById(entity.metadata.uid);
|
||||
|
||||
if (!cluster) {
|
||||
this.store.addCluster({
|
||||
ClusterStore.getInstance().addCluster({
|
||||
id: entity.metadata.uid,
|
||||
preferences: {
|
||||
clusterName: entity.metadata.name
|
||||
@ -128,7 +127,7 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
protected onNetworkOffline = () => {
|
||||
logger.info("[CLUSTER-MANAGER]: network is offline");
|
||||
this.store.clustersList.forEach((cluster) => {
|
||||
ClusterStore.getInstance().clustersList.forEach((cluster) => {
|
||||
if (!cluster.disconnected) {
|
||||
cluster.online = false;
|
||||
cluster.accessible = false;
|
||||
@ -139,7 +138,7 @@ export class ClusterManager extends Singleton {
|
||||
|
||||
protected onNetworkOnline = () => {
|
||||
logger.info("[CLUSTER-MANAGER]: network is online");
|
||||
this.store.clustersList.forEach((cluster) => {
|
||||
ClusterStore.getInstance().clustersList.forEach((cluster) => {
|
||||
if (!cluster.disconnected) {
|
||||
cluster.refreshConnectionStatus().catch((e) => e);
|
||||
}
|
||||
@ -147,7 +146,7 @@ export class ClusterManager extends Singleton {
|
||||
};
|
||||
|
||||
stop() {
|
||||
this.store.clusters.forEach((cluster: Cluster) => {
|
||||
ClusterStore.getInstance().clusters.forEach((cluster: Cluster) => {
|
||||
cluster.disconnect();
|
||||
});
|
||||
}
|
||||
@ -159,18 +158,18 @@ export class ClusterManager extends Singleton {
|
||||
if (req.headers.host.startsWith("127.0.0.1")) {
|
||||
const clusterId = req.url.split("/")[1];
|
||||
|
||||
cluster = this.store.getById(clusterId);
|
||||
cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
// we need to swap path prefix so that request is proxied to kube api
|
||||
req.url = req.url.replace(`/${clusterId}`, apiKubePrefix);
|
||||
}
|
||||
} else if (req.headers["x-cluster-id"]) {
|
||||
cluster = this.store.getById(req.headers["x-cluster-id"].toString());
|
||||
cluster = ClusterStore.getInstance().getById(req.headers["x-cluster-id"].toString());
|
||||
} else {
|
||||
const clusterId = getClusterIdFromHost(req.headers.host);
|
||||
|
||||
cluster = this.store.getById(clusterId);
|
||||
cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
}
|
||||
|
||||
return cluster;
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
*/
|
||||
|
||||
import { ipcMain } from "electron";
|
||||
import type { ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel } from "../common/cluster-store";
|
||||
import type { ClusterId, ClusterMetadata, ClusterMetricsResourceType, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, ClusterRefreshOptions, ClusterState, UpdateClusterModel } from "../common/cluster-types";
|
||||
import { ClusterStatus } from "../common/cluster-types";
|
||||
import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx";
|
||||
import { broadcastMessage, ClusterListNamespaceForbiddenChannel } from "../common/ipc";
|
||||
import { ContextHandler } from "./context-handler";
|
||||
@ -31,61 +32,16 @@ import { loadConfigFromFile, loadConfigFromFileSync, validateKubeConfig } from "
|
||||
import { apiResourceRecord, apiResources, KubeApiResource, KubeResource } from "../common/rbac";
|
||||
import logger from "./logger";
|
||||
import { VersionDetector } from "./cluster-detectors/version-detector";
|
||||
import { detectorRegistry } from "./cluster-detectors/detector-registry";
|
||||
import { DetectorRegistry } from "./cluster-detectors/detector-registry";
|
||||
import plimit from "p-limit";
|
||||
import { toJS } from "../common/utils";
|
||||
|
||||
export enum ClusterStatus {
|
||||
AccessGranted = 2,
|
||||
AccessDenied = 1,
|
||||
Offline = 0
|
||||
}
|
||||
|
||||
export enum ClusterMetadataKey {
|
||||
VERSION = "version",
|
||||
CLUSTER_ID = "id",
|
||||
DISTRIBUTION = "distribution",
|
||||
NODES_COUNT = "nodes",
|
||||
LAST_SEEN = "lastSeen",
|
||||
PROMETHEUS = "prometheus"
|
||||
}
|
||||
|
||||
export enum ClusterMetricsResourceType {
|
||||
Cluster = "Cluster",
|
||||
Node = "Node",
|
||||
Pod = "Pod",
|
||||
Deployment = "Deployment",
|
||||
StatefulSet = "StatefulSet",
|
||||
Container = "Container",
|
||||
Ingress = "Ingress",
|
||||
VolumeClaim = "VolumeClaim",
|
||||
ReplicaSet = "ReplicaSet",
|
||||
DaemonSet = "DaemonSet",
|
||||
}
|
||||
|
||||
export type ClusterRefreshOptions = {
|
||||
refreshMetadata?: boolean
|
||||
};
|
||||
|
||||
export interface ClusterState {
|
||||
apiUrl: string;
|
||||
online: boolean;
|
||||
disconnected: boolean;
|
||||
accessible: boolean;
|
||||
ready: boolean;
|
||||
failureReason: string;
|
||||
isAdmin: boolean;
|
||||
allowedNamespaces: string[]
|
||||
allowedResources: string[]
|
||||
isGlobalWatchEnabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cluster
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export class Cluster implements ClusterModel, ClusterState {
|
||||
export class Cluster implements ClusterState {
|
||||
/** Unique id for a cluster */
|
||||
public readonly id: ClusterId;
|
||||
/**
|
||||
@ -283,11 +239,9 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
/**
|
||||
* Update cluster data model
|
||||
*
|
||||
* @param model
|
||||
* @param model The data to update this instance with
|
||||
*/
|
||||
@action updateModel(model: UpdateClusterModel) {
|
||||
// Note: do not assign ID as that should never be updated
|
||||
|
||||
this.kubeConfigPath = model.kubeConfigPath;
|
||||
|
||||
if (model.workspace) {
|
||||
@ -432,7 +386,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
||||
@action
|
||||
async refreshMetadata() {
|
||||
logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta());
|
||||
const metadata = await detectorRegistry.detectForCluster(this);
|
||||
const metadata = await DetectorRegistry.getInstance().detectForCluster(this);
|
||||
const existingMetadata = this.metadata;
|
||||
|
||||
this.metadata = Object.assign(existingMetadata, metadata);
|
||||
|
||||
@ -21,13 +21,13 @@
|
||||
|
||||
import type { PrometheusProvider, PrometheusService } from "./prometheus/provider-registry";
|
||||
import { PrometheusProviderRegistry } from "./prometheus/provider-registry";
|
||||
import type { ClusterPrometheusPreferences } from "../common/cluster-store";
|
||||
import type { Cluster } from "./cluster";
|
||||
import type httpProxy from "http-proxy";
|
||||
import url, { UrlWithStringQuery } from "url";
|
||||
import { CoreV1Api } from "@kubernetes/client-node";
|
||||
import logger from "./logger";
|
||||
import { KubeAuthProxy } from "./kube-auth-proxy";
|
||||
import type { ClusterPrometheusPreferences } from "../common/cluster-types";
|
||||
|
||||
export class ContextHandler {
|
||||
public clusterUrl: UrlWithStringQuery;
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import * as tempy from "tempy";
|
||||
import tempy from "tempy";
|
||||
import fse from "fs-extra";
|
||||
import * as yaml from "js-yaml";
|
||||
import { promiseExec } from "../promise-exec";
|
||||
|
||||
@ -28,7 +28,7 @@ import * as LensExtensionsMainApi from "../extensions/main-api";
|
||||
import { app, autoUpdater, dialog, powerMonitor } from "electron";
|
||||
import { appName, isMac, productName } from "../common/vars";
|
||||
import path from "path";
|
||||
import { LensProxy } from "./proxy/lens-proxy";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { ClusterManager } from "./cluster-manager";
|
||||
import { shellSync } from "./shell-sync";
|
||||
@ -54,10 +54,14 @@ 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";
|
||||
import configurePackages from "../common/configure-packages";
|
||||
import { PrometheusProviderRegistry } from "./prometheus";
|
||||
import * as initializers from "./initializers";
|
||||
import { Router } from "./router";
|
||||
import { initMenu } from "./menu";
|
||||
import { initTray } from "./tray";
|
||||
import { DetectorRegistry } from "./cluster-detectors/detector-registry";
|
||||
import { kubeApiRequest, shellApiRequest } from "./proxy-functions";
|
||||
|
||||
const workingDir = path.join(app.getPath("appData"), appName);
|
||||
const cleanup = disposer();
|
||||
@ -146,7 +150,11 @@ app.on("ready", async () => {
|
||||
filesystemStore.load(),
|
||||
]);
|
||||
|
||||
const lensProxy = LensProxy.createInstance(handleWsUpgrade);
|
||||
const lensProxy = LensProxy.createInstance(new Router(), {
|
||||
getClusterForRequest: req => ClusterManager.getInstance().getClusterForRequest(req),
|
||||
kubeApiRequest,
|
||||
shellApiRequest,
|
||||
});
|
||||
|
||||
ClusterManager.createInstance();
|
||||
KubeconfigSyncManager.createInstance();
|
||||
@ -175,6 +183,9 @@ app.on("ready", async () => {
|
||||
app.exit();
|
||||
}
|
||||
|
||||
DetectorRegistry.createInstance();
|
||||
initializers.initClusterMetadataDetectors();
|
||||
|
||||
initializers.initRegistries();
|
||||
const extensionDiscovery = ExtensionDiscovery.createInstance();
|
||||
|
||||
@ -187,6 +198,11 @@ app.on("ready", async () => {
|
||||
|
||||
logger.info("🖥️ Starting WindowManager");
|
||||
const windowManager = WindowManager.createInstance();
|
||||
|
||||
cleanup.push(
|
||||
initMenu(windowManager),
|
||||
initTray(windowManager),
|
||||
);
|
||||
|
||||
installDeveloperTools();
|
||||
|
||||
|
||||
36
src/main/initializers/cluster-metadata-detectors.ts
Normal file
36
src/main/initializers/cluster-metadata-detectors.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { 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.getInstance()
|
||||
.add(ClusterIdDetector)
|
||||
.add(LastSeenDetector)
|
||||
.add(VersionDetector)
|
||||
.add(DistributionDetector)
|
||||
.add(NodesCountDetector);
|
||||
}
|
||||
@ -22,3 +22,4 @@
|
||||
export * from "./registries";
|
||||
export * from "./metrics-providers";
|
||||
export * from "./ipc";
|
||||
export * from "./cluster-metadata-detectors";
|
||||
|
||||
@ -23,7 +23,8 @@ import type { IpcMainInvokeEvent } from "electron";
|
||||
import type { KubernetesCluster } from "../../common/catalog-entities";
|
||||
import { clusterFrameMap } from "../../common/cluster-frames";
|
||||
import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHandler, clusterRefreshHandler, clusterDisconnectHandler, clusterKubectlApplyAllHandler, clusterKubectlDeleteAllHandler } from "../../common/cluster-ipc";
|
||||
import { ClusterId, ClusterStore } from "../../common/cluster-store";
|
||||
import { ClusterStore } from "../../common/cluster-store";
|
||||
import type { ClusterId } from "../../common/cluster-types";
|
||||
import { appEventBus } from "../../common/event-bus";
|
||||
import { ipcMainHandle } from "../../common/ipc";
|
||||
import { catalogEntityRegistry } from "../catalog";
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
import request, { RequestPromiseOptions } from "request-promise-native";
|
||||
import { apiKubePrefix } from "../common/vars";
|
||||
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
||||
import { LensProxy } from "./proxy/lens-proxy";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
import type { Cluster } from "./cluster";
|
||||
|
||||
export async function k8sRequest<T = any>(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise<T> {
|
||||
|
||||
@ -27,7 +27,7 @@ import path from "path";
|
||||
import fs from "fs-extra";
|
||||
import { dumpConfigYaml } from "../common/kube-helpers";
|
||||
import logger from "./logger";
|
||||
import { LensProxy } from "./proxy/lens-proxy";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
|
||||
export class KubeconfigManager {
|
||||
protected configDir = app.getPath("temp");
|
||||
|
||||
@ -19,33 +19,42 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import net from "net";
|
||||
import type net from "net";
|
||||
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 type { ContextHandler } from "../context-handler";
|
||||
import logger from "../logger";
|
||||
import { Singleton } from "../../common/utils";
|
||||
import { ClusterManager } from "../cluster-manager";
|
||||
import { apiPrefix, apiKubePrefix } from "../common/vars";
|
||||
import type { Router } from "./router";
|
||||
import type { ContextHandler } from "./context-handler";
|
||||
import logger from "./logger";
|
||||
import { Singleton } from "../common/utils";
|
||||
import type { Cluster } from "./cluster";
|
||||
import type { ProxyApiRequestArgs } from "./proxy-functions";
|
||||
|
||||
type WSUpgradeHandler = (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => void;
|
||||
type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | null;
|
||||
|
||||
export interface LensProxyFunctions {
|
||||
getClusterForRequest: GetClusterForRequest,
|
||||
shellApiRequest: (args: ProxyApiRequestArgs) => void;
|
||||
kubeApiRequest: (args: ProxyApiRequestArgs) => void;
|
||||
}
|
||||
|
||||
export class LensProxy extends Singleton {
|
||||
protected origin: string;
|
||||
protected proxyServer: http.Server;
|
||||
protected router = new Router();
|
||||
protected closed = false;
|
||||
protected retryCounters = new Map<string, number>();
|
||||
protected proxy = this.createProxy();
|
||||
protected getClusterForRequest: GetClusterForRequest;
|
||||
|
||||
public port: number;
|
||||
|
||||
constructor(handleWsUpgrade: WSUpgradeHandler) {
|
||||
constructor(protected router: Router, functions: LensProxyFunctions) {
|
||||
super();
|
||||
|
||||
const proxy = this.createProxy();
|
||||
const { shellApiRequest, kubeApiRequest } = functions;
|
||||
|
||||
this.getClusterForRequest = functions.getClusterForRequest;
|
||||
|
||||
this.proxyServer = spdy.createServer({
|
||||
spdy: {
|
||||
@ -53,16 +62,14 @@ export class LensProxy extends Singleton {
|
||||
protocols: ["http/1.1", "spdy/3.1"]
|
||||
}
|
||||
}, (req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
this.handleRequest(proxy, req, res);
|
||||
this.handleRequest(req, res);
|
||||
});
|
||||
|
||||
this.proxyServer
|
||||
.on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
|
||||
if (req.url.startsWith(`${apiPrefix}?`)) {
|
||||
handleWsUpgrade(req, socket, head);
|
||||
} else {
|
||||
this.handleProxyUpgrade(proxy, req, socket, head);
|
||||
}
|
||||
const isInternal = req.url.startsWith(`${apiPrefix}?`);
|
||||
|
||||
(isInternal ? shellApiRequest : kubeApiRequest)({ req, socket, head });
|
||||
});
|
||||
}
|
||||
|
||||
@ -103,58 +110,6 @@ export class LensProxy extends Singleton {
|
||||
this.closed = true;
|
||||
}
|
||||
|
||||
protected async handleProxyUpgrade(proxy: httpProxy, req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
|
||||
const cluster = ClusterManager.getInstance().getClusterForRequest(req);
|
||||
|
||||
if (cluster) {
|
||||
const proxyUrl = await cluster.contextHandler.resolveAuthProxyUrl() + req.url.replace(apiKubePrefix, "");
|
||||
const apiUrl = url.parse(cluster.apiUrl);
|
||||
const pUrl = url.parse(proxyUrl);
|
||||
const connectOpts = { port: parseInt(pUrl.port), host: pUrl.hostname };
|
||||
const proxySocket = new net.Socket();
|
||||
|
||||
proxySocket.connect(connectOpts, () => {
|
||||
proxySocket.write(`${req.method} ${pUrl.path} HTTP/1.1\r\n`);
|
||||
proxySocket.write(`Host: ${apiUrl.host}\r\n`);
|
||||
|
||||
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
||||
const key = req.rawHeaders[i];
|
||||
|
||||
if (key !== "Host" && key !== "Authorization") {
|
||||
proxySocket.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i+1]}\r\n`);
|
||||
}
|
||||
}
|
||||
proxySocket.write("\r\n");
|
||||
proxySocket.write(head);
|
||||
});
|
||||
|
||||
proxySocket.setKeepAlive(true);
|
||||
socket.setKeepAlive(true);
|
||||
proxySocket.setTimeout(0);
|
||||
socket.setTimeout(0);
|
||||
|
||||
proxySocket.on("data", function (chunk) {
|
||||
socket.write(chunk);
|
||||
});
|
||||
proxySocket.on("end", function () {
|
||||
socket.end();
|
||||
});
|
||||
proxySocket.on("error", function () {
|
||||
socket.write(`HTTP/${req.httpVersion} 500 Connection error\r\n\r\n`);
|
||||
socket.end();
|
||||
});
|
||||
socket.on("data", function (chunk) {
|
||||
proxySocket.write(chunk);
|
||||
});
|
||||
socket.on("end", function () {
|
||||
proxySocket.end();
|
||||
});
|
||||
socket.on("error", function () {
|
||||
proxySocket.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected createProxy(): httpProxy {
|
||||
const proxy = httpProxy.createProxyServer();
|
||||
|
||||
@ -189,7 +144,7 @@ export class LensProxy extends Singleton {
|
||||
logger.debug(`Retrying proxy request to url: ${reqId}`);
|
||||
setTimeout(() => {
|
||||
this.retryCounters.set(reqId, retryCount + 1);
|
||||
this.handleRequest(proxy, req, res);
|
||||
this.handleRequest(req, res);
|
||||
}, timeoutMs);
|
||||
}
|
||||
}
|
||||
@ -219,8 +174,8 @@ export class LensProxy extends Singleton {
|
||||
return req.headers.host + req.url;
|
||||
}
|
||||
|
||||
protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) {
|
||||
const cluster = ClusterManager.getInstance().getClusterForRequest(req);
|
||||
protected async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
|
||||
const cluster = this.getClusterForRequest(req);
|
||||
|
||||
if (cluster) {
|
||||
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler);
|
||||
@ -230,7 +185,7 @@ export class LensProxy extends Singleton {
|
||||
// this should be safe because we have already validated cluster uuid
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
return proxy.web(req, res, proxyTarget);
|
||||
return this.proxy.web(req, res, proxyTarget);
|
||||
}
|
||||
}
|
||||
this.router.route(cluster, req, res);
|
||||
24
src/main/proxy-functions/index.ts
Normal file
24
src/main/proxy-functions/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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 "./shell-api-request";
|
||||
export * from "./kube-api-request";
|
||||
export * from "./types";
|
||||
80
src/main/proxy-functions/kube-api-request.ts
Normal file
80
src/main/proxy-functions/kube-api-request.ts
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import net from "net";
|
||||
import url from "url";
|
||||
import { apiKubePrefix } from "../../common/vars";
|
||||
import { ClusterManager } from "../cluster-manager";
|
||||
import type { ProxyApiRequestArgs } from "./types";
|
||||
|
||||
export async function kubeApiRequest({ req, socket, head }: ProxyApiRequestArgs) {
|
||||
const cluster = ClusterManager.getInstance().getClusterForRequest(req);
|
||||
|
||||
if (!cluster) {
|
||||
return;
|
||||
}
|
||||
|
||||
const proxyUrl = await cluster.contextHandler.resolveAuthProxyUrl() + req.url.replace(apiKubePrefix, "");
|
||||
const apiUrl = url.parse(cluster.apiUrl);
|
||||
const pUrl = url.parse(proxyUrl);
|
||||
const connectOpts = { port: parseInt(pUrl.port), host: pUrl.hostname };
|
||||
const proxySocket = new net.Socket();
|
||||
|
||||
proxySocket.connect(connectOpts, () => {
|
||||
proxySocket.write(`${req.method} ${pUrl.path} HTTP/1.1\r\n`);
|
||||
proxySocket.write(`Host: ${apiUrl.host}\r\n`);
|
||||
|
||||
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
||||
const key = req.rawHeaders[i];
|
||||
|
||||
if (key !== "Host" && key !== "Authorization") {
|
||||
proxySocket.write(`${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`);
|
||||
}
|
||||
}
|
||||
proxySocket.write("\r\n");
|
||||
proxySocket.write(head);
|
||||
});
|
||||
|
||||
proxySocket.setKeepAlive(true);
|
||||
socket.setKeepAlive(true);
|
||||
proxySocket.setTimeout(0);
|
||||
socket.setTimeout(0);
|
||||
|
||||
proxySocket.on("data", function (chunk) {
|
||||
socket.write(chunk);
|
||||
});
|
||||
proxySocket.on("end", function () {
|
||||
socket.end();
|
||||
});
|
||||
proxySocket.on("error", function () {
|
||||
socket.write(`HTTP/${req.httpVersion} 500 Connection error\r\n\r\n`);
|
||||
socket.end();
|
||||
});
|
||||
socket.on("data", function (chunk) {
|
||||
proxySocket.write(chunk);
|
||||
});
|
||||
socket.on("end", function () {
|
||||
proxySocket.end();
|
||||
});
|
||||
socket.on("error", function () {
|
||||
proxySocket.end();
|
||||
});
|
||||
}
|
||||
@ -19,24 +19,18 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is here so that the "../shell-session" import can be injected into
|
||||
* LensProxy at creation time. So that the `pty.node` extension isn't loaded
|
||||
* into Lens Extension webpack bundle.
|
||||
*/
|
||||
|
||||
import * as WebSocket from "ws";
|
||||
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";
|
||||
import logger from "../logger";
|
||||
import * as WebSocket from "ws";
|
||||
import { NodeShellSession, LocalShellSession } from "../shell-session";
|
||||
import type { ProxyApiRequestArgs } from "./types";
|
||||
import { ClusterManager } from "../cluster-manager";
|
||||
|
||||
function createWsListener(): WebSocket.Server {
|
||||
export function shellApiRequest({ req, socket, head }: ProxyApiRequestArgs) {
|
||||
const ws = new WebSocket.Server({ noServer: true });
|
||||
|
||||
return ws.on("connection", ((socket: WebSocket, req: http.IncomingMessage) => {
|
||||
ws.on("connection", ((socket: WebSocket, req: http.IncomingMessage) => {
|
||||
const cluster = ClusterManager.getInstance().getClusterForRequest(req);
|
||||
const nodeParam = url.parse(req.url, true).query["node"]?.toString();
|
||||
const shell = nodeParam
|
||||
@ -46,12 +40,8 @@ function createWsListener(): WebSocket.Server {
|
||||
shell.open()
|
||||
.catch(error => logger.error(`[SHELL-SESSION]: failed to open: ${error}`, { error }));
|
||||
}));
|
||||
}
|
||||
|
||||
export async function handleWsUpgrade(req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
|
||||
const wsServer = createWsListener();
|
||||
|
||||
wsServer.handleUpgrade(req, socket, head, (con) => {
|
||||
wsServer.emit("connection", con, req);
|
||||
ws.handleUpgrade(req, socket, head, (con) => {
|
||||
ws.emit("connection", con, req);
|
||||
});
|
||||
}
|
||||
29
src/main/proxy-functions/types.ts
Normal file
29
src/main/proxy-functions/types.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type http from "http";
|
||||
import type net from "net";
|
||||
|
||||
export interface ProxyApiRequestArgs {
|
||||
req: http.IncomingMessage,
|
||||
socket: net.Socket,
|
||||
head: Buffer,
|
||||
}
|
||||
@ -25,7 +25,7 @@ import { exec } from "child_process";
|
||||
import fs from "fs";
|
||||
import * as yaml from "js-yaml";
|
||||
import path from "path";
|
||||
import * as tempy from "tempy";
|
||||
import tempy from "tempy";
|
||||
import logger from "./logger";
|
||||
import { appEventBus } from "../common/event-bus";
|
||||
import { cloneJsonObject } from "../common/utils";
|
||||
|
||||
@ -22,11 +22,11 @@
|
||||
import _ from "lodash";
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import { Cluster, ClusterMetadataKey } from "../cluster";
|
||||
import type { ClusterPrometheusMetadata } from "../../common/cluster-store";
|
||||
import type { Cluster } from "../cluster";
|
||||
import logger from "../logger";
|
||||
import { getMetrics } from "../k8s-request";
|
||||
import { PrometheusProviderRegistry } from "../prometheus";
|
||||
import { ClusterPrometheusMetadata, ClusterMetadataKey } from "../../common/cluster-types";
|
||||
|
||||
export type IMetricsQuery = string | string[] | {
|
||||
[metricName: string]: string;
|
||||
|
||||
@ -19,20 +19,18 @@
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { ClusterId } from "../common/cluster-store";
|
||||
import { makeObservable, observable } from "mobx";
|
||||
import { app, BrowserWindow, dialog, ipcMain, shell, webContents } from "electron";
|
||||
import windowStateKeeper from "electron-window-state";
|
||||
import { appEventBus } from "../common/event-bus";
|
||||
import { ipcMainOn } from "../common/ipc";
|
||||
import { initMenu } from "./menu";
|
||||
import { initTray } from "./tray";
|
||||
import { delay, Singleton } from "../common/utils";
|
||||
import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames";
|
||||
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
||||
import logger from "./logger";
|
||||
import { productName } from "../common/vars";
|
||||
import { LensProxy } from "./proxy/lens-proxy";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
import type { ClusterId } from "../common/cluster-types";
|
||||
|
||||
function isHideable(window: BrowserWindow | null): boolean {
|
||||
return Boolean(window && !window.isDestroyed());
|
||||
@ -50,8 +48,6 @@ export class WindowManager extends Singleton {
|
||||
super();
|
||||
makeObservable(this);
|
||||
this.bindEvents();
|
||||
this.initMenu();
|
||||
this.initTray();
|
||||
}
|
||||
|
||||
get mainUrl() {
|
||||
@ -130,14 +126,6 @@ export class WindowManager extends Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
protected async initMenu() {
|
||||
this.disposers.menuAutoUpdater = initMenu(this);
|
||||
}
|
||||
|
||||
protected initTray() {
|
||||
this.disposers.trayAutoUpdater = initTray(this);
|
||||
}
|
||||
|
||||
protected bindEvents() {
|
||||
// track visible cluster from ui
|
||||
ipcMainOn(IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID, (event, clusterId: ClusterId) => {
|
||||
|
||||
@ -26,18 +26,22 @@ import path from "path";
|
||||
import { app, remote } from "electron";
|
||||
import { migration } from "../migration-wrapper";
|
||||
import fse from "fs-extra";
|
||||
import { ClusterModel, ClusterStore } from "../../common/cluster-store";
|
||||
import { loadConfigFromFileSync } from "../../common/kube-helpers";
|
||||
import type { ClusterModel } from "../../common/cluster-types";
|
||||
import { getCustomKubeConfigPath, storedKubeConfigFolder } from "../../common/utils";
|
||||
|
||||
interface Pre360Beta1ClusterModel extends ClusterModel {
|
||||
kubeConfig: string;
|
||||
}
|
||||
|
||||
export default migration({
|
||||
version: "3.6.0-beta.1",
|
||||
run(store, printLog) {
|
||||
const userDataPath = (app || remote.app).getPath("userData");
|
||||
const kubeConfigBase = ClusterStore.getCustomKubeConfigPath("");
|
||||
const storedClusters: ClusterModel[] = store.get("clusters") || [];
|
||||
const storedClusters: Pre360Beta1ClusterModel[] = store.get("clusters") || [];
|
||||
|
||||
if (!storedClusters.length) return;
|
||||
fse.ensureDirSync(kubeConfigBase);
|
||||
fse.ensureDirSync(storedKubeConfigFolder());
|
||||
|
||||
printLog("Number of clusters to migrate: ", storedClusters.length);
|
||||
const migratedClusters = storedClusters
|
||||
@ -46,7 +50,7 @@ export default migration({
|
||||
* migrate kubeconfig
|
||||
*/
|
||||
try {
|
||||
const absPath = ClusterStore.getCustomKubeConfigPath(cluster.id);
|
||||
const absPath = getCustomKubeConfigPath(cluster.id);
|
||||
|
||||
fse.ensureDirSync(path.dirname(absPath));
|
||||
fse.writeFileSync(absPath, cluster.kubeConfig, { encoding: "utf-8", mode: 0o600 });
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
// Fix embedded kubeconfig paths under snap config
|
||||
|
||||
import { migration } from "../migration-wrapper";
|
||||
import type { ClusterModel } from "../../common/cluster-store";
|
||||
import type { ClusterModel } from "../../common/cluster-types";
|
||||
import { getAppVersion } from "../../common/utils/app-version";
|
||||
import fs from "fs";
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
import { CatalogEntityRegistry } from "../catalog-entity-registry";
|
||||
import "../../../common/catalog-entities";
|
||||
import { catalogCategoryRegistry } from "../../../common/catalog/catalog-category-registry";
|
||||
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "../catalog-entity";
|
||||
import { WebLink } from "../../../common/catalog-entities";
|
||||
|
||||
@ -23,7 +23,9 @@ import type { KubeObjectStore } from "../kube-object.store";
|
||||
|
||||
import { action, observable, makeObservable } from "mobx";
|
||||
import { autoBind } from "../utils";
|
||||
import { KubeApi, parseKubeApi } from "./kube-api";
|
||||
import type { KubeApi } from "./kube-api";
|
||||
import type { KubeObject } from "./kube-object";
|
||||
import { IKubeObjectRef, parseKubeApi, createKubeApiURL } from "./kube-api-parse";
|
||||
|
||||
export class ApiManager {
|
||||
private apis = observable.map<string, KubeApi>();
|
||||
@ -84,6 +86,45 @@ export class ApiManager {
|
||||
getStore<S extends KubeObjectStore>(api: string | KubeApi): S {
|
||||
return this.stores.get(this.resolveApi(api)?.apiBase) as S;
|
||||
}
|
||||
|
||||
lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): string {
|
||||
const {
|
||||
kind, apiVersion, name,
|
||||
namespace = parentObject.getNs()
|
||||
} = ref;
|
||||
|
||||
if (!kind) return "";
|
||||
|
||||
// search in registered apis by 'kind' & 'apiVersion'
|
||||
const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);
|
||||
|
||||
if (api) {
|
||||
return api.getUrl({ namespace, name });
|
||||
}
|
||||
|
||||
// lookup api by generated resource link
|
||||
const apiPrefixes = ["/apis", "/api"];
|
||||
const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`;
|
||||
|
||||
for (const apiPrefix of apiPrefixes) {
|
||||
const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });
|
||||
|
||||
if (this.getApi(apiLink)) {
|
||||
return apiLink;
|
||||
}
|
||||
}
|
||||
|
||||
// resolve by kind only (hpa's might use refs to older versions of resources for example)
|
||||
const apiByKind = this.getApi(api => api.kind === kind);
|
||||
|
||||
if (apiByKind) {
|
||||
return apiByKind.getUrl({ name, namespace });
|
||||
}
|
||||
|
||||
// otherwise generate link with default prefix
|
||||
// resource still might exists in k8s, but api is not registered in the app
|
||||
return createKubeApiURL({ apiVersion, name, namespace, resource });
|
||||
}
|
||||
}
|
||||
|
||||
export const apiManager = new ApiManager();
|
||||
|
||||
@ -20,35 +20,21 @@
|
||||
*/
|
||||
|
||||
import jsYaml from "js-yaml";
|
||||
import { KubeObject } from "../kube-object";
|
||||
import type { KubeJsonApiData } from "../kube-json-api";
|
||||
import { apiBase } from "../index";
|
||||
import { apiManager } from "../api-manager";
|
||||
|
||||
export const resourceApplierApi = {
|
||||
annotations: [
|
||||
"kubectl.kubernetes.io/last-applied-configuration"
|
||||
],
|
||||
|
||||
async update<D extends KubeObject>(resource: object | string): Promise<D> {
|
||||
async update(resource: object | string): Promise<KubeJsonApiData | null> {
|
||||
if (typeof resource === "string") {
|
||||
resource = jsYaml.safeLoad(resource);
|
||||
}
|
||||
|
||||
return apiBase
|
||||
.post<KubeJsonApiData[]>("/stack", { data: resource })
|
||||
.then(data => {
|
||||
const items = data.map(obj => {
|
||||
const api = apiManager.getApiByKind(obj.kind, obj.apiVersion);
|
||||
const [ data = null ] = await apiBase.post<KubeJsonApiData[]>("/stack", { data: resource });
|
||||
|
||||
if (api) {
|
||||
return new api.objectConstructor(obj);
|
||||
} else {
|
||||
return new KubeObject(obj);
|
||||
}
|
||||
});
|
||||
|
||||
return items.length === 1 ? items[0] : items;
|
||||
});
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
@ -21,9 +21,7 @@
|
||||
|
||||
// Parse kube-api path and get api-version, group, etc.
|
||||
|
||||
import type { KubeObject } from "./kube-object";
|
||||
import { splitArray } from "../../common/utils";
|
||||
import { apiManager } from "./api-manager";
|
||||
|
||||
export interface IKubeObjectRef {
|
||||
kind: string;
|
||||
@ -136,42 +134,3 @@ export function createKubeApiURL(ref: IKubeApiLinkRef): string {
|
||||
.filter(v => v)
|
||||
.join("/");
|
||||
}
|
||||
|
||||
export function lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): string {
|
||||
const {
|
||||
kind, apiVersion, name,
|
||||
namespace = parentObject.getNs()
|
||||
} = ref;
|
||||
|
||||
if (!kind) return "";
|
||||
|
||||
// search in registered apis by 'kind' & 'apiVersion'
|
||||
const api = apiManager.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);
|
||||
|
||||
if (api) {
|
||||
return api.getUrl({ namespace, name });
|
||||
}
|
||||
|
||||
// lookup api by generated resource link
|
||||
const apiPrefixes = ["/apis", "/api"];
|
||||
const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`;
|
||||
|
||||
for (const apiPrefix of apiPrefixes) {
|
||||
const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });
|
||||
|
||||
if (apiManager.getApi(apiLink)) {
|
||||
return apiLink;
|
||||
}
|
||||
}
|
||||
|
||||
// resolve by kind only (hpa's might use refs to older versions of resources for example)
|
||||
const apiByKind = apiManager.getApi(api => api.kind === kind);
|
||||
|
||||
if (apiByKind) {
|
||||
return apiByKind.getUrl({ name, namespace });
|
||||
}
|
||||
|
||||
// otherwise generate link with default prefix
|
||||
// resource still might exists in k8s, but api is not registered in the app
|
||||
return createKubeApiURL({ apiVersion, name, namespace, resource });
|
||||
}
|
||||
|
||||
@ -71,11 +71,6 @@ export interface IKubeApiQueryParams {
|
||||
fieldSelector?: string | string[]; // restrict list of objects by their fields, e.g. fieldSelector: "field=name"
|
||||
}
|
||||
|
||||
export interface KubeApiListOptions {
|
||||
namespace?: string;
|
||||
reqInit?: RequestInit;
|
||||
}
|
||||
|
||||
export interface IKubePreferredVersion {
|
||||
preferredVersion?: {
|
||||
version: string;
|
||||
@ -506,5 +501,3 @@ export class KubeApi<T extends KubeObject = any> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export * from "./kube-api-parse";
|
||||
|
||||
@ -279,14 +279,14 @@ export class KubeObject<Metadata extends IKubeObjectMetadata = IKubeObjectMetada
|
||||
}
|
||||
|
||||
// use unified resource-applier api for updating all k8s objects
|
||||
async update<T extends KubeObject>(data: Partial<T>): Promise<T> {
|
||||
async update(data: Partial<this>): Promise<KubeJsonApiData | null> {
|
||||
for (const field of KubeObject.nonEditableFields) {
|
||||
if (!_.isEqual(_.get(this, field), _.get(data, field))) {
|
||||
throw new Error(`Failed to update Kube Object: ${field} has been modified`);
|
||||
}
|
||||
}
|
||||
|
||||
return resourceApplierApi.update<T>({
|
||||
return resourceApplierApi.update({
|
||||
...this.toPlainObject(),
|
||||
...data,
|
||||
});
|
||||
|
||||
@ -30,12 +30,11 @@ import path from "path";
|
||||
import React from "react";
|
||||
|
||||
import { catalogURL } from "../../../common/routes";
|
||||
import { ClusterStore } from "../../../common/cluster-store";
|
||||
import { appEventBus } from "../../../common/event-bus";
|
||||
import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers";
|
||||
import { docsUrl } from "../../../common/vars";
|
||||
import { navigate } from "../../navigation";
|
||||
import { iter } from "../../utils";
|
||||
import { getCustomKubeConfigPath, iter } from "../../utils";
|
||||
import { AceEditor } from "../ace-editor";
|
||||
import { Button } from "../button";
|
||||
import { PageLayout } from "../layout/page-layout";
|
||||
@ -93,7 +92,7 @@ export class AddCluster extends React.Component {
|
||||
appEventBus.emit({ name: "cluster-add", action: "click" });
|
||||
|
||||
try {
|
||||
const absPath = ClusterStore.getCustomKubeConfigPath();
|
||||
const absPath = getCustomKubeConfigPath();
|
||||
|
||||
await fse.ensureDir(path.dirname(absPath));
|
||||
await fse.writeFile(absPath, this.customConfig.trim(), { encoding: "utf-8", mode: 0o600 });
|
||||
|
||||
@ -45,7 +45,7 @@ import { apiManager } from "../../api/api-manager";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { secretsStore } from "../+config-secrets/secrets.store";
|
||||
import { Secret } from "../../api/endpoints";
|
||||
import { getDetailsUrl } from "../kube-object";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
import { Checkbox } from "../checkbox";
|
||||
|
||||
interface Props {
|
||||
|
||||
@ -33,8 +33,8 @@ import { boundMethod, cssNames, prevDefault } from "../../utils";
|
||||
import type { ItemObject } from "../../item.store";
|
||||
import { Spinner } from "../spinner";
|
||||
import { ThemeStore } from "../../theme.store";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { kubeSelectedUrlParam, showDetails } from "../kube-object";
|
||||
import { kubeSelectedUrlParam, showDetails } from "../kube-details";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
@ -101,7 +101,7 @@ export class ClusterIssues extends React.Component<Props> {
|
||||
age: getAge(),
|
||||
message,
|
||||
kind,
|
||||
selfLink: lookupApiLink(involvedObject, error),
|
||||
selfLink: apiManager.lookupApiLink(involvedObject, error),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ import { reaction } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { nodesStore } from "../+nodes/nodes.store";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { getHostedCluster } from "../../../common/cluster-store";
|
||||
import { ClusterStore } from "../../../common/cluster-store";
|
||||
import { interval } from "../../utils";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { Spinner } from "../spinner";
|
||||
@ -35,14 +35,15 @@ import { ClusterMetrics } from "./cluster-metrics";
|
||||
import { clusterOverviewStore } from "./cluster-overview.store";
|
||||
import { ClusterPieCharts } from "./cluster-pie-charts";
|
||||
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
||||
import { ClusterMetricsResourceType } from "../../../main/cluster";
|
||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||
import { getHostedClusterId } from "../../../common/utils/cluster-id-url-parsing";
|
||||
|
||||
@observer
|
||||
export class ClusterOverview extends React.Component {
|
||||
private metricPoller = interval(60, () => this.loadMetrics());
|
||||
|
||||
loadMetrics() {
|
||||
getHostedCluster().available && clusterOverviewStore.loadMetrics();
|
||||
ClusterStore.getInstance().getById(getHostedClusterId()).available && clusterOverviewStore.loadMetrics();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
@ -26,12 +26,13 @@ import { observer } from "mobx-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
import { Badge } from "../badge";
|
||||
import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
import { cssNames } from "../../utils";
|
||||
import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
|
||||
}
|
||||
@ -54,7 +55,7 @@ export class HpaDetails extends React.Component<Props> {
|
||||
case HpaMetricType.Object:
|
||||
const { target } = metric.object;
|
||||
const { kind, name } = target;
|
||||
const objectUrl = getDetailsUrl(lookupApiLink(target, hpa));
|
||||
const objectUrl = getDetailsUrl(apiManager.lookupApiLink(target, hpa));
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -107,7 +108,7 @@ export class HpaDetails extends React.Component<Props> {
|
||||
|
||||
<DrawerItem name="Reference">
|
||||
{scaleTargetRef && (
|
||||
<Link to={getDetailsUrl(lookupApiLink(scaleTargetRef, hpa))}>
|
||||
<Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}>
|
||||
{scaleTargetRef.kind}/{scaleTargetRef.name}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@ -24,7 +24,7 @@ import "./hpa.scss";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { HorizontalPodAutoscaler } from "../../api/endpoints/hpa.api";
|
||||
import { hpaStore } from "./hpa.store";
|
||||
import { Badge } from "../badge";
|
||||
|
||||
@ -23,9 +23,9 @@ import "./limit-range-details.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { LimitPart, LimitRange, LimitRangeItem, Resource } from "../../api/endpoints/limit-range.api";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { DrawerItem } from "../drawer/drawer-item";
|
||||
import { Badge } from "../badge";
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ import "./limit-ranges.scss";
|
||||
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { observer } from "mobx-react";
|
||||
import { KubeObjectListLayout } from "../kube-object/kube-object-list-layout";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { limitRangeStore } from "./limit-ranges.store";
|
||||
import React from "react";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
|
||||
@ -29,9 +29,9 @@ import { Notifications } from "../notifications";
|
||||
import { Input } from "../input";
|
||||
import { Button } from "../button";
|
||||
import { configMapsStore } from "./config-maps.store";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { ConfigMap } from "../../api/endpoints";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<ConfigMap> {
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { configMapsStore } from "./config-maps.store";
|
||||
import type { ConfigMap } from "../../api/endpoints/configmap.api";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { ConfigMapsRouteParams } from "../../../common/routes";
|
||||
|
||||
|
||||
@ -25,9 +25,9 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { DrawerItem } from "../drawer";
|
||||
import { Badge } from "../badge";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { PodDisruptionBudget } from "../../api/endpoints";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<PodDisruptionBudget> {
|
||||
}
|
||||
|
||||
@ -25,8 +25,9 @@ import * as React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { podDisruptionBudgetsStore } from "./pod-disruption-budgets.store";
|
||||
import type { PodDisruptionBudget } from "../../api/endpoints/poddisruptionbudget.api";
|
||||
import { KubeObjectDetailsProps, KubeObjectListLayout } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
|
||||
@ -25,11 +25,11 @@ import kebabCase from "lodash/kebabCase";
|
||||
import { observer } from "mobx-react";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
import { cpuUnitsToNumber, cssNames, unitsToBytes, metricUnitsToNumber } from "../../utils";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { ResourceQuota } from "../../api/endpoints/resource-quota.api";
|
||||
import { LineProgress } from "../line-progress";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<ResourceQuota> {
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ import "./resource-quotas.scss";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { ResourceQuota } from "../../api/endpoints/resource-quota.api";
|
||||
import { AddQuotaDialog } from "./add-quota-dialog";
|
||||
import { resourceQuotaStore } from "./resource-quotas.store";
|
||||
|
||||
@ -37,7 +37,7 @@ import type { IKubeObjectMetadata } from "../../api/kube-object";
|
||||
import { base64 } from "../../utils";
|
||||
import { Notifications } from "../notifications";
|
||||
import upperFirst from "lodash/upperFirst";
|
||||
import { showDetails } from "../kube-object";
|
||||
import { showDetails } from "../kube-details";
|
||||
|
||||
interface Props extends Partial<DialogProps> {
|
||||
}
|
||||
|
||||
@ -32,9 +32,9 @@ import { Notifications } from "../notifications";
|
||||
import { base64 } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { secretsStore } from "./secrets.store";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { Secret } from "../../api/endpoints";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<Secret> {
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import type { Secret } from "../../api/endpoints";
|
||||
import { AddSecretDialog } from "./add-secret-dialog";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { Badge } from "../badge";
|
||||
import { secretsStore } from "./secrets.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
|
||||
@ -27,7 +27,7 @@ import { Secrets } from "../+config-secrets";
|
||||
import { ResourceQuotas } from "../+config-resource-quotas";
|
||||
import { PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
|
||||
import { HorizontalPodAutoscalers } from "../+config-autoscalers";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import { LimitRanges } from "../+config-limit-ranges";
|
||||
import * as routes from "../../../common/routes";
|
||||
|
||||
|
||||
@ -29,10 +29,10 @@ import { cssNames } from "../../utils";
|
||||
import { AceEditor } from "../ace-editor";
|
||||
import { Badge } from "../badge";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { Input } from "../input";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<CustomResourceDefinition> {
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import { computed, makeObservable } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { stopPropagation } from "../../utils";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { crdStore } from "./crd.store";
|
||||
import type { CustomResourceDefinition } from "../../api/endpoints/crd.api";
|
||||
import { Select, SelectOption } from "../select";
|
||||
|
||||
@ -28,9 +28,9 @@ import { computed, makeObservable } from "mobx";
|
||||
import { cssNames } from "../../utils";
|
||||
import { Badge } from "../badge";
|
||||
import { DrawerItem } from "../drawer";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { crdStore } from "./crd.store";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { Input } from "../input";
|
||||
import type { AdditionalPrinterColumnsV1, CustomResourceDefinition } from "../../api/endpoints/crd.api";
|
||||
import { parseJsonPath } from "../../utils/jsonPath";
|
||||
|
||||
@ -25,7 +25,7 @@ import React from "react";
|
||||
import jsonPath from "jsonpath";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { KubeObject } from "../../api/kube-object";
|
||||
import { autorun, computed, makeObservable } from "mobx";
|
||||
import { crdStore } from "./crd.store";
|
||||
|
||||
@ -26,12 +26,13 @@ import kebabCase from "lodash/kebabCase";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
import { Link } from "react-router-dom";
|
||||
import { observer } from "mobx-react";
|
||||
import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { KubeEvent } from "../../api/endpoints/events.api";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { LocaleDate } from "../locale-date";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<KubeEvent> {
|
||||
}
|
||||
@ -81,7 +82,7 @@ export class EventDetails extends React.Component<Props> {
|
||||
</TableHead>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Link to={getDetailsUrl(lookupApiLink(involvedObject, event))}>
|
||||
<Link to={getDetailsUrl(apiManager.lookupApiLink(involvedObject, event))}>
|
||||
{name}
|
||||
</Link>
|
||||
</TableCell>
|
||||
|
||||
@ -27,7 +27,6 @@ import { observer } from "mobx-react";
|
||||
import { orderBy } from "lodash";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { EventStore, eventStore } from "./event.store";
|
||||
import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object";
|
||||
import type { KubeEvent } from "../../api/endpoints/events.api";
|
||||
import type { TableSortCallbacks, TableSortParams, TableProps } from "../table";
|
||||
import type { HeaderCustomizer } from "../item-object-list";
|
||||
@ -35,8 +34,10 @@ import { Tooltip } from "../tooltip";
|
||||
import { Link } from "react-router-dom";
|
||||
import { cssNames, IClassName, stopPropagation } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { eventsURL } from "../../../common/routes";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
import { KubeObjectListLayoutProps, KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
|
||||
enum columnId {
|
||||
message = "message",
|
||||
@ -197,7 +198,7 @@ export class Events extends React.Component<Props> {
|
||||
)
|
||||
},
|
||||
event.getNs(),
|
||||
<Link key="link" to={getDetailsUrl(lookupApiLink(involvedObject, event))} onClick={stopPropagation}>
|
||||
<Link key="link" to={getDetailsUrl(apiManager.lookupApiLink(involvedObject, event))} onClick={stopPropagation}>
|
||||
{involvedObject.kind}: {involvedObject.name}
|
||||
</Link>,
|
||||
event.getSource(),
|
||||
|
||||
@ -27,12 +27,13 @@ import { observer } from "mobx-react";
|
||||
import { DrawerItem } from "../drawer";
|
||||
import { cssNames } from "../../utils";
|
||||
import type { Namespace } from "../../api/endpoints";
|
||||
import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Spinner } from "../spinner";
|
||||
import { resourceQuotaStore } from "../+config-resource-quotas/resource-quotas.store";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { limitRangeStore } from "../+config-limit-ranges/limit-ranges.store";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<Namespace> {
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ import { AddNamespaceDialog } from "./add-namespace-dialog";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { Badge } from "../badge";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { namespaceStore } from "./namespace.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { NamespacesRouteParams } from "../../../common/routes";
|
||||
|
||||
@ -24,9 +24,9 @@ import "./endpoint-details.scss";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { DrawerTitle } from "../drawer";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { Endpoint } from "../../api/endpoints";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { EndpointSubsetList } from "./endpoint-subset-list";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<Endpoint> {
|
||||
|
||||
@ -26,9 +26,9 @@ import { observer } from "mobx-react";
|
||||
import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { boundMethod } from "../../utils";
|
||||
import { lookupApiLink } from "../../api/kube-api";
|
||||
import { Link } from "react-router-dom";
|
||||
import { getDetailsUrl } from "../kube-object";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
|
||||
interface Props {
|
||||
subset: EndpointSubset;
|
||||
@ -92,7 +92,7 @@ export class EndpointSubsetList extends React.Component<Props> {
|
||||
<TableCell className="name">{address.hostname}</TableCell>
|
||||
<TableCell className="target">
|
||||
{ address.targetRef && (
|
||||
<Link to={getDetailsUrl(lookupApiLink(address.getTargetRef(), endpoint))}>
|
||||
<Link to={getDetailsUrl(apiManager.lookupApiLink(address.getTargetRef(), endpoint))}>
|
||||
{address.targetRef.name}
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@ -26,7 +26,7 @@ import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import type { Endpoint } from "../../api/endpoints/endpoint.api";
|
||||
import { endpointStore } from "./endpoints.store";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { EndpointRouteParams } from "../../../common/routes";
|
||||
|
||||
|
||||
@ -29,12 +29,12 @@ import type { ILoadBalancerIngress, Ingress } from "../../api/endpoints";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { ingressStore } from "./ingress.store";
|
||||
import { ResourceMetrics } from "../resource-metrics";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { IngressCharts } from "./ingress-charts";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { getBackendServiceNamePort } from "../../api/endpoints/ingress.api";
|
||||
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
||||
import { ClusterMetricsResourceType } from "../../../main/cluster";
|
||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<Ingress> {
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import type { Ingress } from "../../api/endpoints/ingress.api";
|
||||
import { ingressStore } from "./ingress.store";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { IngressRouteParams } from "../../../common/routes";
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import type { NetworkPolicy } from "../../api/endpoints/network-policy.api";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { networkPolicyStore } from "./network-policy.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { NetworkPoliciesRouteParams } from "../../../common/routes";
|
||||
|
||||
@ -28,8 +28,8 @@ import type { IPolicyEgress, IPolicyIngress, IPolicyIpBlock, IPolicySelector, Ne
|
||||
import { Badge } from "../badge";
|
||||
import { SubTitle } from "../layout/sub-title";
|
||||
import { observer } from "mobx-react";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<NetworkPolicy> {
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ import { Table, TableHead, TableCell, TableRow } from "../table";
|
||||
import { prevDefault } from "../../utils";
|
||||
import { endpointStore } from "../+network-endpoints/endpoints.store";
|
||||
import { Spinner } from "../spinner";
|
||||
import { showDetails } from "../kube-object";
|
||||
import { showDetails } from "../kube-details";
|
||||
|
||||
interface Props {
|
||||
endpoint: KubeObject;
|
||||
|
||||
@ -25,9 +25,9 @@ import React from "react";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
import { Badge } from "../badge";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { Service } from "../../api/endpoints";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { ServicePortComponent } from "./service-port-component";
|
||||
import { endpointStore } from "../+network-endpoints/endpoints.store";
|
||||
import { ServiceDetailsEndpoint } from "./service-details-endpoint";
|
||||
|
||||
@ -25,7 +25,7 @@ import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { RouteComponentProps } from "react-router";
|
||||
import type { Service } from "../../api/endpoints/service.api";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { Badge } from "../badge";
|
||||
import { serviceStore } from "./services.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
|
||||
@ -28,7 +28,7 @@ import { Services } from "../+network-services";
|
||||
import { Endpoints } from "../+network-endpoints";
|
||||
import { Ingresses } from "../+network-ingresses";
|
||||
import { NetworkPolicies } from "../+network-policies";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
import { isAllowedResource } from "../../../common/utils/allowed-resource";
|
||||
import * as routes from "../../../common/routes";
|
||||
|
||||
@observer
|
||||
|
||||
@ -30,14 +30,14 @@ import { Badge } from "../badge";
|
||||
import { nodesStore } from "./nodes.store";
|
||||
import { ResourceMetrics } from "../resource-metrics";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { Node } from "../../api/endpoints";
|
||||
import { NodeCharts } from "./node-charts";
|
||||
import { reaction } from "mobx";
|
||||
import { PodDetailsList } from "../+workloads-pods/pod-details-list";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
||||
import { ClusterMetricsResourceType } from "../../../main/cluster";
|
||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||
import { NodeDetailsResources } from "./node-details-resources";
|
||||
import { DrawerTitle } from "../drawer/drawer-title";
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ import { cssNames, interval } from "../../utils";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { nodesStore } from "./nodes.store";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import type { Node } from "../../api/endpoints/nodes.api";
|
||||
import { LineProgress } from "../line-progress";
|
||||
import { bytesToUnits } from "../../utils/convertMemory";
|
||||
|
||||
@ -23,7 +23,7 @@ import "./pod-security-policies.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { podSecurityPoliciesStore } from "./pod-security-policies.store";
|
||||
import type { PodSecurityPolicy } from "../../api/endpoints";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
|
||||
@ -24,11 +24,11 @@ import "./pod-security-policy-details.scss";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { PodSecurityPolicy } from "../../api/endpoints";
|
||||
import { Badge } from "../badge";
|
||||
import { Table, TableCell, TableHead, TableRow } from "../table";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<PodSecurityPolicy> {
|
||||
}
|
||||
|
||||
@ -26,9 +26,9 @@ import startCase from "lodash/startCase";
|
||||
import { DrawerItem, DrawerTitle } from "../drawer";
|
||||
import { Badge } from "../badge";
|
||||
import { observer } from "mobx-react";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { StorageClass } from "../../api/endpoints";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { KubeObjectMeta } from "../kube-object-meta/kube-object-meta";
|
||||
import { storageClassStore } from "./storage-class.store";
|
||||
import { VolumeDetailsList } from "../+storage-volumes/volume-details-list";
|
||||
import { volumesStore } from "../+storage-volumes/volumes.store";
|
||||
|
||||
@ -25,7 +25,7 @@ import React from "react";
|
||||
import type { RouteComponentProps } from "react-router-dom";
|
||||
import { observer } from "mobx-react";
|
||||
import type { StorageClass } from "../../api/endpoints/storage-class.api";
|
||||
import { KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { storageClassStore } from "./storage-class.store";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { StorageClassesRouteParams } from "../../../common/routes";
|
||||
|
||||
@ -31,10 +31,12 @@ import { Link } from "react-router-dom";
|
||||
import { volumeClaimStore } from "./volume-claim.store";
|
||||
import { ResourceMetrics } from "../resource-metrics";
|
||||
import { VolumeClaimDiskChart } from "./volume-claim-disk-chart";
|
||||
import { getDetailsUrl, KubeObjectDetailsProps, KubeObjectMeta } from "../kube-object";
|
||||
import type { KubeObjectDetailsProps } from "../kube-object-details";
|
||||
import type { PersistentVolumeClaim } from "../../api/endpoints";
|
||||
import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
|
||||
import { ClusterMetricsResourceType } from "../../../main/cluster";
|
||||
import { ClusterMetricsResourceType } from "../../../common/cluster-types";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
import { KubeObjectMeta } from "../kube-object-meta";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<PersistentVolumeClaim> {
|
||||
}
|
||||
|
||||
@ -27,12 +27,13 @@ import { Link, RouteComponentProps } from "react-router-dom";
|
||||
import { volumeClaimStore } from "./volume-claim.store";
|
||||
import type { PersistentVolumeClaim } from "../../api/endpoints/persistent-volume-claims.api";
|
||||
import { podsStore } from "../+workloads-pods/pods.store";
|
||||
import { getDetailsUrl, KubeObjectListLayout } from "../kube-object";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { unitsToBytes } from "../../utils/convertMemory";
|
||||
import { stopPropagation } from "../../utils";
|
||||
import { storageClassApi } from "../../api/endpoints";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import type { VolumeClaimsRouteParams } from "../../../common/routes";
|
||||
import { getDetailsUrl } from "../kube-details";
|
||||
|
||||
enum columnId {
|
||||
name = "name",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user