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

Add support for customreasourcedefinitions under the v1 API

Signed-off-by: Sebastian Malton <smalton@mirantis.com>
This commit is contained in:
Sebastian Malton 2020-08-07 10:52:11 -04:00
parent 0c3be9bbae
commit 2af58cde72
11 changed files with 177 additions and 150 deletions

View File

@ -274,6 +274,7 @@ msgstr "CA Bundle"
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:53 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:53
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:95 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:95
#: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13
#: src/renderer/components/+nodes/nodes.tsx:55
msgid "CPU" msgid "CPU"
msgstr "CPU" msgstr "CPU"
@ -296,10 +297,7 @@ msgstr "CPU limits"
msgid "CPU requests" msgid "CPU requests"
msgstr "CPU requests" msgstr "CPU requests"
#: src/renderer/components/+nodes/nodes.tsx:55 #: src/renderer/components/+workspaces/workspaces.tsx:119
msgid "CPU:"
msgstr "CPU:"
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97 #: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/wizard/wizard.tsx:130 #: src/renderer/components/wizard/wizard.tsx:130
@ -665,12 +663,13 @@ msgstr "Desired number of replicas"
#: src/renderer/components/+nodes/node-details.tsx:65 #: src/renderer/components/+nodes/node-details.tsx:65
#: src/renderer/components/+nodes/nodes.tsx:115 #: src/renderer/components/+nodes/nodes.tsx:115
#: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44 #: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44
#: src/renderer/components/+nodes/nodes.tsx:71
msgid "Disk" msgid "Disk"
msgstr "Disk" msgstr "Disk"
#: src/renderer/components/+nodes/nodes.tsx:71 #: src/renderer/components/+preferences/preferences.tsx:171
msgid "Disk:" msgid "Does not affect cluster communications!"
msgstr "Disk:" msgstr "Does not affect cluster communications!"
#: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89 #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89
msgid "Domains" msgid "Domains"
@ -1146,6 +1145,7 @@ msgstr "Medium"
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:63 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:63
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:96 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:96
#: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18
#: src/renderer/components/+nodes/nodes.tsx:63
msgid "Memory" msgid "Memory"
msgstr "Memory" msgstr "Memory"
@ -1167,10 +1167,6 @@ msgstr "Memory requests"
msgid "Memory usage" msgid "Memory usage"
msgstr "Memory usage" msgstr "Memory usage"
#: src/renderer/components/+nodes/nodes.tsx:63
msgid "Memory:"
msgstr "Memory:"
#: src/renderer/components/+cluster/cluster-issues.tsx:100 #: src/renderer/components/+cluster/cluster-issues.tsx:100
#: src/renderer/components/+events/event-details.tsx:30 #: src/renderer/components/+events/event-details.tsx:30
#: src/renderer/components/+events/events.tsx:62 #: src/renderer/components/+events/events.tsx:62
@ -2393,8 +2389,8 @@ msgid "and <0>{tailCount}</0> more"
msgstr "and <0>{tailCount}</0> more" msgstr "and <0>{tailCount}</0> more"
#: src/renderer/components/+nodes/nodes.tsx:55 #: src/renderer/components/+nodes/nodes.tsx:55
msgid "cores:" msgid "cores"
msgstr "cores:" msgstr "cores"
#: src/renderer/components/+workloads-pods/pod-details-container.tsx:41 #: src/renderer/components/+workloads-pods/pod-details-container.tsx:41
msgid "exit code" msgid "exit code"

View File

