From 2af58cde7250d34073a5d4a5a08cb81586c21f91 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 7 Aug 2020 10:52:11 -0400 Subject: [PATCH] Add support for customreasourcedefinitions under the v1 API Signed-off-by: Sebastian Malton --- locales/en/messages.po | 22 ++- locales/fi/messages.po | 18 +-- locales/ru/messages.po | 22 ++- src/renderer/api/api-manager.ts | 53 +++++-- src/renderer/api/endpoints/crd.api.ts | 9 +- src/renderer/api/kube-api-parse_test.ts | 20 ++- src/renderer/api/kube-api.ts | 2 +- .../+custom-resources/crd-details.tsx | 4 +- .../components/+custom-resources/crd-list.tsx | 4 +- .../components/+custom-resources/crd.store.ts | 42 +++--- src/renderer/components/+nodes/nodes.tsx | 131 ++++++++++-------- 11 files changed, 177 insertions(+), 150 deletions(-) diff --git a/locales/en/messages.po b/locales/en/messages.po index f2ca7973e8..039da01cb7 100644 --- a/locales/en/messages.po +++ b/locales/en/messages.po @@ -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:95 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13 +#: src/renderer/components/+nodes/nodes.tsx:55 msgid "CPU" msgstr "CPU" @@ -296,10 +297,7 @@ msgstr "CPU limits" msgid "CPU requests" msgstr "CPU requests" -#: src/renderer/components/+nodes/nodes.tsx:55 -msgid "CPU:" -msgstr "CPU:" - +#: src/renderer/components/+workspaces/workspaces.tsx:119 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/dock/info-panel.tsx:97 #: 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/nodes.tsx:115 #: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44 +#: src/renderer/components/+nodes/nodes.tsx:71 msgid "Disk" msgstr "Disk" -#: src/renderer/components/+nodes/nodes.tsx:71 -msgid "Disk:" -msgstr "Disk:" +#: src/renderer/components/+preferences/preferences.tsx:171 +msgid "Does not affect cluster communications!" +msgstr "Does not affect cluster communications!" #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89 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:96 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18 +#: src/renderer/components/+nodes/nodes.tsx:63 msgid "Memory" msgstr "Memory" @@ -1167,10 +1167,6 @@ msgstr "Memory requests" msgid "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/+events/event-details.tsx:30 #: src/renderer/components/+events/events.tsx:62 @@ -2393,8 +2389,8 @@ msgid "and <0>{tailCount} more" msgstr "and <0>{tailCount} more" #: src/renderer/components/+nodes/nodes.tsx:55 -msgid "cores:" -msgstr "cores:" +msgid "cores" +msgstr "cores" #: src/renderer/components/+workloads-pods/pod-details-container.tsx:41 msgid "exit code" diff --git a/locales/fi/messages.po b/locales/fi/messages.po index 4d81ae2a65..11eb031bac 100644 --- a/locales/fi/messages.po +++ b/locales/fi/messages.po @@ -274,6 +274,7 @@ msgstr "" #: src/renderer/components/+workloads-pods/pod-details-list.tsx:53 #: src/renderer/components/+workloads-pods/pod-details-list.tsx:95 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13 +#: src/renderer/components/+nodes/nodes.tsx:55 msgid "CPU" msgstr "" @@ -296,10 +297,7 @@ msgstr "" msgid "CPU requests" msgstr "" -#: src/renderer/components/+nodes/nodes.tsx:55 -msgid "CPU:" -msgstr "" - +#: src/renderer/components/+workspaces/workspaces.tsx:119 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/dock/info-panel.tsx:97 #: src/renderer/components/wizard/wizard.tsx:130 @@ -661,11 +659,8 @@ msgstr "" #: src/renderer/components/+nodes/node-details.tsx:65 #: src/renderer/components/+nodes/nodes.tsx:115 #: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44 -msgid "Disk" -msgstr "" - #: src/renderer/components/+nodes/nodes.tsx:71 -msgid "Disk:" +msgid "Disk" msgstr "" #: 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:96 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18 +#: src/renderer/components/+nodes/nodes.tsx:63 msgid "Memory" msgstr "" @@ -1158,10 +1154,6 @@ msgstr "" msgid "Memory usage" msgstr "" -#: src/renderer/components/+nodes/nodes.tsx:63 -msgid "Memory:" -msgstr "" - #: src/renderer/components/+cluster/cluster-issues.tsx:100 #: src/renderer/components/+events/event-details.tsx:30 #: src/renderer/components/+events/events.tsx:62 @@ -2376,7 +2368,7 @@ msgid "and <0>{tailCount} more" msgstr "" #: src/renderer/components/+nodes/nodes.tsx:55 -msgid "cores:" +msgid "cores" msgstr "" #: src/renderer/components/+workloads-pods/pod-details-container.tsx:41 diff --git a/locales/ru/messages.po b/locales/ru/messages.po index 6e55ab9f73..6c53eb3c03 100644 --- a/locales/ru/messages.po +++ b/locales/ru/messages.po @@ -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:95 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:13 +#: src/renderer/components/+nodes/nodes.tsx:55 msgid "CPU" msgstr "Процессор" @@ -297,10 +298,7 @@ msgstr "Лимиты процессора" msgid "CPU requests" msgstr "Запросы к процессору" -#: src/renderer/components/+nodes/nodes.tsx:55 -msgid "CPU:" -msgstr "CPU:" - +#: src/renderer/components/+workspaces/workspaces.tsx:119 #: src/renderer/components/confirm-dialog/confirm-dialog.tsx:44 #: src/renderer/components/dock/info-panel.tsx:97 #: src/renderer/components/wizard/wizard.tsx:130 @@ -666,12 +664,13 @@ msgstr "Нужный уровень реплик" #: src/renderer/components/+nodes/node-details.tsx:65 #: src/renderer/components/+nodes/nodes.tsx:115 #: src/renderer/components/+storage-volume-claims/volume-claim-details.tsx:44 +#: src/renderer/components/+nodes/nodes.tsx:71 msgid "Disk" msgstr "Диск" -#: src/renderer/components/+nodes/nodes.tsx:71 -msgid "Disk:" -msgstr "Диск:" +#: src/renderer/components/+preferences/preferences.tsx:171 +msgid "Does not affect cluster communications!" +msgstr "" #: src/renderer/components/+custom-resources/certmanager.k8s.io/certificate-details.tsx:89 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:96 #: src/renderer/components/resource-metrics/resource-metrics-text.tsx:18 +#: src/renderer/components/+nodes/nodes.tsx:63 msgid "Memory" msgstr "Память" @@ -1168,10 +1168,6 @@ msgstr "Запросы к памяти" msgid "Memory usage" msgstr "Использование памяти" -#: src/renderer/components/+nodes/nodes.tsx:63 -msgid "Memory:" -msgstr "Память:" - #: src/renderer/components/+cluster/cluster-issues.tsx:100 #: src/renderer/components/+events/event-details.tsx:30 #: src/renderer/components/+events/events.tsx:62 @@ -2394,8 +2390,8 @@ msgid "and <0>{tailCount} more" msgstr "и <0>{tailCount} ещё" #: src/renderer/components/+nodes/nodes.tsx:55 -msgid "cores:" -msgstr "ядер:" +msgid "cores" +msgstr "ядер" #: src/renderer/components/+workloads-pods/pod-details-container.tsx:41 msgid "exit code" diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index 22735c3c09..e585af3d41 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -20,16 +20,31 @@ export class ApiManager { getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { 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); } - registerApi(apiBase: string, api: KubeApi) { - if (!this.apis.has(apiBase)) { - this.apis.set(apiBase, api); + /** + * registerApi registers the provided api under its `apiBase` URL. + * @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 { @@ -37,13 +52,17 @@ export class ApiManager { return api; } - unregisterApi(api: string | KubeApi) { - if (typeof api === "string") this.apis.delete(api); - else { - const apis = Array.from(this.apis.entries()); - const entry = apis.find(entry => entry[1] === api); - if (entry) this.unregisterApi(entry[0]); + /** + * unregisterApi removes the + * @param api the apiBase or KubeApi object to remove from the map + * @returns true if the item was removed, false if not present + */ + 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) { @@ -54,11 +73,7 @@ export class ApiManager { return this.stores.get(this.resolveApi(api)); } - registerViews(api: KubeApi | KubeApi[], views: ApiComponents) { - if (Array.isArray(api)) { - api.forEach(api => this.registerViews(api, views)); - return; - } + private registerViewsForApi(api: KubeApi, views: ApiComponents) { const currentViews = this.views.get(api) || {}; this.views.set(api, { ...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 { return this.views.get(this.resolveApi(api)) || {} } diff --git a/src/renderer/api/endpoints/crd.api.ts b/src/renderer/api/endpoints/crd.api.ts index 39a4e50f8c..829719a3cb 100644 --- a/src/renderer/api/endpoints/crd.api.ts +++ b/src/renderer/api/endpoints/crd.api.ts @@ -130,9 +130,16 @@ export class CustomResourceDefinition extends KubeObject { } } -export const crdApi = new KubeApi({ +export const crdBetaApi = new KubeApi({ kind: CustomResourceDefinition.kind, apiBase: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions", isNamespaced: false, objectConstructor: CustomResourceDefinition, }); + +export const crdApi = new KubeApi({ + kind: CustomResourceDefinition.kind, + apiBase: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions", + isNamespaced: false, + objectConstructor: CustomResourceDefinition, +}); diff --git a/src/renderer/api/kube-api-parse_test.ts b/src/renderer/api/kube-api-parse_test.ts index 1fda8c3e53..c103ad177c 100644 --- a/src/renderer/api/kube-api-parse_test.ts +++ b/src/renderer/api/kube-api-parse_test.ts @@ -123,13 +123,25 @@ const tests: KubeApi_Parse_Test[] = [ 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", () => { - for (const i in tests) { - const { url: tUrl, expected:tExpect} = tests[i]; - test(`test #${parseInt(i)+1}`, () => { - expect(parseApi(tUrl)).toStrictEqual(tExpect); + for (const { url, expected } of tests) { + test(`testing "${url}"`, () => { + expect(parseApi(url)).toStrictEqual(expected); }); } }); diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index 869e3a90ea..ae97092667 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -68,7 +68,7 @@ export class KubeApi { this.objectConstructor = objectConstructor; this.parseResponse = this.parseResponse.bind(this); - apiManager.registerApi(apiBase, this); + apiManager.registerApi(this); } setResourceVersion(namespace = "", newVersion: string) { diff --git a/src/renderer/components/+custom-resources/crd-details.tsx b/src/renderer/components/+custom-resources/crd-details.tsx index fdf8dd3968..6fc73824d8 100644 --- a/src/renderer/components/+custom-resources/crd-details.tsx +++ b/src/renderer/components/+custom-resources/crd-details.tsx @@ -5,7 +5,7 @@ import { Trans } from "@lingui/macro"; import { Link } from "react-router-dom"; import { observer } from "mobx-react"; 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 { AceEditor } from "../ace-editor"; import { Badge } from "../badge"; @@ -133,6 +133,6 @@ export class CRDDetails extends React.Component { } } -apiManager.registerViews(crdApi, { +apiManager.registerViews(crdBetaApi, { Details: CRDDetails }) \ No newline at end of file diff --git a/src/renderer/components/+custom-resources/crd-list.tsx b/src/renderer/components/+custom-resources/crd-list.tsx index 5707f27c9d..0e132d0214 100644 --- a/src/renderer/components/+custom-resources/crd-list.tsx +++ b/src/renderer/components/+custom-resources/crd-list.tsx @@ -9,7 +9,7 @@ import { stopPropagation } from "../../utils"; import { KubeObjectListLayout } from "../kube-object"; import { crdStore } from "./crd.store"; 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 { Select, SelectOption } from "../select"; import { navigation, setQueryParams } from "../../navigation"; @@ -117,6 +117,6 @@ export function CRDMenu(props: KubeObjectMenuProps) { ) } -apiManager.registerViews(crdApi, { +apiManager.registerViews(crdBetaApi, { Menu: CRDMenu, }); diff --git a/src/renderer/components/+custom-resources/crd.store.ts b/src/renderer/components/+custom-resources/crd.store.ts index fbcd2a4ee6..ec4e251771 100644 --- a/src/renderer/components/+custom-resources/crd.store.ts +++ b/src/renderer/components/+custom-resources/crd.store.ts @@ -1,23 +1,29 @@ import { computed, reaction } from "mobx"; import { KubeObjectStore } from "../../kube-object.store"; 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 { KubeApi } from "../../api/kube-api"; import { CRDResourceStore } from "./crd-resource.store"; 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() export class CRDStore extends KubeObjectStore { - api = crdApi + api = crdBetaApi constructor() { super(); // auto-init stores for crd-s - reaction(() => this.items.toJS(), items => { - items.forEach(this.initStore); - }) + reaction(() => this.items.toJS(), items => items.forEach(initStore)) } protected sortItems(items: CustomResourceDefinition[]) { @@ -27,23 +33,6 @@ export class CRDStore extends KubeObjectStore { ]) } - 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() { const groups: Record = {}; return this.items.reduce((groups, crd) => { @@ -63,12 +52,13 @@ export class CRDStore extends KubeObjectStore { getByObject(obj: KubeObject) { if (!obj) return null 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(); -apiManager.registerStore(crdApi, crdStore); +apiManager.registerStore(crdBetaApi, crdStore); diff --git a/src/renderer/components/+nodes/nodes.tsx b/src/renderer/components/+nodes/nodes.tsx index 2b9658fbf3..f10273cb94 100644 --- a/src/renderer/components/+nodes/nodes.tsx +++ b/src/renderer/components/+nodes/nodes.tsx @@ -36,66 +36,66 @@ enum sortBy { interface Props extends RouteComponentProps { } -@observer -export class Nodes extends React.Component { - private metricsWatcher = interval(30, () => nodesStore.loadUsageMetrics()); - - componentDidMount() { - this.metricsWatcher.start(true); +function renderCpuUsage(node: Node) { + const metrics = nodesStore.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]); + if (!metrics?.[1]) { + return ; } - componentWillUnmount() { - this.metricsWatcher.stop(); + const [usage, cores] = metrics; + const percentage = Math.ceil(usage * 100) / cores; + const CPUTrans = _i18n._(t`CPU`); + const CoreTrans = _i18n._(t`cores`); + + return ; +} + +function renderMemoryUsage(node: Node) { + const metrics = nodesStore.getLastMetricValues(node, ["memoryUsage", "memoryCapacity"]); + if (!metrics?.[1]) { + return ; } - renderCpuUsage(node: Node) { - const metrics = nodesStore.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]); - if (!metrics || !metrics[1]) return ; - const usage = metrics[0]; - const cores = metrics[1]; - return ( - - ) + const [usage, capacity] = metrics; + const percentage = Math.ceil(usage * 100 / capacity); + const MemoryTrans = _i18n._(t`Memory`); + + return ; +} + +function renderDiskUsage(node: Node): any { + const metrics = nodesStore.getLastMetricValues(node, ["fsUsage", "fsSize"]); + if (!metrics?.[1]) { + return ; } - renderMemoryUsage(node: Node) { - const metrics = nodesStore.getLastMetricValues(node, ["memoryUsage", "memoryCapacity"]); - if (!metrics || !metrics[1]) return ; - const usage = metrics[0]; - const capacity = metrics[1]; - return ( - - ) + const [usage, capacity] = metrics; + const percentage = Math.ceil(usage * 100 / capacity); + const DiskTrans = _i18n._(t`Disk`); + + return ; +} + +function renderConditions(node: Node) { + if (!node.status.conditions) { + return null } - renderDiskUsage(node: Node): any { - const metrics = nodesStore.getLastMetricValues(node, ["fsUsage", "fsSize"]); - if (!metrics || !metrics[1]) return ; - const usage = metrics[0]; - const capacity = metrics[1]; - return ( - - ) - } - - renderConditions(node: Node) { - if (!node.status.conditions) { - return null - } - const conditions = node.getActiveConditions(); - return conditions.map(condition => { + return node + .getActiveConditions() + .map(condition => { const { type } = condition const tooltipId = `node-${node.getName()}-condition-${type}` return ( @@ -113,6 +113,18 @@ export class Nodes extends React.Component { ) }) +} + +@observer +export class Nodes extends React.Component { + private metricsWatcher = interval(30, () => nodesStore.loadUsageMetrics()); + + componentDidMount() { + this.metricsWatcher.start(true); + } + + componentWillUnmount() { + this.metricsWatcher.stop(); } render() { @@ -120,7 +132,8 @@ export class Nodes extends React.Component { { const tooltipId = `node-taints-${node.getId()}`; return [ node.getName(), - this.renderCpuUsage(node), - this.renderMemoryUsage(node), - this.renderDiskUsage(node), + renderCpuUsage(node), + renderMemoryUsage(node), + renderDiskUsage(node), <> {node.getTaints().length} @@ -169,12 +182,10 @@ export class Nodes extends React.Component { node.getRoleLabels(), node.status.nodeInfo.kubeletVersion, node.getAge(), - this.renderConditions(node), + renderConditions(node), ] }} - renderItemMenu={(item: Node) => { - return - }} + renderItemMenu={(item: Node) => } /> )