1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Ban circular dependencies

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2021-07-16 09:14:05 -04:00
parent 48009b7fc5
commit bd5aace104
162 changed files with 1188 additions and 729 deletions

View File

@ -20,12 +20,14 @@
*/ */
const packageJson = require("./package.json"); const packageJson = require("./package.json");
const isInVscode = Boolean(process.env.VSCODE_PID);
module.exports = { module.exports = {
ignorePatterns: [ ignorePatterns: [
"**/node_modules/**/*", "**/node_modules/**/*",
"**/dist/**/*", "**/dist/**/*",
"**/static/**/*", "**/static/**/*",
"**/site/**/*",
], ],
settings: { settings: {
react: { react: {
@ -94,6 +96,8 @@ module.exports = {
parser: "@typescript-eslint/parser", parser: "@typescript-eslint/parser",
extends: [ extends: [
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
], ],
plugins: [ plugins: [
"header", "header",
@ -114,6 +118,10 @@ module.exports = {
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
"import/no-cycle": [2, {
ignoreExternal: true,
maxDepth: isInVscode ? 10 : undefined, // This should speed up VScode locally
}],
"unused-imports/no-unused-imports-ts": "error", "unused-imports/no-unused-imports-ts": "error",
"unused-imports/no-unused-vars-ts": [ "unused-imports/no-unused-vars-ts": [
"warn", { "warn", {
@ -129,7 +137,6 @@ module.exports = {
"avoidEscape": true, "avoidEscape": true,
"allowTemplateLiterals": true, "allowTemplateLiterals": true,
}], }],
"react/prop-types": "off",
"semi": "off", "semi": "off",
"@typescript-eslint/semi": ["error"], "@typescript-eslint/semi": ["error"],
"linebreak-style": ["error", "unix"], "linebreak-style": ["error", "unix"],
@ -160,6 +167,8 @@ module.exports = {
extends: [ extends: [
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:react/recommended", "plugin:react/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
], ],
parserOptions: { parserOptions: {
ecmaVersion: 2018, ecmaVersion: 2018,
@ -168,6 +177,7 @@ module.exports = {
}, },
rules: { rules: {
"header/header": [2, "./license-header"], "header/header": [2, "./license-header"],
"react/prop-types": "off",
"no-invalid-this": "off", "no-invalid-this": "off",
"@typescript-eslint/no-invalid-this": ["error"], "@typescript-eslint/no-invalid-this": ["error"],
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-function-return-type": "off",
@ -182,6 +192,11 @@ module.exports = {
"@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-empty-function": "off",
"react/display-name": "off", "react/display-name": "off",
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
"import/no-cycle": [2, {
ignoreExternal: true,
// The following should speed up VScode but running `yarn lint` manually or in CI will still completely ban it
maxDepth: isInVscode ? 10 : undefined,
}],
"unused-imports/no-unused-imports-ts": "error", "unused-imports/no-unused-imports-ts": "error",
"unused-imports/no-unused-vars-ts": [ "unused-imports/no-unused-vars-ts": [
"warn", { "warn", {
@ -197,7 +212,6 @@ module.exports = {
"avoidEscape": true, "avoidEscape": true,
"allowTemplateLiterals": true, "allowTemplateLiterals": true,
}], }],
"react/prop-types": "off",
"semi": "off", "semi": "off",
"@typescript-eslint/semi": ["error"], "@typescript-eslint/semi": ["error"],
"linebreak-style": ["error", "unix"], "linebreak-style": ["error", "unix"],

View File

@ -35,7 +35,7 @@ dev: binaries/client build-extensions static/build/LensDev.html
yarn dev yarn dev
.PHONY: lint .PHONY: lint
lint: lint: node_modules
yarn lint yarn lint
.PHONY: release-version .PHONY: release-version

36
extensions/.eslintrc.js Normal file
View 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"]
}],
}
}
]
};

View File

@ -11,7 +11,8 @@
"@material-ui/core": "*", "@material-ui/core": "*",
"@types/node": "*", "@types/node": "*",
"@types/react-select": "*", "@types/react-select": "*",
"conf": "^7.0.1" "conf": "^7.0.1",
"typed-emitter": "^1.3.1"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": { "@babel/runtime": {
@ -675,6 +676,12 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true "dev": true
}, },
"typed-emitter": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.3.1.tgz",
"integrity": "sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ==",
"dev": true
},
"uri-js": { "uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@ -665,7 +665,8 @@
"@material-ui/core": "*", "@material-ui/core": "*",
"@types/node": "*", "@types/node": "*",
"@types/react-select": "*", "@types/react-select": "*",
"conf": "^7.0.1" "conf": "^7.0.1",
"typed-emitter": "^1.3.1"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": { "@babel/runtime": {
@ -1329,6 +1330,12 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true "dev": true
}, },
"typed-emitter": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.3.1.tgz",
"integrity": "sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ==",
"dev": true
},
"uri-js": { "uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@ -631,7 +631,8 @@
"@material-ui/core": "*", "@material-ui/core": "*",
"@types/node": "*", "@types/node": "*",
"@types/react-select": "*", "@types/react-select": "*",
"conf": "^7.0.1" "conf": "^7.0.1",
"typed-emitter": "^1.3.1"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": { "@babel/runtime": {
@ -1295,6 +1296,12 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true "dev": true
}, },
"typed-emitter": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.3.1.tgz",
"integrity": "sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ==",
"dev": true
},
"uri-js": { "uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@ -631,7 +631,8 @@
"@material-ui/core": "*", "@material-ui/core": "*",
"@types/node": "*", "@types/node": "*",
"@types/react-select": "*", "@types/react-select": "*",
"conf": "^7.0.1" "conf": "^7.0.1",
"typed-emitter": "^1.3.1"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": { "@babel/runtime": {
@ -1248,6 +1249,12 @@
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true "dev": true
}, },
"typed-emitter": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.3.1.tgz",
"integrity": "sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ==",
"dev": true
},
"uri-js": { "uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@ -63,8 +63,7 @@
}, },
"moduleNameMapper": { "moduleNameMapper": {
"\\.(css|scss)$": "<rootDir>/__mocks__/styleMock.ts", "\\.(css|scss)$": "<rootDir>/__mocks__/styleMock.ts",
"\\.(svg)$": "<rootDir>/__mocks__/imageMock.ts", "\\.(svg)$": "<rootDir>/__mocks__/imageMock.ts"
"src/(.*)": "<rootDir>/__mocks__/windowMock.ts"
}, },
"modulePathIgnorePatterns": [ "modulePathIgnorePatterns": [
"<rootDir>/dist", "<rootDir>/dist",
@ -309,8 +308,8 @@
"@types/webpack-dev-server": "^3.11.1", "@types/webpack-dev-server": "^3.11.1",
"@types/webpack-env": "^1.15.2", "@types/webpack-env": "^1.15.2",
"@types/webpack-node-externals": "^1.7.1", "@types/webpack-node-externals": "^1.7.1",
"@typescript-eslint/eslint-plugin": "^4.14.2", "@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.0.0", "@typescript-eslint/parser": "^4.28.3",
"ace-builds": "^1.4.12", "ace-builds": "^1.4.12",
"ansi_up": "^5.0.0", "ansi_up": "^5.0.0",
"chart.js": "^2.9.3", "chart.js": "^2.9.3",
@ -327,6 +326,7 @@
"esbuild-loader": "^2.13.1", "esbuild-loader": "^2.13.1",
"eslint": "^7.7.0", "eslint": "^7.7.0",
"eslint-plugin-header": "^3.1.1", "eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-react": "^7.24.0", "eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-unused-imports": "^1.0.1", "eslint-plugin-unused-imports": "^1.0.1",

View File

@ -25,10 +25,11 @@ import yaml from "js-yaml";
import path from "path"; import path from "path";
import fse from "fs-extra"; import fse from "fs-extra";
import { Cluster } from "../../main/cluster"; import { Cluster } from "../../main/cluster";
import { ClusterStore } from "../cluster-store";
import { Console } from "console"; import { Console } from "console";
import { stdout, stderr } from "process"; import { stdout, stderr } from "process";
import { ClusterStore } from "../cluster-store";
import type { ClusterId } from "../cluster-types"; import type { ClusterId } from "../cluster-types";
import { getCustomKubeConfigPath } from "../utils";
console = new Console(stdout, stderr); console = new Console(stdout, stderr);
@ -58,7 +59,7 @@ users:
`; `;
function embed(clusterId: ClusterId, contents: any): string { function embed(clusterId: ClusterId, contents: any): string {
const absPath = ClusterStore.getCustomKubeConfigPath(clusterId); const absPath = getCustomKubeConfigPath(clusterId);
fse.ensureDirSync(path.dirname(absPath)); fse.ensureDirSync(path.dirname(absPath));
fse.writeFileSync(absPath, contents, { encoding: "utf-8", mode: 0o600 }); fse.writeFileSync(absPath, contents, { encoding: "utf-8", mode: 0o600 });

View File

@ -20,11 +20,10 @@
*/ */
import { catalogCategoryRegistry } from "../catalog/catalog-category-registry"; 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, clusterDeleteHandler, clusterDisconnectHandler } from "../cluster-ipc"; import { clusterActivateHandler, clusterDeleteHandler, clusterDisconnectHandler } from "../cluster-ipc";
import { ClusterStore } from "../cluster-store"; import { ClusterStore } from "../cluster-store";
import { requestMain } from "../ipc"; import { requestMain } from "../ipc";
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
import { addClusterURL } from "../routes"; import { addClusterURL } from "../routes";
import { app } from "electron"; import { app } from "electron";
import type { CatalogEntitySpec } from "../catalog/catalog-entity"; import type { CatalogEntitySpec } from "../catalog/catalog-entity";

View File

@ -22,38 +22,17 @@
import { action, comparer, observable, makeObservable } from "mobx"; import { action, comparer, observable, makeObservable } from "mobx";
import { BaseStore } from "./base-store"; import { BaseStore } from "./base-store";
import migrations from "../migrations/hotbar-store"; import migrations from "../migrations/hotbar-store";
import * as uuid from "uuid";
import isNull from "lodash/isNull"; import isNull from "lodash/isNull";
import { toJS } from "./utils"; import { toJS } from "./utils";
import { CatalogEntity } from "./catalog"; import { CatalogEntity } from "./catalog";
import { catalogEntity } from "../main/catalog-sources/general"; import { catalogEntity } from "../main/catalog-sources/general";
import { Hotbar, HotbarCreateOptions, HotbarItem, getEmptyHotbar } from "./hotbar-types";
export interface HotbarItem {
entity: {
uid: string;
name?: string;
source?: string;
};
params?: {
[key: string]: string;
}
}
export type Hotbar = Required<HotbarCreateOptions>;
export interface HotbarCreateOptions {
id?: string;
name: string;
items?: (HotbarItem | null)[];
}
export interface HotbarStoreModel { export interface HotbarStoreModel {
hotbars: Hotbar[]; hotbars: Hotbar[];
activeHotbarId: string; activeHotbarId: string;
} }
export const defaultHotbarCells = 12; // Number is chosen to easy hit any item with keyboard
export class HotbarStore extends BaseStore<HotbarStoreModel> { export class HotbarStore extends BaseStore<HotbarStoreModel> {
@observable hotbars: Hotbar[] = []; @observable hotbars: Hotbar[] = [];
@observable private _activeHotbarId: string; @observable private _activeHotbarId: string;
@ -89,18 +68,16 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
return this.hotbarIndex(this.activeHotbarId); return this.hotbarIndex(this.activeHotbarId);
} }
static getInitialItems() {
return [...Array.from(Array(defaultHotbarCells).fill(null))];
}
@action @action
protected fromStore(data: Partial<HotbarStoreModel> = {}) { protected fromStore(data: Partial<HotbarStoreModel> = {}) {
if (!data.hotbars || !data.hotbars.length) { if (!data.hotbars || !data.hotbars.length) {
this.hotbars = [{ const hotbar = getEmptyHotbar("default");
id: uuid.v4(), const { metadata: { uid, name, source } } = catalogEntity;
name: "Default", const initialItem = { entity: { uid, name, source } };
items: this.defaultHotbarInitialItems,
}]; hotbar.items[0] = initialItem;
this.hotbars = [hotbar];
} else { } else {
this.hotbars = data.hotbars; this.hotbars = data.hotbars;
} }
@ -116,16 +93,6 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
} }
} }
get defaultHotbarInitialItems() {
const { metadata: { uid, name, source } } = catalogEntity;
const initialItem = { entity: { uid, name, source }};
return [
initialItem,
...Array.from(Array(defaultHotbarCells - 1).fill(null))
];
}
getActive() { getActive() {
return this.getById(this.activeHotbarId); return this.getById(this.activeHotbarId);
} }
@ -140,16 +107,12 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
@action @action
add(data: HotbarCreateOptions, { setActive = false } = {}) { add(data: HotbarCreateOptions, { setActive = false } = {}) {
const { const hotbar = getEmptyHotbar(data.name, data.id);
id = uuid.v4(),
items = HotbarStore.getInitialItems(),
name,
} = data;
this.hotbars.push({ id, name, items }); this.hotbars.push(hotbar);
if (setActive) { if (setActive) {
this._activeHotbarId = id; this._activeHotbarId = hotbar.id;
} }
} }

View File

@ -0,0 +1,52 @@
/**
* 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 * as uuid from "uuid";
import type { Tuple } from "./utils";
export interface HotbarItem {
entity: {
uid: string;
name?: string;
source?: string;
};
params?: {
[key: string]: string;
}
}
export type Hotbar = Required<HotbarCreateOptions>;
export interface HotbarCreateOptions {
id?: string;
name: string;
items?: Tuple<HotbarItem | null, typeof defaultHotbarCells>;
}
export const defaultHotbarCells = 12; // Number is chosen to easy hit any item with keyboard
export function getEmptyHotbar(name: string, id: string = uuid.v4()): Hotbar {
return {
id,
items: Array(defaultHotbarCells).fill(null) as Tuple<HotbarItem | null, typeof defaultHotbarCells>,
name,
};
}

View File

@ -19,9 +19,6 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { ClusterStore } from "./cluster-store";
import { getHostedClusterId } from "./utils";
export type KubeResource = export type KubeResource =
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" |
"secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" | "secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" |
@ -74,18 +71,3 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
// TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7) // TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7)
export const apiResources: KubeApiResource[] = Object.entries(apiResourceRecord) export const apiResources: KubeApiResource[] = Object.entries(apiResourceRecord)
.map(([apiName, data]) => ({ apiName: apiName as KubeResource, ...data })); .map(([apiName, data]) => ({ apiName: apiName as KubeResource, ...data }));
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;
}

View File

@ -22,9 +22,11 @@
import moment from "moment-timezone"; import moment from "moment-timezone";
import path from "path"; import path from "path";
import os from "os"; import os from "os";
import { ThemeStore } from "../../renderer/theme.store"; import type { ThemeId } from "../../renderer/theme.store";
import { ObservableToggleSet } from "../utils"; import { ObservableToggleSet } from "../utils";
export const defaultThemeName: ThemeId = "lens-dark";
export interface KubeconfigSyncEntry extends KubeconfigSyncValue { export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
filePath: string; filePath: string;
} }
@ -56,10 +58,10 @@ const shell: PreferenceDescription<string | undefined> = {
const colorTheme: PreferenceDescription<string> = { const colorTheme: PreferenceDescription<string> = {
fromStore(val) { fromStore(val) {
return val || ThemeStore.defaultTheme; return val || defaultThemeName;
}, },
toStore(val) { toStore(val) {
if (!val || val === ThemeStore.defaultTheme) { if (!val || val === defaultThemeName) {
return undefined; return undefined;
} }

View File

@ -23,12 +23,11 @@ import { app, remote } from "electron";
import semver from "semver"; import semver from "semver";
import { action, computed, observable, reaction, makeObservable } from "mobx"; import { action, computed, observable, reaction, makeObservable } from "mobx";
import { BaseStore } from "../base-store"; 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 { getAppVersion } from "../utils/app-version";
import { kubeConfigDefaultPath } from "../kube-helpers"; import { kubeConfigDefaultPath } from "../kube-helpers";
import { appEventBus } from "../event-bus"; import { appEventBus } from "../event-bus";
import path from "path"; import path from "path";
import { fileNameMigration } from "../../migrations/user-store";
import { ObservableToggleSet, toJS } from "../../renderer/utils"; import { ObservableToggleSet, toJS } from "../../renderer/utils";
import { DESCRIPTORS, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; import { DESCRIPTORS, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers";
import logger from "../../main/logger"; import logger from "../../main/logger";

View 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;
}

View File

@ -19,13 +19,13 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
/** // Common utils (main OR renderer)
* A function that does nothing
*/
export function noop<T extends any[]>(...args: T): void { export function noop<T extends any[]>(...args: T): void {
return void args; return void args;
} }
export * from "./sort-compare";
export * from "./app-version"; export * from "./app-version";
export * from "./autobind"; export * from "./autobind";
export * from "./base64"; export * from "./base64";
@ -47,7 +47,6 @@ export * from "./openExternal";
export * from "./paths"; export * from "./paths";
export * from "./reject-promise"; export * from "./reject-promise";
export * from "./singleton"; export * from "./singleton";
export * from "./sort-compare";
export * from "./splitArray"; export * from "./splitArray";
export * from "./tar"; export * from "./tar";
export * from "./toggle-set"; export * from "./toggle-set";

View File

@ -20,8 +20,6 @@
*/ */
// layouts // layouts
export * from "../../renderer/components/layout/main-layout";
export * from "../../renderer/components/layout/setting-layout";
export * from "../../renderer/components/layout/page-layout"; export * from "../../renderer/components/layout/page-layout";
export * from "../../renderer/components/layout/wizard-layout"; export * from "../../renderer/components/layout/wizard-layout";
export * from "../../renderer/components/layout/tab-layout"; export * from "../../renderer/components/layout/tab-layout";
@ -62,10 +60,14 @@ export * from "../../renderer/components/chart/bar-chart";
export * from "../../renderer/components/chart/pie-chart"; export * from "../../renderer/components/chart/pie-chart";
// kube helpers // 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"; export * from "../../renderer/components/+events/kube-event-details";
// specific exports // specific exports
export * from "../../renderer/components/status-brick"; export * from "../../renderer/components/status-brick";
export { terminalStore, createTerminalTab, TerminalStore } from "../../renderer/components/dock/terminal.store"; export { terminalStore, TerminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store";
export { logTabStore } from "../../renderer/components/dock/log-tab.store"; export { logTabStore } from "../../renderer/components/dock/log-tab.store";

View File

@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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 { ResourceStack } from "../../common/k8s/resource-stack";
export { apiManager } from "../../renderer/api/api-manager"; export { apiManager } from "../../renderer/api/api-manager";
export { KubeObjectStore } from "../../renderer/kube-object.store"; export { KubeObjectStore } from "../../renderer/kube-object.store";

View File

@ -23,7 +23,7 @@ import { navigation, PageParam, PageParamInit } from "../../renderer/navigation"
export type { PageParamInit, PageParam } from "../../renderer/navigation/page-param"; export type { PageParamInit, PageParam } from "../../renderer/navigation/page-param";
export { navigate, isActiveRoute } from "../../renderer/navigation/helpers"; 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 type { URLParams } from "../../common/utils/buildUrl";
export function createPageParam<V>(init: PageParamInit<V>) { export function createPageParam<V>(init: PageParamInit<V>) {

View File

@ -21,19 +21,17 @@
import { observable } from "mobx"; import { observable } from "mobx";
import type { ClusterMetadata } from "../../common/cluster-types"; import type { ClusterMetadata } from "../../common/cluster-types";
import { Singleton } from "../../common/utils";
import type { Cluster } from "../cluster"; import type { Cluster } from "../cluster";
import type { BaseClusterDetector, ClusterDetectionResult } from "./base-cluster-detector"; 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 }); registry = observable.array<typeof BaseClusterDetector>([], { deep: false });
add(detectorClass: typeof BaseClusterDetector) { add(detectorClass: typeof BaseClusterDetector): this {
this.registry.push(detectorClass); this.registry.push(detectorClass);
return this;
} }
async detectForCluster(cluster: Cluster): Promise<ClusterMetadata> { async detectForCluster(cluster: Cluster): Promise<ClusterMetadata> {
@ -63,10 +61,3 @@ export class DetectorRegistry {
return metadata; return metadata;
} }
} }
export const detectorRegistry = new DetectorRegistry();
detectorRegistry.add(ClusterIdDetector);
detectorRegistry.add(LastSeenDetector);
detectorRegistry.add(VersionDetector);
detectorRegistry.add(DistributionDetector);
detectorRegistry.add(NodesCountDetector);

View File

@ -19,8 +19,8 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { BaseClusterDetector } from "./base-cluster-detector";
import { ClusterMetadataKey } from "../../common/cluster-types"; import { ClusterMetadataKey } from "../../common/cluster-types";
import { BaseClusterDetector } from "./base-cluster-detector";
export class VersionDetector extends BaseClusterDetector { export class VersionDetector extends BaseClusterDetector {
key = ClusterMetadataKey.VERSION; key = ClusterMetadataKey.VERSION;

View File

@ -20,6 +20,8 @@
*/ */
import { ipcMain } from "electron"; import { ipcMain } from "electron";
import type { ClusterId, ClusterMetadata, ClusterMetricsResourceType, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, ClusterRefreshOptions, ClusterState, UpdateClusterModel } from "../common/cluster-types";
import { ClusterStatus, ClusterMetadataKey, initialNodeShellImage } from "../common/cluster-types";
import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx"; import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx";
import { broadcastMessage, ClusterListNamespaceForbiddenChannel } from "../common/ipc"; import { broadcastMessage, ClusterListNamespaceForbiddenChannel } from "../common/ipc";
import { ContextHandler } from "./context-handler"; import { ContextHandler } from "./context-handler";
@ -30,11 +32,9 @@ import { loadConfigFromFile, loadConfigFromFileSync, validateKubeConfig } from "
import { apiResourceRecord, apiResources, KubeApiResource, KubeResource } from "../common/rbac"; import { apiResourceRecord, apiResources, KubeApiResource, KubeResource } from "../common/rbac";
import logger from "./logger"; import logger from "./logger";
import { VersionDetector } from "./cluster-detectors/version-detector"; 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 plimit from "p-limit";
import { toJS } from "../common/utils"; import { toJS } from "../common/utils";
import { initialNodeShellImage } from "../common/cluster-store";
import { ClusterState, ClusterMetadataKey, ClusterRefreshOptions, ClusterStatus, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel } from "../common/cluster-types";
/** /**
* Cluster * Cluster
@ -405,7 +405,7 @@ export class Cluster implements ClusterModel, ClusterState {
@action @action
async refreshMetadata() { async refreshMetadata() {
logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta()); logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta());
const metadata = await detectorRegistry.detectForCluster(this); const metadata = await DetectorRegistry.getInstance().detectForCluster(this);
const existingMetadata = this.metadata; const existingMetadata = this.metadata;
this.metadata = Object.assign(existingMetadata, metadata); this.metadata = Object.assign(existingMetadata, metadata);

View File

@ -19,7 +19,7 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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 fse from "fs-extra";
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import { promiseExec } from "../promise-exec"; import { promiseExec } from "../promise-exec";

View File

@ -28,7 +28,7 @@ import * as LensExtensionsMainApi from "../extensions/main-api";
import { app, autoUpdater, dialog, powerMonitor } from "electron"; import { app, autoUpdater, dialog, powerMonitor } from "electron";
import { appName, isMac, productName } from "../common/vars"; import { appName, isMac, productName } from "../common/vars";
import path from "path"; import path from "path";
import { LensProxy } from "./proxy/lens-proxy"; import { LensProxy } from "./lens-proxy";
import { WindowManager } from "./window-manager"; import { WindowManager } from "./window-manager";
import { ClusterManager } from "./cluster-manager"; import { ClusterManager } from "./cluster-manager";
import { shellSync } from "./shell-sync"; import { shellSync } from "./shell-sync";
@ -49,7 +49,6 @@ import { pushCatalogToRenderer } from "./catalog-pusher";
import { catalogEntityRegistry } from "./catalog"; import { catalogEntityRegistry } from "./catalog";
import { HelmRepoManager } from "./helm/helm-repo-manager"; import { HelmRepoManager } from "./helm/helm-repo-manager";
import { syncGeneralEntities, syncWeblinks, KubeconfigSyncManager } from "./catalog-sources"; import { syncGeneralEntities, syncWeblinks, KubeconfigSyncManager } from "./catalog-sources";
import { handleWsUpgrade } from "./proxy/ws-upgrade";
import configurePackages from "../common/configure-packages"; import configurePackages from "../common/configure-packages";
import { PrometheusProviderRegistry } from "./prometheus"; import { PrometheusProviderRegistry } from "./prometheus";
import * as initializers from "./initializers"; import * as initializers from "./initializers";
@ -60,6 +59,11 @@ import { WeblinkStore } from "../common/weblink-store";
import { ExtensionsStore } from "../extensions/extensions-store"; import { ExtensionsStore } from "../extensions/extensions-store";
import { FilesystemProvisionerStore } from "./extension-filesystem"; import { FilesystemProvisionerStore } from "./extension-filesystem";
import { SentryInit } from "../common/sentry"; import { SentryInit } from "../common/sentry";
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";
// This has to be called before start using winton-based logger // This has to be called before start using winton-based logger
// For example, before any logger.log // For example, before any logger.log
@ -159,14 +163,18 @@ app.on("ready", async () => {
HelmRepoManager.createInstance(); // create the instance HelmRepoManager.createInstance(); // create the instance
const lensProxy = LensProxy.createInstance( const lensProxy = LensProxy.createInstance(new Router(), {
handleWsUpgrade, getClusterForRequest: req => ClusterManager.getInstance().getClusterForRequest(req),
req => ClusterManager.getInstance().getClusterForRequest(req), kubeApiRequest,
); shellApiRequest,
});
ClusterManager.createInstance().init(); ClusterManager.createInstance().init();
KubeconfigSyncManager.createInstance(); KubeconfigSyncManager.createInstance();
DetectorRegistry.createInstance();
initializers.initClusterMetadataDetectors();
try { try {
logger.info("🔌 Starting LensProxy"); logger.info("🔌 Starting LensProxy");
await lensProxy.listen(); await lensProxy.listen();
@ -204,6 +212,11 @@ app.on("ready", async () => {
logger.info("🖥️ Starting WindowManager"); logger.info("🖥️ Starting WindowManager");
const windowManager = WindowManager.createInstance(); const windowManager = WindowManager.createInstance();
cleanup.push(
initMenu(windowManager),
initTray(windowManager),
);
installDeveloperTools(); installDeveloperTools();
if (!startHidden) { if (!startHidden) {

View 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);
}

View File

@ -22,3 +22,4 @@
export * from "./registries"; export * from "./registries";
export * from "./metrics-providers"; export * from "./metrics-providers";
export * from "./ipc"; export * from "./ipc";
export * from "./cluster-metadata-detectors";

View File

@ -22,7 +22,7 @@
import request, { RequestPromiseOptions } from "request-promise-native"; import request, { RequestPromiseOptions } from "request-promise-native";
import { apiKubePrefix } from "../common/vars"; import { apiKubePrefix } from "../common/vars";
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api"; 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"; import type { Cluster } from "./cluster";
export async function k8sRequest<T = any>(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise<T> { export async function k8sRequest<T = any>(cluster: Cluster, path: string, options: RequestPromiseOptions = {}): Promise<T> {

View File

@ -27,7 +27,7 @@ import path from "path";
import fs from "fs-extra"; import fs from "fs-extra";
import { dumpConfigYaml } from "../common/kube-helpers"; import { dumpConfigYaml } from "../common/kube-helpers";
import logger from "./logger"; import logger from "./logger";
import { LensProxy } from "./proxy/lens-proxy"; import { LensProxy } from "./lens-proxy";
export class KubeconfigManager { export class KubeconfigManager {
protected configDir = app.getPath("temp"); protected configDir = app.getPath("temp");

View File

@ -19,33 +19,42 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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 type http from "http";
import spdy from "spdy"; import spdy from "spdy";
import httpProxy from "http-proxy"; import httpProxy from "http-proxy";
import url from "url"; import { apiPrefix, apiKubePrefix } from "../common/vars";
import { apiPrefix, apiKubePrefix } from "../../common/vars"; import type { Router } from "./router";
import { Router } from "../router"; import type { ContextHandler } from "./context-handler";
import type { ContextHandler } from "../context-handler"; import logger from "./logger";
import logger from "../logger"; import { Singleton } from "../common/utils";
import { Singleton } from "../../common/utils"; import type { Cluster } from "./cluster";
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 | Promise<void>;
kubeApiRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
}
export class LensProxy extends Singleton { export class LensProxy extends Singleton {
protected origin: string; protected origin: string;
protected proxyServer: http.Server; protected proxyServer: http.Server;
protected router = new Router();
protected closed = false; protected closed = false;
protected retryCounters = new Map<string, number>(); protected retryCounters = new Map<string, number>();
protected proxy = this.createProxy();
protected getClusterForRequest: GetClusterForRequest;
public port: number; public port: number;
constructor(handleWsUpgrade: WSUpgradeHandler, protected getClusterForRequest: (req: http.IncomingMessage) => Cluster | undefined) { constructor(protected router: Router, functions: LensProxyFunctions) {
super(); super();
const proxy = this.createProxy(); const { shellApiRequest, kubeApiRequest } = functions;
this.getClusterForRequest = functions.getClusterForRequest;
this.proxyServer = spdy.createServer({ this.proxyServer = spdy.createServer({
spdy: { spdy: {
@ -53,17 +62,16 @@ export class LensProxy extends Singleton {
protocols: ["http/1.1", "spdy/3.1"] protocols: ["http/1.1", "spdy/3.1"]
} }
}, (req: http.IncomingMessage, res: http.ServerResponse) => { }, (req: http.IncomingMessage, res: http.ServerResponse) => {
this.handleRequest(proxy, req, res); this.handleRequest(req, res);
}); });
this.proxyServer this.proxyServer
.on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => { .on("upgrade", (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => {
if (req.url.startsWith(`${apiPrefix}?`)) { const isInternal = req.url.startsWith(`${apiPrefix}?`);
handleWsUpgrade(req, socket, head); const reqHandler = isInternal ? shellApiRequest : kubeApiRequest;
} else {
this.handleProxyUpgrade(proxy, req, socket, head) (async () => reqHandler({ req, socket, head }))()
.catch(error => logger.error(`[LENS-PROXY]: failed to handle proxy upgrade: ${error}`)); .catch(error => logger.error(logger.error(`[LENS-PROXY]: failed to handle proxy upgrade: ${error}`)));
}
}); });
} }
@ -104,58 +112,6 @@ export class LensProxy extends Singleton {
this.closed = true; this.closed = true;
} }
protected async handleProxyUpgrade(proxy: httpProxy, req: http.IncomingMessage, socket: net.Socket, head: Buffer) {
const cluster = this.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 { protected createProxy(): httpProxy {
const proxy = httpProxy.createProxyServer(); const proxy = httpProxy.createProxyServer();
@ -169,10 +125,7 @@ export class LensProxy extends Singleton {
if (!res.headersSent && req.url) { if (!res.headersSent && req.url) {
const url = new URL(req.url, "http://localhost"); const url = new URL(req.url, "http://localhost");
if (url.searchParams.has("watch")) { if (url.searchParams.has("watch")) res.flushHeaders();
res.statusCode = proxyRes.statusCode;
res.flushHeaders();
}
} }
}); });
@ -181,8 +134,6 @@ export class LensProxy extends Singleton {
return; return;
} }
logger.error(`[LENS-PROXY]: http proxy errored for cluster: ${error}`, { url: req.url });
if (target) { if (target) {
logger.debug(`Failed proxy to target: ${JSON.stringify(target, null, 2)}`); logger.debug(`Failed proxy to target: ${JSON.stringify(target, null, 2)}`);
@ -195,7 +146,7 @@ export class LensProxy extends Singleton {
logger.debug(`Retrying proxy request to url: ${reqId}`); logger.debug(`Retrying proxy request to url: ${reqId}`);
setTimeout(() => { setTimeout(() => {
this.retryCounters.set(reqId, retryCount + 1); this.retryCounters.set(reqId, retryCount + 1);
this.handleRequest(proxy, req, res) this.handleRequest(req, res)
.catch(error => logger.error(`[LENS-PROXY]: failed to handle request on proxy error: ${error}`)); .catch(error => logger.error(`[LENS-PROXY]: failed to handle request on proxy error: ${error}`));
}, timeoutMs); }, timeoutMs);
} }
@ -203,7 +154,7 @@ export class LensProxy extends Singleton {
} }
try { try {
res.writeHead(500).end(`Oops, something went wrong.\n${error}`); res.writeHead(500).end("Oops, something went wrong.");
} catch (e) { } catch (e) {
logger.error(`[LENS-PROXY]: Failed to write headers: `, e); logger.error(`[LENS-PROXY]: Failed to write headers: `, e);
} }
@ -226,7 +177,7 @@ export class LensProxy extends Singleton {
return req.headers.host + req.url; return req.headers.host + req.url;
} }
protected async handleRequest(proxy: httpProxy, req: http.IncomingMessage, res: http.ServerResponse) { protected async handleRequest(req: http.IncomingMessage, res: http.ServerResponse) {
const cluster = this.getClusterForRequest(req); const cluster = this.getClusterForRequest(req);
if (cluster) { if (cluster) {
@ -237,7 +188,7 @@ export class LensProxy extends Singleton {
// this should be safe because we have already validated cluster uuid // this should be safe because we have already validated cluster uuid
res.setHeader("Access-Control-Allow-Origin", "*"); 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); this.router.route(cluster, req, res);

View 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";

View 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();
});
}

View File

@ -19,24 +19,18 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 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 http from "http";
import type net from "net";
import url from "url"; import url from "url";
import { NodeShellSession, LocalShellSession } from "../shell-session";
import { ClusterManager } from "../cluster-manager";
import logger from "../logger"; 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 }); 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 cluster = ClusterManager.getInstance().getClusterForRequest(req);
const nodeParam = url.parse(req.url, true).query["node"]?.toString(); const nodeParam = url.parse(req.url, true).query["node"]?.toString();
const shell = nodeParam const shell = nodeParam
@ -46,12 +40,8 @@ function createWsListener(): WebSocket.Server {
shell.open() shell.open()
.catch(error => logger.error(`[SHELL-SESSION]: failed to open: ${error}`, { error })); .catch(error => logger.error(`[SHELL-SESSION]: failed to open: ${error}`, { error }));
})); }));
}
export async function handleWsUpgrade(req: http.IncomingMessage, socket: net.Socket, head: Buffer) { ws.handleUpgrade(req, socket, head, (con) => {
const wsServer = createWsListener(); ws.emit("connection", con, req);
wsServer.handleUpgrade(req, socket, head, (con) => {
wsServer.emit("connection", con, req);
}); });
} }

View 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,
}

View File

@ -25,7 +25,7 @@ import { exec } from "child_process";
import fs from "fs"; import fs from "fs";
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import path from "path"; import path from "path";
import * as tempy from "tempy"; import tempy from "tempy";
import logger from "./logger"; import logger from "./logger";
import { appEventBus } from "../common/event-bus"; import { appEventBus } from "../common/event-bus";
import { cloneJsonObject } from "../common/utils"; import { cloneJsonObject } from "../common/utils";

View File

@ -23,10 +23,10 @@ import _ from "lodash";
import type { LensApiRequest } from "../router"; import type { LensApiRequest } from "../router";
import { respondJson } from "../utils/http-responses"; import { respondJson } from "../utils/http-responses";
import type { Cluster } from "../cluster"; import type { Cluster } from "../cluster";
import { ClusterMetadataKey, ClusterPrometheusMetadata } from "../../common/cluster-types";
import logger from "../logger"; import logger from "../logger";
import { getMetrics } from "../k8s-request"; import { getMetrics } from "../k8s-request";
import { PrometheusProviderRegistry } from "../prometheus"; import { PrometheusProviderRegistry } from "../prometheus";
import { ClusterPrometheusMetadata, ClusterMetadataKey } from "../../common/cluster-types";
export type IMetricsQuery = string | string[] | { export type IMetricsQuery = string | string[] | {
[metricName: string]: string; [metricName: string]: string;

View File

@ -25,14 +25,12 @@ import { app, BrowserWindow, dialog, ipcMain, shell, webContents } from "electro
import windowStateKeeper from "electron-window-state"; import windowStateKeeper from "electron-window-state";
import { appEventBus } from "../common/event-bus"; import { appEventBus } from "../common/event-bus";
import { ipcMainOn } from "../common/ipc"; import { ipcMainOn } from "../common/ipc";
import { initMenu } from "./menu";
import { initTray } from "./tray";
import { delay, iter, Singleton } from "../common/utils"; import { delay, iter, Singleton } from "../common/utils";
import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames"; import { ClusterFrameInfo, clusterFrameMap } from "../common/cluster-frames";
import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
import logger from "./logger"; import logger from "./logger";
import { productName } from "../common/vars"; import { productName } from "../common/vars";
import { LensProxy } from "./proxy/lens-proxy"; import { LensProxy } from "./lens-proxy";
function isHideable(window: BrowserWindow | null): boolean { function isHideable(window: BrowserWindow | null): boolean {
return Boolean(window && !window.isDestroyed()); return Boolean(window && !window.isDestroyed());
@ -56,8 +54,6 @@ export class WindowManager extends Singleton {
super(); super();
makeObservable(this); makeObservable(this);
this.bindEvents(); this.bindEvents();
this.initMenu();
this.initTray();
} }
get mainUrl() { get mainUrl() {
@ -136,14 +132,6 @@ export class WindowManager extends Singleton {
} }
} }
protected async initMenu() {
this.disposers.menuAutoUpdater = initMenu(this);
}
protected initTray() {
this.disposers.trayAutoUpdater = initTray(this);
}
protected bindEvents() { protected bindEvents() {
// track visible cluster from ui // track visible cluster from ui
ipcMainOn(IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID, (event, clusterId: ClusterId) => { ipcMainOn(IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID, (event, clusterId: ClusterId) => {

View File

@ -20,23 +20,18 @@
*/ */
// Cleans up a store that had the state related data stored // Cleans up a store that had the state related data stored
import { Hotbar, HotbarStore } from "../../common/hotbar-store";
import * as uuid from "uuid";
import type { MigrationDeclaration } from "../helpers"; import type { MigrationDeclaration } from "../helpers";
import { catalogEntity } from "../../main/catalog-sources/general"; import { catalogEntity } from "../../main/catalog-sources/general";
import { getEmptyHotbar } from "../../common/hotbar-types";
export default { export default {
version: "5.0.0-alpha.0", version: "5.0.0-alpha.0",
run(store) { run(store) {
const hotbar: Hotbar = { const hotbar = getEmptyHotbar("default");
id: uuid.v4(),
name: "default",
items: HotbarStore.getInitialItems(),
};
const { metadata: { uid, name, source } } = catalogEntity; const { metadata: { uid, name, source } } = catalogEntity;
const initialItem = { entity: { uid, name, source } };
hotbar.items[0] = { entity: { uid, name, source } }; hotbar.items[0] = initialItem;
store.set("hotbars", [hotbar]); store.set("hotbars", [hotbar]);
} }

View File

@ -20,7 +20,7 @@
*/ */
// Cleans up a store that had the state related data stored // Cleans up a store that had the state related data stored
import type { Hotbar } from "../../common/hotbar-store"; import type { Hotbar } from "../../common/hotbar-types";
import * as uuid from "uuid"; import * as uuid from "uuid";
import type { MigrationDeclaration } from "../helpers"; import type { MigrationDeclaration } from "../helpers";

View File

@ -25,7 +25,7 @@ import { isNull } from "lodash";
import path from "path"; import path from "path";
import * as uuid from "uuid"; import * as uuid from "uuid";
import type { ClusterStoreModel } from "../../common/cluster-store"; import type { ClusterStoreModel } from "../../common/cluster-store";
import { defaultHotbarCells, Hotbar, HotbarStore } from "../../common/hotbar-store"; import { Hotbar, defaultHotbarCells, getEmptyHotbar } from "../../common/hotbar-types";
import { catalogEntity } from "../../main/catalog-sources/general"; import { catalogEntity } from "../../main/catalog-sources/general";
import { MigrationDeclaration, migrationLog } from "../helpers"; import { MigrationDeclaration, migrationLog } from "../helpers";
import { generateNewIdFor } from "../utils"; import { generateNewIdFor } from "../utils";
@ -52,7 +52,7 @@ export default {
migrationLog(`Creating new hotbar for ${name}`); migrationLog(`Creating new hotbar for ${name}`);
workspaceHotbars.set(id, { workspaceHotbars.set(id, {
id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs
items: [], items: [] as any,
name: `Workspace: ${name}`, name: `Workspace: ${name}`,
}); });
} }
@ -65,7 +65,7 @@ export default {
workspaceHotbars.set("default", { workspaceHotbars.set("default", {
name, name,
id, id,
items: items.filter(Boolean), items: items.filter(Boolean) as any,
}); });
} }
@ -94,7 +94,7 @@ export default {
} }
for (const hotbar of workspaceHotbars.values()) { for (const hotbar of workspaceHotbars.values()) {
if (hotbar.items.length === 0) { if ((hotbar.items as any).length === 0) {
migrationLog(`Skipping ${hotbar.name} due to it being empty`); migrationLog(`Skipping ${hotbar.name} due to it being empty`);
continue; continue;
} }
@ -123,11 +123,7 @@ export default {
if (freeIndex === -1) { if (freeIndex === -1) {
// making a new hotbar is less destructive if the first hotbar // making a new hotbar is less destructive if the first hotbar
// called "default" is full than overriding a hotbar item // called "default" is full than overriding a hotbar item
const hotbar = { const hotbar = getEmptyHotbar("initial");
id: uuid.v4(),
name: "initial",
items: HotbarStore.getInitialItems(),
};
hotbar.items[0] = { entity: { uid, name, source } }; hotbar.items[0] = { entity: { uid, name, source } };
hotbars.unshift(hotbar); hotbars.unshift(hotbar);
@ -135,11 +131,7 @@ export default {
defaultHotbar.items[freeIndex] = { entity: { uid, name, source } }; defaultHotbar.items[freeIndex] = { entity: { uid, name, source } };
} }
} else { } else {
const hotbar = { const hotbar = getEmptyHotbar("default");
id: uuid.v4(),
name: "default",
items: HotbarStore.getInitialItems(),
};
hotbar.items[0] = { entity: { uid, name, source } }; hotbar.items[0] = { entity: { uid, name, source } };
hotbars.unshift(hotbar); hotbars.unshift(hotbar);

View File

@ -19,14 +19,14 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import type { Hotbar } from "../../common/hotbar-store"; import type { Hotbar } from "../../common/hotbar-types";
import { catalogEntityRegistry } from "../../main/catalog"; import { catalogEntityRegistry } from "../../main/catalog";
import type { MigrationDeclaration } from "../helpers"; import type { MigrationDeclaration } from "../helpers";
export default { export default {
version: "5.0.0-beta.5", version: "5.0.0-beta.5",
run(store) { run(store) {
const hotbars: Hotbar[] = store.get("hotbars"); const hotbars: Hotbar[] = store.get("hotbars") ?? [];
hotbars.forEach((hotbar, hotbarIndex) => { hotbars.forEach((hotbar, hotbarIndex) => {
hotbar.items.forEach((item, itemIndex) => { hotbar.items.forEach((item, itemIndex) => {

View File

@ -20,7 +20,6 @@
*/ */
import { CatalogEntityRegistry } from "../catalog-entity-registry"; import { CatalogEntityRegistry } from "../catalog-entity-registry";
import "../../../common/catalog-entities";
import { catalogCategoryRegistry } from "../../../common/catalog/catalog-category-registry"; import { catalogCategoryRegistry } from "../../../common/catalog/catalog-category-registry";
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "../catalog-entity"; import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "../catalog-entity";
import { WebLink } from "../../../common/catalog-entities"; import { WebLink } from "../../../common/catalog-entities";

View File

@ -23,8 +23,9 @@ import type { KubeObjectStore } from "../kube-object.store";
import { action, observable, makeObservable } from "mobx"; import { action, observable, makeObservable } from "mobx";
import { autoBind, iter } from "../utils"; import { autoBind, iter } from "../utils";
import { KubeApi, parseKubeApi } from "./kube-api"; import type { KubeApi } from "./kube-api";
import type { KubeObject } from "./kube-object"; import type { KubeObject } from "./kube-object";
import { IKubeObjectRef, parseKubeApi, createKubeApiURL } from "./kube-api-parse";
export class ApiManager { export class ApiManager {
private apis = observable.map<string, KubeApi<KubeObject>>(); private apis = observable.map<string, KubeApi<KubeObject>>();
@ -91,6 +92,45 @@ export class ApiManager {
getStore<S extends KubeObjectStore<KubeObject>>(api: string | KubeApi<KubeObject>): S | undefined { getStore<S extends KubeObjectStore<KubeObject>>(api: string | KubeApi<KubeObject>): S | undefined {
return this.stores.get(this.resolveApi(api)?.apiBase) as 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(); export const apiManager = new ApiManager();

View File

@ -20,35 +20,21 @@
*/ */
import jsYaml from "js-yaml"; import jsYaml from "js-yaml";
import { KubeObject } from "../kube-object";
import type { KubeJsonApiData } from "../kube-json-api"; import type { KubeJsonApiData } from "../kube-json-api";
import { apiBase } from "../index"; import { apiBase } from "../index";
import { apiManager } from "../api-manager";
export const resourceApplierApi = { export const resourceApplierApi = {
annotations: [ annotations: [
"kubectl.kubernetes.io/last-applied-configuration" "kubectl.kubernetes.io/last-applied-configuration"
], ],
async update<K extends KubeObject>(resource: object | string): Promise<K | null> { async update(resource: object | string): Promise<KubeJsonApiData | null> {
if (typeof resource === "string") { if (typeof resource === "string") {
resource = jsYaml.safeLoad(resource); resource = jsYaml.safeLoad(resource);
} }
return apiBase const [data = null] = await apiBase.post<KubeJsonApiData[]>("/stack", { data: resource });
.post<KubeJsonApiData[]>("/stack", { data: resource })
.then(data => {
const items = data.map(obj => {
const api = apiManager.getApiByKind(obj.kind, obj.apiVersion);
if (api) { return data;
return new api.objectConstructor(obj);
} else {
return new KubeObject(obj);
}
});
return items[0] as K ?? null;
});
} }
}; };

View File

@ -21,9 +21,7 @@
// Parse kube-api path and get api-version, group, etc. // Parse kube-api path and get api-version, group, etc.
import type { KubeObject } from "./kube-object";
import { splitArray } from "../../common/utils"; import { splitArray } from "../../common/utils";
import { apiManager } from "./api-manager";
import { isDebugging } from "../../common/vars"; import { isDebugging } from "../../common/vars";
import logger from "../../main/logger"; import logger from "../../main/logger";
import { inspect } from "util"; import { inspect } from "util";
@ -159,42 +157,3 @@ export function createKubeApiURL(ref: IKubeApiLinkRef): string {
.filter(v => v) .filter(v => v)
.join("/"); .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 });
}

View File

@ -56,11 +56,6 @@ export interface IKubeApiOptions<T extends KubeObject> {
checkPreferredVersion?: boolean; checkPreferredVersion?: boolean;
} }
export interface KubeApiListOptions {
namespace?: string;
reqInit?: RequestInit;
}
export interface IKubeApiQueryParams { export interface IKubeApiQueryParams {
watch?: boolean | number; watch?: boolean | number;
resourceVersion?: string; resourceVersion?: string;

View File

@ -277,14 +277,14 @@ export class KubeObject<Metadata extends KubeObjectMetadata = KubeObjectMetadata
} }
// use unified resource-applier api for updating all k8s objects // use unified resource-applier api for updating all k8s objects
async update<K extends KubeObject>(data: Partial<K>): Promise<K> { async update(data: Partial<this>): Promise<KubeJsonApiData | null> {
for (const field of KubeObject.nonEditableFields) { for (const field of KubeObject.nonEditableFields) {
if (!_.isEqual(_.get(this, field), _.get(data, field))) { if (!_.isEqual(_.get(this, field), _.get(data, field))) {
throw new Error(`Failed to update Kube Object: ${field} has been modified`); throw new Error(`Failed to update Kube Object: ${field} has been modified`);
} }
} }
return resourceApplierApi.update<K>({ return resourceApplierApi.update({
...this.toPlainObject(), ...this.toPlainObject(),
...data, ...data,
}); });

View File

@ -30,12 +30,11 @@ import path from "path";
import React from "react"; import React from "react";
import { catalogURL } from "../../../common/routes"; import { catalogURL } from "../../../common/routes";
import { ClusterStore } from "../../../common/cluster-store";
import { appEventBus } from "../../../common/event-bus"; import { appEventBus } from "../../../common/event-bus";
import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers"; import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers";
import { docsUrl } from "../../../common/vars"; import { docsUrl } from "../../../common/vars";
import { navigate } from "../../navigation"; import { navigate } from "../../navigation";
import { iter } from "../../utils"; import { getCustomKubeConfigPath, iter } from "../../utils";
import { AceEditor } from "../ace-editor"; import { AceEditor } from "../ace-editor";
import { Button } from "../button"; import { Button } from "../button";
import { Notifications } from "../notifications"; import { Notifications } from "../notifications";
@ -93,7 +92,7 @@ export class AddCluster extends React.Component {
appEventBus.emit({ name: "cluster-add", action: "click" }); appEventBus.emit({ name: "cluster-add", action: "click" });
try { try {
const absPath = ClusterStore.getCustomKubeConfigPath(); const absPath = getCustomKubeConfigPath();
await fse.ensureDir(path.dirname(absPath)); await fse.ensureDir(path.dirname(absPath));
await fse.writeFile(absPath, this.customConfig.trim(), { encoding: "utf-8", mode: 0o600 }); await fse.writeFile(absPath, this.customConfig.trim(), { encoding: "utf-8", mode: 0o600 });

View File

@ -45,7 +45,7 @@ import { apiManager } from "../../api/api-manager";
import { SubTitle } from "../layout/sub-title"; import { SubTitle } from "../layout/sub-title";
import { secretsStore } from "../+config-secrets/secrets.store"; import { secretsStore } from "../+config-secrets/secrets.store";
import { Secret } from "../../api/endpoints"; import { Secret } from "../../api/endpoints";
import { getDetailsUrl } from "../kube-object"; import { getDetailsUrl } from "../kube-details";
import { Checkbox } from "../checkbox"; import { Checkbox } from "../checkbox";
interface Props { interface Props {

View File

@ -33,9 +33,9 @@ import { boundMethod, cssNames, prevDefault } from "../../utils";
import type { ItemObject } from "../../item.store"; import type { ItemObject } from "../../item.store";
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
import { ThemeStore } from "../../theme.store"; import { ThemeStore } from "../../theme.store";
import { lookupApiLink } from "../../api/kube-api"; import { kubeSelectedUrlParam, showDetails } from "../kube-details";
import { kubeSelectedUrlParam, showDetails } from "../kube-object";
import { kubeWatchApi } from "../../api/kube-watch-api"; import { kubeWatchApi } from "../../api/kube-watch-api";
import { apiManager } from "../../api/api-manager";
interface Props { interface Props {
className?: string; className?: string;
@ -106,7 +106,7 @@ export class ClusterIssues extends React.Component<Props> {
age: getAge(), age: getAge(),
message, message,
kind, kind,
selfLink: lookupApiLink(involvedObject, error), selfLink: apiManager.lookupApiLink(involvedObject, error),
}); });
}); });

View File

@ -26,12 +26,13 @@ import { observer } from "mobx-react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object"; import { getDetailsUrl } from "../kube-details";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api"; import { HorizontalPodAutoscaler, HpaMetricType, IHpaMetric } from "../../api/endpoints/hpa.api";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { lookupApiLink } from "../../api/kube-api"; import { KubeObjectMeta } from "../kube-object-meta";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { apiManager } from "../../api/api-manager";
import type { KubeObjectDetailsProps } from "../kube-object-details";
interface Props extends KubeObjectDetailsProps<HorizontalPodAutoscaler> { interface Props extends KubeObjectDetailsProps<HorizontalPodAutoscaler> {
} }
@ -54,7 +55,7 @@ export class HpaDetails extends React.Component<Props> {
case HpaMetricType.Object: case HpaMetricType.Object:
const { target } = metric.object; const { target } = metric.object;
const { kind, name } = target; const { kind, name } = target;
const objectUrl = getDetailsUrl(lookupApiLink(target, hpa)); const objectUrl = getDetailsUrl(apiManager.lookupApiLink(target, hpa));
return ( return (
<> <>
@ -107,7 +108,7 @@ export class HpaDetails extends React.Component<Props> {
<DrawerItem name="Reference"> <DrawerItem name="Reference">
{scaleTargetRef && ( {scaleTargetRef && (
<Link to={getDetailsUrl(lookupApiLink(scaleTargetRef, hpa))}> <Link to={getDetailsUrl(apiManager.lookupApiLink(scaleTargetRef, hpa))}>
{scaleTargetRef.kind}/{scaleTargetRef.name} {scaleTargetRef.kind}/{scaleTargetRef.name}
</Link> </Link>
)} )}

View File

@ -24,7 +24,7 @@ import "./hpa.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router"; 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 type { HorizontalPodAutoscaler } from "../../api/endpoints/hpa.api";
import { hpaStore } from "./hpa.store"; import { hpaStore } from "./hpa.store";
import { Badge } from "../badge"; import { Badge } from "../badge";

View File

@ -23,9 +23,9 @@ import "./limit-range-details.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-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 { LimitPart, LimitRange, LimitRangeItem, Resource } from "../../api/endpoints/limit-range.api";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { DrawerItem } from "../drawer/drawer-item"; import { DrawerItem } from "../drawer/drawer-item";
import { Badge } from "../badge"; import { Badge } from "../badge";

View File

@ -23,7 +23,7 @@ import "./limit-ranges.scss";
import type { RouteComponentProps } from "react-router"; import type { RouteComponentProps } from "react-router";
import { observer } from "mobx-react"; 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 { limitRangeStore } from "./limit-ranges.store";
import React from "react"; import React from "react";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";

View File

@ -29,9 +29,9 @@ import { Notifications } from "../notifications";
import { Input } from "../input"; import { Input } from "../input";
import { Button } from "../button"; import { Button } from "../button";
import { configMapsStore } from "./config-maps.store"; 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 type { ConfigMap } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<ConfigMap> { interface Props extends KubeObjectDetailsProps<ConfigMap> {
} }

View File

@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router"; import type { RouteComponentProps } from "react-router";
import { configMapsStore } from "./config-maps.store"; import { configMapsStore } from "./config-maps.store";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { ConfigMapsRouteParams } from "../../../common/routes"; import type { ConfigMapsRouteParams } from "../../../common/routes";

View File

@ -25,9 +25,9 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import type { KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import type { PodDisruptionBudget } from "../../api/endpoints"; import type { PodDisruptionBudget } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<PodDisruptionBudget> { interface Props extends KubeObjectDetailsProps<PodDisruptionBudget> {
} }

View File

@ -25,8 +25,9 @@ import * as React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { podDisruptionBudgetsStore } from "./pod-disruption-budgets.store"; import { podDisruptionBudgetsStore } from "./pod-disruption-budgets.store";
import type { PodDisruptionBudget } from "../../api/endpoints/poddisruptionbudget.api"; import type { PodDisruptionBudget } from "../../api/endpoints/poddisruptionbudget.api";
import { KubeObjectDetailsProps, KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { KubeObjectDetailsProps } from "../kube-object-details";
enum columnId { enum columnId {
name = "name", name = "name",

View File

@ -25,11 +25,11 @@ import kebabCase from "lodash/kebabCase";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import { cpuUnitsToNumber, cssNames, unitsToBytes, metricUnitsToNumber } from "../../utils"; 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 type { ResourceQuota } from "../../api/endpoints/resource-quota.api";
import { LineProgress } from "../line-progress"; import { LineProgress } from "../line-progress";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<ResourceQuota> { interface Props extends KubeObjectDetailsProps<ResourceQuota> {
} }

View File

@ -24,7 +24,7 @@ import "./resource-quotas.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router"; import type { RouteComponentProps } from "react-router";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { AddQuotaDialog } from "./add-quota-dialog"; import { AddQuotaDialog } from "./add-quota-dialog";
import { resourceQuotaStore } from "./resource-quotas.store"; import { resourceQuotaStore } from "./resource-quotas.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";

View File

@ -37,7 +37,7 @@ import type { KubeObjectMetadata } from "../../api/kube-object";
import { base64 } from "../../utils"; import { base64 } from "../../utils";
import { Notifications } from "../notifications"; import { Notifications } from "../notifications";
import upperFirst from "lodash/upperFirst"; import upperFirst from "lodash/upperFirst";
import { showDetails } from "../kube-object"; import { showDetails } from "../kube-details";
interface Props extends Partial<DialogProps> { interface Props extends Partial<DialogProps> {
} }

View File

@ -32,9 +32,9 @@ import { Notifications } from "../notifications";
import { base64 } from "../../utils"; import { base64 } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { secretsStore } from "./secrets.store"; 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 type { Secret } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<Secret> { interface Props extends KubeObjectDetailsProps<Secret> {
} }

View File

@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router"; import type { RouteComponentProps } from "react-router";
import { AddSecretDialog } from "./add-secret-dialog"; import { AddSecretDialog } from "./add-secret-dialog";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { secretsStore } from "./secrets.store"; import { secretsStore } from "./secrets.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";

View File

@ -27,7 +27,7 @@ import { Secrets } from "../+config-secrets";
import { ResourceQuotas } from "../+config-resource-quotas"; import { ResourceQuotas } from "../+config-resource-quotas";
import { PodDisruptionBudgets } from "../+config-pod-disruption-budgets"; import { PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
import { HorizontalPodAutoscalers } from "../+config-autoscalers"; import { HorizontalPodAutoscalers } from "../+config-autoscalers";
import { isAllowedResource } from "../../../common/rbac"; import { isAllowedResource } from "../../../common/utils/allowed-resource";
import { LimitRanges } from "../+config-limit-ranges"; import { LimitRanges } from "../+config-limit-ranges";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";

View File

@ -28,10 +28,10 @@ import type { CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { AceEditor } from "../ace-editor"; import { AceEditor } from "../ace-editor";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { DrawerItem, DrawerTitle } from "../drawer"; 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 { Table, TableCell, TableHead, TableRow } from "../table";
import { Input } from "../input"; import { Input } from "../input";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<CustomResourceDefinition> { interface Props extends KubeObjectDetailsProps<CustomResourceDefinition> {
} }

View File

@ -26,7 +26,7 @@ import { computed, makeObservable } from "mobx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { stopPropagation } from "../../utils"; import { stopPropagation } from "../../utils";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { crdStore } from "./crd.store"; import { crdStore } from "./crd.store";
import type { CustomResourceDefinition } from "../../api/endpoints/crd.api"; import type { CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { Select, SelectOption } from "../select"; import { Select, SelectOption } from "../select";
@ -116,7 +116,7 @@ export class CrdList extends React.Component {
controlShouldRenderValue={false} controlShouldRenderValue={false}
formatOptionLabel={({ value: group }: SelectOption) => { formatOptionLabel={({ value: group }: SelectOption) => {
const isSelected = selectedGroups.includes(group); const isSelected = selectedGroups.includes(group);
return ( return (
<div className="flex gaps align-center"> <div className="flex gaps align-center">
<Icon small material="folder"/> <Icon small material="folder"/>

View File

@ -28,9 +28,9 @@ import { computed, makeObservable } from "mobx";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { crdStore } from "./crd.store"; import { crdStore } from "./crd.store";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { Input } from "../input"; import { Input } from "../input";
import type { AdditionalPrinterColumnsV1, CustomResourceDefinition } from "../../api/endpoints/crd.api"; import type { AdditionalPrinterColumnsV1, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { parseJsonPath } from "../../utils/jsonPath"; import { parseJsonPath } from "../../utils/jsonPath";

View File

@ -25,7 +25,7 @@ import React from "react";
import jsonPath from "jsonpath"; import jsonPath from "jsonpath";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router"; 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 type { KubeObject } from "../../api/kube-object";
import { computed, makeObservable } from "mobx"; import { computed, makeObservable } from "mobx";
import { crdStore } from "./crd.store"; import { crdStore } from "./crd.store";

View File

@ -26,12 +26,13 @@ import kebabCase from "lodash/kebabCase";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { KubeObjectDetailsProps, getDetailsUrl } from "../kube-object"; import type { KubeObjectDetailsProps} from "../kube-object-details";
import { getDetailsUrl } from "../kube-details";
import type { KubeEvent } from "../../api/endpoints/events.api"; import type { KubeEvent } from "../../api/endpoints/events.api";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { lookupApiLink } from "../../api/kube-api";
import { LocaleDate } from "../locale-date"; import { LocaleDate } from "../locale-date";
import { apiManager } from "../../api/api-manager";
interface Props extends KubeObjectDetailsProps<KubeEvent> { interface Props extends KubeObjectDetailsProps<KubeEvent> {
} }
@ -81,7 +82,7 @@ export class EventDetails extends React.Component<Props> {
</TableHead> </TableHead>
<TableRow> <TableRow>
<TableCell> <TableCell>
<Link to={getDetailsUrl(lookupApiLink(involvedObject, event))}> <Link to={getDetailsUrl(apiManager.lookupApiLink(involvedObject, event))}>
{name} {name}
</Link> </Link>
</TableCell> </TableCell>

View File

@ -27,7 +27,6 @@ import { observer } from "mobx-react";
import { orderBy } from "lodash"; import { orderBy } from "lodash";
import { TabLayout } from "../layout/tab-layout"; import { TabLayout } from "../layout/tab-layout";
import { EventStore, eventStore } from "./event.store"; import { EventStore, eventStore } from "./event.store";
import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object";
import type { KubeEvent } from "../../api/endpoints/events.api"; import type { KubeEvent } from "../../api/endpoints/events.api";
import type { TableSortCallbacks, TableSortParams } from "../table"; import type { TableSortCallbacks, TableSortParams } from "../table";
import type { HeaderCustomizer } from "../item-object-list"; import type { HeaderCustomizer } from "../item-object-list";
@ -35,8 +34,10 @@ import { Tooltip } from "../tooltip";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { cssNames, IClassName, stopPropagation } from "../../utils"; import { cssNames, IClassName, stopPropagation } from "../../utils";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { lookupApiLink } from "../../api/kube-api";
import { eventsURL } from "../../../common/routes"; 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 { enum columnId {
message = "message", message = "message",
@ -133,7 +134,7 @@ export class Events extends React.Component<Props> {
tooltip={`Limited to ${store.limit}`} tooltip={`Limited to ${store.limit}`}
/> />
</>, </>,
title, title,
...headerPlaceholders ...headerPlaceholders
}; };
}; };
@ -195,7 +196,7 @@ export class Events extends React.Component<Props> {
) )
}, },
event.getNs(), 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} {involvedObject.kind}: {involvedObject.name}
</Link>, </Link>,
event.getSource(), event.getSource(),

View File

@ -27,16 +27,17 @@ import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem } from "../drawer"; import { DrawerItem } from "../drawer";
import { boundMethod, cssNames } from "../../utils"; import { boundMethod, cssNames } from "../../utils";
import { getMetricsForNamespace, IPodMetrics, Namespace } from "../../api/endpoints"; import { getMetricsForNamespace, IPodMetrics, Namespace } from "../../api/endpoints";
import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
import { resourceQuotaStore } from "../+config-resource-quotas/resource-quotas.store"; import { resourceQuotaStore } from "../+config-resource-quotas/resource-quotas.store";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { limitRangeStore } from "../+config-limit-ranges/limit-ranges.store"; import { limitRangeStore } from "../+config-limit-ranges/limit-ranges.store";
import { ResourceMetrics } from "../resource-metrics";
import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts";
import { getDetailsUrl } from "../kube-details";
import { ClusterMetricsResourceType } from "../../../common/cluster-types"; import { ClusterMetricsResourceType } from "../../../common/cluster-types";
import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
import { ResourceMetrics } from "../resource-metrics";
interface Props extends KubeObjectDetailsProps<Namespace> { interface Props extends KubeObjectDetailsProps<Namespace> {
} }

View File

@ -27,7 +27,7 @@ import { AddNamespaceDialog } from "./add-namespace-dialog";
import { TabLayout } from "../layout/tab-layout"; import { TabLayout } from "../layout/tab-layout";
import { Badge } from "../badge"; import { Badge } from "../badge";
import type { RouteComponentProps } from "react-router"; import type { RouteComponentProps } from "react-router";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { namespaceStore } from "./namespace.store"; import { namespaceStore } from "./namespace.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { NamespacesRouteParams } from "../../../common/routes"; import type { NamespacesRouteParams } from "../../../common/routes";

View File

@ -24,9 +24,9 @@ import "./endpoint-details.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { DrawerTitle } from "../drawer"; import { DrawerTitle } from "../drawer";
import type { KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import type { Endpoint } from "../../api/endpoints"; import type { Endpoint } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { EndpointSubsetList } from "./endpoint-subset-list"; import { EndpointSubsetList } from "./endpoint-subset-list";
interface Props extends KubeObjectDetailsProps<Endpoint> { interface Props extends KubeObjectDetailsProps<Endpoint> {

View File

@ -26,9 +26,9 @@ import { observer } from "mobx-react";
import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints"; import { EndpointSubset, Endpoint, EndpointAddress} from "../../api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { boundMethod } from "../../utils"; import { boundMethod } from "../../utils";
import { lookupApiLink } from "../../api/kube-api";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { getDetailsUrl } from "../kube-object"; import { getDetailsUrl } from "../kube-details";
import { apiManager } from "../../api/api-manager";
interface Props { interface Props {
subset: EndpointSubset; subset: EndpointSubset;
@ -92,7 +92,7 @@ export class EndpointSubsetList extends React.Component<Props> {
<TableCell className="name">{address.hostname}</TableCell> <TableCell className="name">{address.hostname}</TableCell>
<TableCell className="target"> <TableCell className="target">
{ address.targetRef && ( { address.targetRef && (
<Link to={getDetailsUrl(lookupApiLink(address.getTargetRef(), endpoint))}> <Link to={getDetailsUrl(apiManager.lookupApiLink(address.getTargetRef(), endpoint))}>
{address.targetRef.name} {address.targetRef.name}
</Link> </Link>
)} )}

View File

@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router-dom"; import type { RouteComponentProps } from "react-router-dom";
import { endpointStore } from "./endpoints.store"; 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 { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { EndpointRouteParams } from "../../../common/routes"; import type { EndpointRouteParams } from "../../../common/routes";

View File

@ -28,9 +28,9 @@ import { DrawerItem, DrawerTitle } from "../drawer";
import type { ILoadBalancerIngress, Ingress } from "../../api/endpoints"; import type { ILoadBalancerIngress, Ingress } from "../../api/endpoints";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { ResourceMetrics } from "../resource-metrics"; import { ResourceMetrics } from "../resource-metrics";
import type { KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { IngressCharts } from "./ingress-charts"; import { IngressCharts } from "./ingress-charts";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { getBackendServiceNamePort, getMetricsForIngress, IIngressMetrics } from "../../api/endpoints/ingress.api"; import { getBackendServiceNamePort, getMetricsForIngress, IIngressMetrics } from "../../api/endpoints/ingress.api";
import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
import { ClusterMetricsResourceType } from "../../../common/cluster-types"; import { ClusterMetricsResourceType } from "../../../common/cluster-types";

View File

@ -25,7 +25,7 @@ import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router-dom"; import type { RouteComponentProps } from "react-router-dom";
import { ingressStore } from "./ingress.store"; 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 { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { IngressRouteParams } from "../../../common/routes"; import type { IngressRouteParams } from "../../../common/routes";

View File

@ -24,7 +24,7 @@ import "./network-policies.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router-dom"; import type { RouteComponentProps } from "react-router-dom";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { networkPolicyStore } from "./network-policy.store"; import { networkPolicyStore } from "./network-policy.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { NetworkPoliciesRouteParams } from "../../../common/routes"; import type { NetworkPoliciesRouteParams } from "../../../common/routes";

View File

@ -28,8 +28,8 @@ import type { IPolicyEgress, IPolicyIngress, IPolicyIpBlock, IPolicySelector, Ne
import { Badge } from "../badge"; import { Badge } from "../badge";
import { SubTitle } from "../layout/sub-title"; import { SubTitle } from "../layout/sub-title";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<NetworkPolicy> { interface Props extends KubeObjectDetailsProps<NetworkPolicy> {
} }

View File

@ -26,7 +26,7 @@ import { Table, TableHead, TableCell, TableRow } from "../table";
import { prevDefault } from "../../utils"; import { prevDefault } from "../../utils";
import { endpointStore } from "../+network-endpoints/endpoints.store"; import { endpointStore } from "../+network-endpoints/endpoints.store";
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
import { showDetails } from "../kube-object"; import { showDetails } from "../kube-details";
interface Props { interface Props {
endpoint: KubeObject; endpoint: KubeObject;

View File

@ -25,9 +25,9 @@ import React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import type { KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import type { Service } from "../../api/endpoints"; import type { Service } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { ServicePortComponent } from "./service-port-component"; import { ServicePortComponent } from "./service-port-component";
import { endpointStore } from "../+network-endpoints/endpoints.store"; import { endpointStore } from "../+network-endpoints/endpoints.store";
import { ServiceDetailsEndpoint } from "./service-details-endpoint"; import { ServiceDetailsEndpoint } from "./service-details-endpoint";

View File

@ -24,7 +24,7 @@ import "./services.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { RouteComponentProps } from "react-router"; import type { RouteComponentProps } from "react-router";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { serviceStore } from "./services.store"; import { serviceStore } from "./services.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";

View File

@ -28,7 +28,7 @@ import { Services } from "../+network-services";
import { Endpoints } from "../+network-endpoints"; import { Endpoints } from "../+network-endpoints";
import { Ingresses } from "../+network-ingresses"; import { Ingresses } from "../+network-ingresses";
import { NetworkPolicies } from "../+network-policies"; import { NetworkPolicies } from "../+network-policies";
import { isAllowedResource } from "../../../common/rbac"; import { isAllowedResource } from "../../../common/utils/allowed-resource";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";
@observer @observer

View File

@ -29,12 +29,12 @@ import { DrawerItem, DrawerItemLabels } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { ResourceMetrics } from "../resource-metrics"; import { ResourceMetrics } from "../resource-metrics";
import { podsStore } from "../+workloads-pods/pods.store"; import { podsStore } from "../+workloads-pods/pods.store";
import type { KubeObjectDetailsProps } from "../kube-object";
import { getMetricsByNodeNames, IClusterMetrics, Node } from "../../api/endpoints"; import { getMetricsByNodeNames, IClusterMetrics, Node } from "../../api/endpoints";
import type { KubeObjectDetailsProps } from "../kube-object-details";
import { NodeCharts } from "./node-charts"; import { NodeCharts } from "./node-charts";
import { makeObservable, observable, reaction } from "mobx"; import { makeObservable, observable, reaction } from "mobx";
import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { PodDetailsList } from "../+workloads-pods/pod-details-list";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
import { ClusterMetricsResourceType } from "../../../common/cluster-types"; import { ClusterMetricsResourceType } from "../../../common/cluster-types";
import { NodeDetailsResources } from "./node-details-resources"; import { NodeDetailsResources } from "./node-details-resources";

View File

@ -27,8 +27,8 @@ import { cssNames, interval } from "../../utils";
import { TabLayout } from "../layout/tab-layout"; import { TabLayout } from "../layout/tab-layout";
import { nodesStore } from "./nodes.store"; import { nodesStore } from "./nodes.store";
import { podsStore } from "../+workloads-pods/pods.store"; import { podsStore } from "../+workloads-pods/pods.store";
import { KubeObjectListLayout } from "../kube-object";
import { getMetricsForAllNodes, INodeMetrics, Node } from "../../api/endpoints/nodes.api"; import { getMetricsForAllNodes, INodeMetrics, Node } from "../../api/endpoints/nodes.api";
import { KubeObjectListLayout } from "../kube-object-list-layout";
import { LineProgress } from "../line-progress"; import { LineProgress } from "../line-progress";
import { bytesToUnits } from "../../utils/convertMemory"; import { bytesToUnits } from "../../utils/convertMemory";
import { Tooltip, TooltipPosition } from "../tooltip"; import { Tooltip, TooltipPosition } from "../tooltip";

View File

@ -23,7 +23,7 @@ import "./pod-security-policies.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-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 { podSecurityPoliciesStore } from "./pod-security-policies.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";

View File

@ -24,11 +24,11 @@ import "./pod-security-policy-details.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer"; 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 type { PodSecurityPolicy } from "../../api/endpoints";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { Table, TableCell, TableHead, TableRow } from "../table"; import { Table, TableCell, TableHead, TableRow } from "../table";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<PodSecurityPolicy> { interface Props extends KubeObjectDetailsProps<PodSecurityPolicy> {
} }

View File

@ -26,7 +26,7 @@ import moment from "moment-timezone";
import { computed, observable, reaction, makeObservable } from "mobx"; import { computed, observable, reaction, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { isWindows } from "../../../common/vars"; import { isWindows, sentryDsn } from "../../../common/vars";
import { AppPreferenceRegistry, RegisteredAppPreference } from "../../../extensions/registries/app-preference-registry"; import { AppPreferenceRegistry, RegisteredAppPreference } from "../../../extensions/registries/app-preference-registry";
import { UserStore } from "../../../common/user-store"; import { UserStore } from "../../../common/user-store";
import { ThemeStore } from "../../theme.store"; import { ThemeStore } from "../../theme.store";
@ -41,7 +41,6 @@ import { FormSwitch, Switcher } from "../switch";
import { KubeconfigSyncs } from "./kubeconfig-syncs"; import { KubeconfigSyncs } from "./kubeconfig-syncs";
import { SettingLayout } from "../layout/setting-layout"; import { SettingLayout } from "../layout/setting-layout";
import { Checkbox } from "../checkbox"; import { Checkbox } from "../checkbox";
import { sentryDsn } from "../../../common/vars";
enum Pages { enum Pages {
Application = "application", Application = "application",

View File

@ -26,9 +26,9 @@ import startCase from "lodash/startCase";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { observer } from "mobx-react"; 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 type { StorageClass } from "../../api/endpoints";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../kube-object-meta";
import { storageClassStore } from "./storage-class.store"; import { storageClassStore } from "./storage-class.store";
import { VolumeDetailsList } from "../+storage-volumes/volume-details-list"; import { VolumeDetailsList } from "../+storage-volumes/volume-details-list";
import { volumesStore } from "../+storage-volumes/volumes.store"; import { volumesStore } from "../+storage-volumes/volumes.store";

View File

@ -24,7 +24,7 @@ import "./storage-classes.scss";
import React from "react"; import React from "react";
import type { RouteComponentProps } from "react-router-dom"; import type { RouteComponentProps } from "react-router-dom";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object-list-layout";
import { storageClassStore } from "./storage-class.store"; import { storageClassStore } from "./storage-class.store";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { StorageClassesRouteParams } from "../../../common/routes"; import type { StorageClassesRouteParams } from "../../../common/routes";

View File

@ -30,10 +30,12 @@ import { podsStore } from "../+workloads-pods/pods.store";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { ResourceMetrics } from "../resource-metrics"; import { ResourceMetrics } from "../resource-metrics";
import { VolumeClaimDiskChart } from "./volume-claim-disk-chart"; import { VolumeClaimDiskChart } from "./volume-claim-disk-chart";
import { getDetailsUrl, KubeObjectDetailsProps, KubeObjectMeta } from "../kube-object";
import { getMetricsForPvc, IPvcMetrics, PersistentVolumeClaim } from "../../api/endpoints"; import { getMetricsForPvc, IPvcMetrics, PersistentVolumeClaim } from "../../api/endpoints";
import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry";
import { ClusterMetricsResourceType } from "../../../common/cluster-types"; import { ClusterMetricsResourceType } from "../../../common/cluster-types";
import { getDetailsUrl } from "../kube-details";
import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<PersistentVolumeClaim> { interface Props extends KubeObjectDetailsProps<PersistentVolumeClaim> {
} }

View File

@ -26,12 +26,13 @@ import { observer } from "mobx-react";
import { Link, RouteComponentProps } from "react-router-dom"; import { Link, RouteComponentProps } from "react-router-dom";
import { volumeClaimStore } from "./volume-claim.store"; import { volumeClaimStore } from "./volume-claim.store";
import { podsStore } from "../+workloads-pods/pods.store"; 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 { unitsToBytes } from "../../utils/convertMemory";
import { stopPropagation } from "../../utils"; import { stopPropagation } from "../../utils";
import { storageClassApi } from "../../api/endpoints"; import { storageClassApi } from "../../api/endpoints";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { VolumeClaimsRouteParams } from "../../../common/routes"; import type { VolumeClaimsRouteParams } from "../../../common/routes";
import { getDetailsUrl } from "../kube-details";
enum columnId { enum columnId {
name = "name", name = "name",

View File

@ -27,7 +27,7 @@ import type { PersistentVolume } from "../../api/endpoints/persistent-volume.api
import { boundMethod } from "../../../common/utils/autobind"; import { boundMethod } from "../../../common/utils/autobind";
import { TableRow } from "../table/table-row"; import { TableRow } from "../table/table-row";
import { cssNames, prevDefault } from "../../utils"; import { cssNames, prevDefault } from "../../utils";
import { showDetails } from "../kube-object/kube-object-details"; import { showDetails } from "../kube-details";
import { TableCell } from "../table/table-cell"; import { TableCell } from "../table/table-cell";
import { Spinner } from "../spinner/spinner"; import { Spinner } from "../spinner/spinner";
import { DrawerTitle } from "../drawer/drawer-title"; import { DrawerTitle } from "../drawer/drawer-title";

View File

@ -28,8 +28,9 @@ import { observer } from "mobx-react";
import { DrawerItem, DrawerTitle } from "../drawer"; import { DrawerItem, DrawerTitle } from "../drawer";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { PersistentVolume, pvcApi } from "../../api/endpoints"; import { PersistentVolume, pvcApi } from "../../api/endpoints";
import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; import type { KubeObjectDetailsProps } from "../kube-object-details";
import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { getDetailsUrl} from "../kube-details";
import { KubeObjectMeta } from "../kube-object-meta";
interface Props extends KubeObjectDetailsProps<PersistentVolume> { interface Props extends KubeObjectDetailsProps<PersistentVolume> {
} }

View File

@ -24,12 +24,13 @@ import "./volumes.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Link, RouteComponentProps } from "react-router-dom"; import { Link, RouteComponentProps } from "react-router-dom";
import { getDetailsUrl, KubeObjectListLayout } from "../kube-object";
import { stopPropagation } from "../../utils"; import { stopPropagation } from "../../utils";
import { volumesStore } from "./volumes.store"; import { volumesStore } from "./volumes.store";
import { pvcApi, storageClassApi } from "../../api/endpoints"; import { pvcApi, storageClassApi } from "../../api/endpoints";
import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../kube-object-status-icon";
import type { VolumesRouteParams } from "../../../common/routes"; import type { VolumesRouteParams } from "../../../common/routes";
import { getDetailsUrl } from "../kube-details";
import { KubeObjectListLayout } from "../kube-object-list-layout";
enum columnId { enum columnId {
name = "name", name = "name",

View File

@ -27,7 +27,7 @@ import { TabLayout, TabLayoutRoute } from "../layout/tab-layout";
import { PersistentVolumes } from "../+storage-volumes"; import { PersistentVolumes } from "../+storage-volumes";
import { StorageClasses } from "../+storage-classes"; import { StorageClasses } from "../+storage-classes";
import { PersistentVolumeClaims } from "../+storage-volume-claims"; import { PersistentVolumeClaims } from "../+storage-volume-claims";
import { isAllowedResource } from "../../../common/rbac"; import { isAllowedResource } from "../../../common/utils/allowed-resource";
import * as routes from "../../../common/routes"; import * as routes from "../../../common/routes";
@observer @observer

View File

@ -30,8 +30,8 @@ import { autoBind, ObservableHashSet, prevDefault } from "../../../utils";
import { AddRemoveButtons } from "../../add-remove-buttons"; import { AddRemoveButtons } from "../../add-remove-buttons";
import { ConfirmDialog } from "../../confirm-dialog"; import { ConfirmDialog } from "../../confirm-dialog";
import { DrawerTitle } from "../../drawer"; import { DrawerTitle } from "../../drawer";
import type { KubeObjectDetailsProps } from "../../kube-object"; import type { KubeObjectDetailsProps } from "../../kube-object-details";
import { KubeObjectMeta } from "../../kube-object/kube-object-meta"; import { KubeObjectMeta } from "../../kube-object-meta";
import { Table, TableCell, TableHead, TableRow } from "../../table"; import { Table, TableCell, TableHead, TableRow } from "../../table";
import { ClusterRoleBindingDialog } from "./dialog"; import { ClusterRoleBindingDialog } from "./dialog";
import { clusterRoleBindingsStore } from "./store"; import { clusterRoleBindingsStore } from "./store";

View File

@ -30,7 +30,7 @@ import { ClusterRole, ClusterRoleBinding, ClusterRoleBindingSubject, ServiceAcco
import { Dialog, DialogProps } from "../../dialog"; import { Dialog, DialogProps } from "../../dialog";
import { EditableList } from "../../editable-list"; import { EditableList } from "../../editable-list";
import { Icon } from "../../icon"; import { Icon } from "../../icon";
import { showDetails } from "../../kube-object"; import { showDetails } from "../../kube-details";
import { SubTitle } from "../../layout/sub-title"; import { SubTitle } from "../../layout/sub-title";
import { Notifications } from "../../notifications"; import { Notifications } from "../../notifications";
import { Select, SelectOption } from "../../select"; import { Select, SelectOption } from "../../select";
@ -202,13 +202,13 @@ export class ClusterRoleBindingDialog extends React.Component<Props> {
if (!this.selectedRoleRef || this.bindingName === this.selectedRoleRef.getName()) { if (!this.selectedRoleRef || this.bindingName === this.selectedRoleRef.getName()) {
this.bindingName = value.getName(); this.bindingName = value.getName();
} }
this.selectedRoleRef = value; this.selectedRoleRef = value;
}} }}
/> />
<SubTitle title="Binding Name" /> <SubTitle title="Binding Name" />
<Input <Input
placeholder="Name of ClusterRoleBinding ..." placeholder="Name of ClusterRoleBinding ..."
disabled={this.isEditing} disabled={this.isEditing}
value={this.bindingName} value={this.bindingName}

View File

@ -24,7 +24,7 @@ import "./view.scss";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import React from "react"; import React from "react";
import type { RouteComponentProps } from "react-router"; import type { RouteComponentProps } from "react-router";
import { KubeObjectListLayout } from "../../kube-object"; import { KubeObjectListLayout } from "../../kube-object-list-layout";
import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon";
import { ClusterRoleBindingDialog } from "./dialog"; import { ClusterRoleBindingDialog } from "./dialog";
import { clusterRoleBindingsStore } from "./store"; import { clusterRoleBindingsStore } from "./store";

View File

@ -26,7 +26,7 @@ import React from "react";
import { Dialog, DialogProps } from "../../dialog"; import { Dialog, DialogProps } from "../../dialog";
import { Input } from "../../input"; import { Input } from "../../input";
import { showDetails } from "../../kube-object"; import { showDetails } from "../../kube-details";
import { SubTitle } from "../../layout/sub-title"; import { SubTitle } from "../../layout/sub-title";
import { Notifications } from "../../notifications"; import { Notifications } from "../../notifications";
import { Wizard, WizardStep } from "../../wizard"; import { Wizard, WizardStep } from "../../wizard";

Some files were not shown because too many files have changed in this diff Show More