@ -274,6 +274,7 @@ msgstr ""
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:53 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:53
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:95 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:95
#: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13
#: src/renderer/components/+nodes/nodes.tsx:55
msgid "CPU" msgid "CPU"
msgstr "" msgstr ""
@ -296,10 +297,7 @@ msgstr ""
msgid "CPU requests" msgid "CPU requests"
msgstr "" msgstr ""
#: src/renderer/components/+nodes/nodes.tsx:55 #: src/renderer/components/+workspaces/workspaces.tsx:119
msgid "CPU:"
msgstr ""
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97 #: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/wizard/wizard.tsx:130 #: src/renderer/components/wizard/wizard.tsx:130
@ -661,11 +659,8 @@ msgstr ""
#: src/renderer/components/+nodes/node-details.tsx:65 #: src/renderer/components/+nodes/node-details.tsx:65
#: src/renderer/components/+nodes/nodes.tsx:115 #: src/renderer/components/+nodes/nodes.tsx:115
#: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44 #: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44
msgid "Disk"
msgstr ""
#: src/renderer/components/+nodes/nodes.tsx:71 #: src/renderer/components/+nodes/nodes.tsx:71
msgid "Disk:" msgid "Disk"
msgstr "" msgstr ""
#: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89 #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89
@ -1137,6 +1132,7 @@ msgstr ""
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:63 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:63
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:96 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:96
#: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18
#: src/renderer/components/+nodes/nodes.tsx:63
msgid "Memory" msgid "Memory"
msgstr "" msgstr ""
@ -1158,10 +1154,6 @@ msgstr ""
msgid "Memory usage" msgid "Memory usage"
msgstr "" msgstr ""
#: src/renderer/components/+nodes/nodes.tsx:63
msgid "Memory:"
msgstr ""
#: src/renderer/components/+cluster/cluster-issues.tsx:100 #: src/renderer/components/+cluster/cluster-issues.tsx:100
#: src/renderer/components/+events/event-details.tsx:30 #: src/renderer/components/+events/event-details.tsx:30
#: src/renderer/components/+events/events.tsx:62 #: src/renderer/components/+events/events.tsx:62
@ -2376,7 +2368,7 @@ msgid "and <0>{tailCount}</0> more"
msgstr "" msgstr ""
#: src/renderer/components/+nodes/nodes.tsx:55 #: src/renderer/components/+nodes/nodes.tsx:55
msgid "cores:" msgid "cores"
msgstr "" msgstr ""
#: src/renderer/components/+workloads-pods/pod-details-container.tsx:41 #: src/renderer/components/+workloads-pods/pod-details-container.tsx:41

View File

@ -275,6 +275,7 @@ msgstr "CA Bundle"
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:53 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:53
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:95 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:95
#: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13
#: src/renderer/components/+nodes/nodes.tsx:55
msgid "CPU" msgid "CPU"
msgstr "Процессор" msgstr "Процессор"
@ -297,10 +298,7 @@ msgstr "Лимиты процессора"
msgid "CPU requests" msgid "CPU requests"
msgstr "Запросы к процессору" msgstr "Запросы к процессору"
#: src/renderer/components/+nodes/nodes.tsx:55 #: src/renderer/components/+workspaces/workspaces.tsx:119
msgid "CPU:"
msgstr "CPU:"
#: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44
#: src/renderer/components/dock/info-panel.tsx:97 #: src/renderer/components/dock/info-panel.tsx:97
#: src/renderer/components/wizard/wizard.tsx:130 #: src/renderer/components/wizard/wizard.tsx:130
@ -666,12 +664,13 @@ msgstr "Нужный уровень реплик"
#: src/renderer/components/+nodes/node-details.tsx:65 #: src/renderer/components/+nodes/node-details.tsx:65
#: src/renderer/components/+nodes/nodes.tsx:115 #: src/renderer/components/+nodes/nodes.tsx:115
#: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44 #: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44
#: src/renderer/components/+nodes/nodes.tsx:71
msgid "Disk" msgid "Disk"
msgstr "Диск" msgstr "Диск"
#: src/renderer/components/+nodes/nodes.tsx:71 #: src/renderer/components/+preferences/preferences.tsx:171
msgid "Disk:" msgid "Does not affect cluster communications!"
msgstr "Диск:" msgstr ""
#: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89 #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89
msgid "Domains" msgid "Domains"
@ -1147,6 +1146,7 @@ msgstr ""
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:63 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:63
#: src/renderer/components/+workloads-pods/pod-details-list.tsx:96 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:96
#: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18
#: src/renderer/components/+nodes/nodes.tsx:63
msgid "Memory" msgid "Memory"
msgstr "Память" msgstr "Память"
@ -1168,10 +1168,6 @@ msgstr "Запросы к памяти"
msgid "Memory usage" msgid "Memory usage"
msgstr "Использование памяти" msgstr "Использование памяти"
#: src/renderer/components/+nodes/nodes.tsx:63
msgid "Memory:"
msgstr "Память:"
#: src/renderer/components/+cluster/cluster-issues.tsx:100 #: src/renderer/components/+cluster/cluster-issues.tsx:100
#: src/renderer/components/+events/event-details.tsx:30 #: src/renderer/components/+events/event-details.tsx:30
#: src/renderer/components/+events/events.tsx:62 #: src/renderer/components/+events/events.tsx:62
@ -2394,8 +2390,8 @@ msgid "and <0>{tailCount}</0> more"
msgstr "и <0>{tailCount}</0> ещё" msgstr "и <0>{tailCount}</0> ещё"
#: src/renderer/components/+nodes/nodes.tsx:55 #: src/renderer/components/+nodes/nodes.tsx:55
msgid "cores:" msgid "cores"
msgstr "ядер:" msgstr "ядер"
#: src/renderer/components/+workloads-pods/pod-details-container.tsx:41 #: src/renderer/components/+workloads-pods/pod-details-container.tsx:41
msgid "exit code" msgid "exit code"

