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: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}</0> more"
msgstr "and <0>{tailCount}</0> 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"

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: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}</0> more"
msgstr ""
#: src/renderer/components/+nodes/nodes.tsx:55
msgid "cores:"
msgid "cores"
msgstr ""
#: 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: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}</0> more"
msgstr "и <0>{tailCount}</0> ещё"
#: 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"

View File

@ -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)) || {}
}

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,
apiBase: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions",
isNamespaced: false,
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,
},
},
{
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);
});
}
});

View File

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

View File

@ -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<Props> {
}
}
apiManager.registerViews(crdApi, {
apiManager.registerViews(crdBetaApi, {
Details: CRDDetails
})

View File

@ -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<CustomResourceDefinition>) {
)
}
apiManager.registerViews(crdApi, {
apiManager.registerViews(crdBetaApi, {
Menu: CRDMenu,
});

View File

@ -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<CustomResourceDefinition> {
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<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() {
const groups: Record<string, CustomResourceDefinition[]> = {};
return this.items.reduce((groups, crd) => {
@ -63,12 +52,13 @@ export class CRDStore extends KubeObjectStore<CustomResourceDefinition> {
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);

View File

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