View File

@ -20,16 +20,31 @@ export class ApiManager {
getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) {
if (typeof pathOrCallback === "string") { if (typeof pathOrCallback === "string") {
return this.apis.get(pathOrCallback) || this.apis.get(KubeApi.parseApi(pathOrCallback).apiBase); const { apiBase } = KubeApi.parseApi(pathOrCallback);
const api = this.apis.get(pathOrCallback) || this.apis.get(apiBase);
if (!api) {
throw `"${apiBase}" is an unsupported kubernetes API`;
}
return api;
} }
return Array.from(this.apis.values()).find(pathOrCallback); return Array.from(this.apis.values()).find(pathOrCallback);
} }
registerApi(apiBase: string, api: KubeApi) { /**
if (!this.apis.has(apiBase)) { * registerApi registers the provided api under its `apiBase` URL.
this.apis.set(apiBase, api); * @param api the KubeApi object to register
* @returns true if the KubeApi is a new entry, false if already
* present (and not updated)
*/
registerApi(api: KubeApi): boolean {
if (this.apis.has(api.apiBase)) {
return false
} }
this.apis.set(api.apiBase, api);
return true;
} }
protected resolveApi(api: string | KubeApi): KubeApi { protected resolveApi(api: string | KubeApi): KubeApi {
@ -37,13 +52,17 @@ export class ApiManager {
return api; return api;
} }
unregisterApi(api: string | KubeApi) { /**
if (typeof api === "string") this.apis.delete(api); * unregisterApi removes the
else { * @param api the apiBase or KubeApi object to remove from the map
const apis = Array.from(this.apis.entries()); * @returns true if the item was removed, false if not present
const entry = apis.find(entry => entry[1] === api); */
if (entry) this.unregisterApi(entry[0]); unregisterApi(api: string | KubeApi): boolean {
if (typeof api === "string") {
return this.apis.delete(api)
} }
return this.apis.delete(api.apiBase);
} }
registerStore(api: KubeApi, store: KubeObjectStore) { registerStore(api: KubeApi, store: KubeObjectStore) {
@ -54,11 +73,7 @@ export class ApiManager {
return this.stores.get(this.resolveApi(api)); return this.stores.get(this.resolveApi(api));
} }
registerViews(api: KubeApi | KubeApi[], views: ApiComponents) { private registerViewsForApi(api: KubeApi, views: ApiComponents) {
if (Array.isArray(api)) {
api.forEach(api => this.registerViews(api, views));
return;
}
const currentViews = this.views.get(api) || {}; const currentViews = this.views.get(api) || {};
this.views.set(api, { this.views.set(api, {
...currentViews, ...currentViews,
@ -66,6 +81,14 @@ export class ApiManager {
}); });
} }
registerViews(api: KubeApi | KubeApi[], views: ApiComponents) {
if (Array.isArray(api)) {
api.forEach(api => this.registerViewsForApi(api, views));
} else {
this.registerViewsForApi(api, views);
}
}
getViews(api: string | KubeApi): ApiComponents { getViews(api: string | KubeApi): ApiComponents {
return this.views.get(this.resolveApi(api)) || {} return this.views.get(this.resolveApi(api)) || {}
} }

View File

@ -130,9 +130,16 @@ export class CustomResourceDefinition extends KubeObject {
} }
} }
export const crdApi = new KubeApi<CustomResourceDefinition>({ export const crdBetaApi = new KubeApi<CustomResourceDefinition>({
kind: CustomResourceDefinition.kind, kind: CustomResourceDefinition.kind,
apiBase: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions", apiBase: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions",
isNamespaced: false, isNamespaced: false,
objectConstructor: CustomResourceDefinition, objectConstructor: CustomResourceDefinition,
}); });
export const crdApi = new KubeApi<CustomResourceDefinition>({
kind: CustomResourceDefinition.kind,
apiBase: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions",
isNamespaced: false,
objectConstructor: CustomResourceDefinition,
});

View File

@ -123,13 +123,25 @@ const tests: KubeApi_Parse_Test[] = [
namespace: undefined, namespace: undefined,
}, },
}, },
{
url: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/workspaceroles.iam.kubesphere.io",
expected: {
apiBase: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions",
apiPrefix: "/apis",
apiGroup: "apiextensions.k8s.io",
apiVersion: "v1",
apiVersionWithGroup: "apiextensions.k8s.io/v1",
resource: "customresourcedefinitions",
name: "workspaceroles.iam.kubesphere.io",
namespace: undefined,
},
},
]; ];
describe.only("parseApi unit tests", () => { describe.only("parseApi unit tests", () => {
for (const i in tests) { for (const { url, expected } of tests) {
const { url: tUrl, expected:tExpect} = tests[i]; test(`testing "${url}"`, () => {
test(`test #${parseInt(i)+1}`, () => { expect(parseApi(url)).toStrictEqual(expected);
expect(parseApi(tUrl)).toStrictEqual(tExpect);
}); });
} }
}); });

View File

@ -68,7 +68,7 @@ export class KubeApi<T extends KubeObject = any> {
this.objectConstructor = objectConstructor; this.objectConstructor = objectConstructor;
this.parseResponse = this.parseResponse.bind(this); this.parseResponse = this.parseResponse.bind(this);
apiManager.registerApi(apiBase, this); apiManager.registerApi(this);
} }
setResourceVersion(namespace = "", newVersion: string) { setResourceVersion(namespace = "", newVersion: string) {

View File

@ -5,7 +5,7 @@ import { Trans } from "@lingui/macro";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { crdBetaApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { cssNames } from "../../utils"; import { cssNames } from "../../utils";
import { AceEditor } from "../ace-editor"; import { AceEditor } from "../ace-editor";
import { Badge } from "../badge"; import { Badge } from "../badge";
@ -133,6 +133,6 @@ export class CRDDetails extends React.Component<Props> {
} }
} }
apiManager.registerViews(crdApi, { apiManager.registerViews(crdBetaApi, {
Details: CRDDetails Details: CRDDetails
}) })

View File

@ -9,7 +9,7 @@ import { stopPropagation } from "../../utils";
import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectListLayout } from "../kube-object";
import { crdStore } from "./crd.store"; import { crdStore } from "./crd.store";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { crdBetaApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu"; import { KubeObjectMenu, KubeObjectMenuProps } from "../kube-object/kube-object-menu";
import { Select, SelectOption } from "../select"; import { Select, SelectOption } from "../select";
import { navigation, setQueryParams } from "../../navigation"; import { navigation, setQueryParams } from "../../navigation";
@ -117,6 +117,6 @@ export function CRDMenu(props: KubeObjectMenuProps<CustomResourceDefinition>) {
) )
} }
apiManager.registerViews(crdApi, { apiManager.registerViews(crdBetaApi, {
Menu: CRDMenu, Menu: CRDMenu,
}); });

View File

@ -1,23 +1,29 @@
import { computed, reaction } from "mobx"; import { computed, reaction } from "mobx";
import { KubeObjectStore } from "../../kube-object.store"; import { KubeObjectStore } from "../../kube-object.store";
import { autobind } from "../../utils"; import { autobind } from "../../utils";
import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { crdBetaApi, CustomResourceDefinition } from "../../api/endpoints/crd.api";
import { apiManager } from "../../api/api-manager"; import { apiManager } from "../../api/api-manager";
import { KubeApi } from "../../api/kube-api"; import { KubeApi } from "../../api/kube-api";
import { CRDResourceStore } from "./crd-resource.store"; import { CRDResourceStore } from "./crd-resource.store";
import { KubeObject } from "../../api/kube-object"; import { KubeObject } from "../../api/kube-object";
function initStore(crd: CustomResourceDefinition) {
const apiBase = crd.getResourceApiBase();
const [kind, isNamespaced] = [crd.getResourceKind(), crd.isNamespaced()];
const api = apiManager.getApi(apiBase) || new KubeApi({ apiBase, kind, isNamespaced });
const store = apiManager.getStore(api) || new CRDResourceStore(api);
apiManager.registerStore(api, store);
}
@autobind() @autobind()
export class CRDStore extends KubeObjectStore<CustomResourceDefinition> { export class CRDStore extends KubeObjectStore<CustomResourceDefinition> {
api = crdApi api = crdBetaApi
constructor() { constructor() {
super(); super();
// auto-init stores for crd-s // auto-init stores for crd-s
reaction(() => this.items.toJS(), items => { reaction(() => this.items.toJS(), items => items.forEach(initStore))
items.forEach(this.initStore);
})
} }
protected sortItems(items: CustomResourceDefinition[]) { protected sortItems(items: CustomResourceDefinition[]) {
@ -27,23 +33,6 @@ export class CRDStore extends KubeObjectStore<CustomResourceDefinition> {
]) ])
} }
protected initStore(crd: CustomResourceDefinition) {
const apiBase = crd.getResourceApiBase();
let api = apiManager.getApi(apiBase);
if (!api) {
api = new KubeApi({
apiBase: apiBase,
kind: crd.getResourceKind(),
isNamespaced: crd.isNamespaced(),
});
}
let store = apiManager.getStore(api);
if (!store) {
store = new CRDResourceStore(api);
apiManager.registerStore(api, store);
}
}
@computed get groups() { @computed get groups() {
const groups: Record<string, CustomResourceDefinition[]> = {}; const groups: Record<string, CustomResourceDefinition[]> = {};
return this.items.reduce((groups, crd) => { return this.items.reduce((groups, crd) => {
@ -63,12 +52,13 @@ export class CRDStore extends KubeObjectStore<CustomResourceDefinition> {
getByObject(obj: KubeObject) { getByObject(obj: KubeObject) {
if (!obj) return null if (!obj) return null
const { kind, apiVersion } = obj; const { kind, apiVersion } = obj;
return this.items.find(crd => {
return kind === crd.getResourceKind() && apiVersion === `${crd.getGroup()}/${crd.getVersion()}` return this.items.find(crd => (
}) kind === crd.getResourceKind() && apiVersion === `${crd.getGroup()}/${crd.getVersion()}`
))
} }
} }
export const crdStore = new CRDStore(); export const crdStore = new CRDStore();
apiManager.registerStore(crdApi, crdStore); apiManager.registerStore(crdBetaApi, crdStore);

View File

@ -36,66 +36,66 @@ enum sortBy {
interface Props extends RouteComponentProps<INodesRouteParams> { interface Props extends RouteComponentProps<INodesRouteParams> {
} }
@observer function renderCpuUsage(node: Node) {
export class Nodes extends React.Component<Props> {
private metricsWatcher = interval(30, () => nodesStore.loadUsageMetrics());
componentDidMount() {
this.metricsWatcher.start(true);
}
componentWillUnmount() {
this.metricsWatcher.stop();
}
renderCpuUsage(node: Node) {
const metrics = nodesStore.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]); const metrics = nodesStore.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]);
if (!metrics || !metrics[1]) return <LineProgress value={0}/>; if (!metrics?.[1]) {
const usage = metrics[0]; return <LineProgress value={0} />;
const cores = metrics[1]; }
return (
<LineProgress const [usage, cores] = metrics;
const percentage = Math.ceil(usage * 100) / cores;
const CPUTrans = _i18n._(t`CPU`);
const CoreTrans = _i18n._(t`cores`);
return <LineProgress
max={cores} max={cores}
value={usage} value={usage}
tooltip={_i18n._(t`CPU:`) + ` ${Math.ceil(usage * 100) / cores}\%, ` + _i18n._(t`cores:`) + ` ${cores}`} tooltip={`${CPUTrans}: ${percentage.toPrecision(4)}%, ${CoreTrans}: ${cores}`}
/> />;
)
} }
renderMemoryUsage(node: Node) { function renderMemoryUsage(node: Node) {
const metrics = nodesStore.getLastMetricValues(node, ["memoryUsage", "memoryCapacity"]); const metrics = nodesStore.getLastMetricValues(node, ["memoryUsage", "memoryCapacity"]);
if (!metrics || !metrics[1]) return <LineProgress value={0}/>; if (!metrics?.[1]) {
const usage = metrics[0]; return <LineProgress value={0} />;
const capacity = metrics[1];
return (
<LineProgress
max={capacity}
value={usage}
tooltip={_i18n._(t`Memory:`) + ` ${Math.ceil(usage * 100 / capacity)}%, ${bytesToUnits(usage, 3)}`}
/>
)
} }
renderDiskUsage(node: Node): any { const [usage, capacity] = metrics;
const percentage = Math.ceil(usage * 100 / capacity);
const MemoryTrans = _i18n._(t`Memory`);
return <LineProgress
max={capacity}
value={usage}
tooltip={`${MemoryTrans}: ${percentage.toPrecision(4)}%, ${bytesToUnits(usage, 3)}`}
/>;
}
function renderDiskUsage(node: Node): any {
const metrics = nodesStore.getLastMetricValues(node, ["fsUsage", "fsSize"]); const metrics = nodesStore.getLastMetricValues(node, ["fsUsage", "fsSize"]);
if (!metrics || !metrics[1]) return <LineProgress value={0}/>; if (!metrics?.[1]) {
const usage = metrics[0]; return <LineProgress value={0} />;
const capacity = metrics[1];
return (
<LineProgress
max={capacity}
value={usage}
tooltip={_i18n._(t`Disk:`) + ` ${Math.ceil(usage * 100 / capacity)}%, ${bytesToUnits(usage, 3)}`}
/>
)
} }
renderConditions(node: Node) { const [usage, capacity] = metrics;
const percentage = Math.ceil(usage * 100 / capacity);
const DiskTrans = _i18n._(t`Disk`);
return <LineProgress
max={capacity}
value={usage}
tooltip={`${DiskTrans}: ${percentage.toPrecision(4)}%, ${bytesToUnits(usage, 3)}`}
/>;
}
function renderConditions(node: Node) {
if (!node.status.conditions) { if (!node.status.conditions) {
return null return null
} }
const conditions = node.getActiveConditions();
return conditions.map(condition => { return node
.getActiveConditions()
.map(condition => {
const { type } = condition const { type } = condition
const tooltipId = `node-${node.getName()}-condition-${type}` const tooltipId = `node-${node.getName()}-condition-${type}`
return ( return (
@ -115,12 +115,25 @@ export class Nodes extends React.Component<Props> {
}) })
} }
@observer
export class Nodes extends React.Component<Props> {
private metricsWatcher = interval(30, () => nodesStore.loadUsageMetrics());
componentDidMount() {
this.metricsWatcher.start(true);
}
componentWillUnmount() {
this.metricsWatcher.stop();
}
render() { render() {
return ( return (
<MainLayout> <MainLayout>
<KubeObjectListLayout <KubeObjectListLayout
className="Nodes" className="Nodes"
store={nodesStore} isClusterScoped store={nodesStore}
isClusterScoped
isReady={nodesStore.isLoaded && nodesStore.metricsLoaded} isReady={nodesStore.isLoaded && nodesStore.metricsLoaded}
dependentStores={[podsStore]} dependentStores={[podsStore]}
isSelectable={false} isSelectable={false}
@ -157,9 +170,9 @@ export class Nodes extends React.Component<Props> {
const tooltipId = `node-taints-${node.getId()}`; const tooltipId = `node-taints-${node.getId()}`;
return [ return [
node.getName(), node.getName(),
this.renderCpuUsage(node), renderCpuUsage(node),
this.renderMemoryUsage(node), renderMemoryUsage(node),
this.renderDiskUsage(node), renderDiskUsage(node),
<> <>
<span id={tooltipId}>{node.getTaints().length}</span> <span id={tooltipId}>{node.getTaints().length}</span>
<Tooltip htmlFor={tooltipId} style={{ whiteSpace: "pre-line" }}> <Tooltip htmlFor={tooltipId} style={{ whiteSpace: "pre-line" }}>
@ -169,12 +182,10 @@ export class Nodes extends React.Component<Props> {
node.getRoleLabels(), node.getRoleLabels(),
node.status.nodeInfo.kubeletVersion, node.status.nodeInfo.kubeletVersion,
node.getAge(), node.getAge(),
this.renderConditions(node), renderConditions(node),
] ]
}} }}
renderItemMenu={(item: Node) => { renderItemMenu={(item: Node) => <NodeMenu object={item} />}
return <NodeMenu object={item}/>
}}
/> />
</MainLayout> </MainLayout>
) )