mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add support for LimitRange (#1796)
Signed-off-by: vshakirova <vshakirova@mirantis.com>
This commit is contained in:
parent
12c538b0eb
commit
c48816ca5c
@ -313,6 +313,12 @@ describe("Lens integration tests", () => {
|
||||
expectedSelector: "h5.title",
|
||||
expectedText: "Resource Quotas"
|
||||
},
|
||||
{
|
||||
name: "Limit Ranges",
|
||||
href: "limitranges",
|
||||
expectedSelector: "h5.title",
|
||||
expectedText: "Limit Ranges"
|
||||
},
|
||||
{
|
||||
name: "HPA",
|
||||
href: "hpa",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { getHostedCluster } from "./cluster-store";
|
||||
|
||||
export type KubeResource =
|
||||
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" |
|
||||
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" |
|
||||
"secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" |
|
||||
"pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "jobs" | "cronjobs" |
|
||||
"endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets";
|
||||
@ -23,6 +23,7 @@ export const apiResources: KubeApiResource[] = [
|
||||
{ resource: "horizontalpodautoscalers" },
|
||||
{ resource: "ingresses", group: "networking.k8s.io" },
|
||||
{ resource: "jobs", group: "batch" },
|
||||
{ resource: "limitranges" },
|
||||
{ resource: "namespaces" },
|
||||
{ resource: "networkpolicies", group: "networking.k8s.io" },
|
||||
{ resource: "nodes" },
|
||||
|
||||
@ -14,6 +14,7 @@ export { ConfigMap, configMapApi } from "../../renderer/api/endpoints";
|
||||
export { Secret, secretsApi, ISecretRef } from "../../renderer/api/endpoints";
|
||||
export { ReplicaSet, replicaSetApi } from "../../renderer/api/endpoints";
|
||||
export { ResourceQuota, resourceQuotaApi } from "../../renderer/api/endpoints";
|
||||
export { LimitRange, limitRangeApi } from "../../renderer/api/endpoints";
|
||||
export { HorizontalPodAutoscaler, hpaApi } from "../../renderer/api/endpoints";
|
||||
export { PodDisruptionBudget, pdbApi } from "../../renderer/api/endpoints";
|
||||
export { Service, serviceApi } from "../../renderer/api/endpoints";
|
||||
@ -46,6 +47,7 @@ export type { ConfigMapsStore } from "../../renderer/components/+config-maps/con
|
||||
export type { SecretsStore } from "../../renderer/components/+config-secrets/secrets.store";
|
||||
export type { ReplicaSetStore } from "../../renderer/components/+workloads-replicasets/replicasets.store";
|
||||
export type { ResourceQuotasStore } from "../../renderer/components/+config-resource-quotas/resource-quotas.store";
|
||||
export type { LimitRangesStore } from "../../renderer/components/+config-limit-ranges/limit-ranges.store";
|
||||
export type { HPAStore } from "../../renderer/components/+config-autoscalers/hpa.store";
|
||||
export type { PodDisruptionBudgetsStore } from "../../renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.store";
|
||||
export type { ServiceStore } from "../../renderer/components/+network-services/services.store";
|
||||
|
||||
@ -14,6 +14,7 @@ export * from "./events.api";
|
||||
export * from "./hpa.api";
|
||||
export * from "./ingress.api";
|
||||
export * from "./job.api";
|
||||
export * from "./limit-range.api";
|
||||
export * from "./namespaces.api";
|
||||
export * from "./network-policy.api";
|
||||
export * from "./nodes.api";
|
||||
|
||||
57
src/renderer/api/endpoints/limit-range.api.ts
Normal file
57
src/renderer/api/endpoints/limit-range.api.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { KubeObject } from "../kube-object";
|
||||
import { KubeApi } from "../kube-api";
|
||||
import { autobind } from "../../utils";
|
||||
|
||||
export enum LimitType {
|
||||
CONTAINER = "Container",
|
||||
POD = "Pod",
|
||||
PVC = "PersistentVolumeClaim",
|
||||
}
|
||||
|
||||
export enum Resource {
|
||||
MEMORY = "memory",
|
||||
CPU = "cpu",
|
||||
STORAGE = "storage",
|
||||
EPHEMERAL_STORAGE = "ephemeral-storage",
|
||||
}
|
||||
|
||||
export enum LimitPart {
|
||||
MAX = "max",
|
||||
MIN = "min",
|
||||
DEFAULT = "default",
|
||||
DEFAULT_REQUEST = "defaultRequest",
|
||||
MAX_LIMIT_REQUEST_RATIO = "maxLimitRequestRatio",
|
||||
}
|
||||
|
||||
type LimitRangeParts = Partial<Record<LimitPart, Record<string, string>>>;
|
||||
|
||||
export interface LimitRangeItem extends LimitRangeParts {
|
||||
type: string
|
||||
}
|
||||
|
||||
@autobind()
|
||||
export class LimitRange extends KubeObject {
|
||||
static kind = "LimitRange";
|
||||
static namespaced = true;
|
||||
static apiBase = "/api/v1/limitranges";
|
||||
|
||||
spec: {
|
||||
limits: LimitRangeItem[];
|
||||
};
|
||||
|
||||
getContainerLimits() {
|
||||
return this.spec.limits.filter(limit => limit.type === LimitType.CONTAINER);
|
||||
}
|
||||
|
||||
getPodLimits() {
|
||||
return this.spec.limits.filter(limit => limit.type === LimitType.POD);
|
||||
}
|
||||
|
||||
getPVCLimits() {
|
||||
return this.spec.limits.filter(limit => limit.type === LimitType.PVC);
|
||||
}
|
||||
}
|
||||
|
||||
export const limitRangeApi = new KubeApi({
|
||||
objectConstructor: LimitRange,
|
||||
});
|
||||
3
src/renderer/components/+config-limit-ranges/index.ts
Normal file
3
src/renderer/components/+config-limit-ranges/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./limit-ranges";
|
||||
export * from "./limit-ranges.route";
|
||||
export * from "./limit-range-details";
|
||||
@ -0,0 +1,12 @@
|
||||
.LimitRangeDetails {
|
||||
|
||||
.DrawerItem {
|
||||
> .name {
|
||||
font-weight: $font-weight-normal;
|
||||
padding-left: 4px;
|
||||
}
|
||||
.DrawerItem {
|
||||
padding-top: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
import "./limit-range-details.scss";
|
||||
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { KubeObjectDetailsProps } from "../kube-object";
|
||||
import { LimitPart, LimitRange, LimitRangeItem, Resource } from "../../api/endpoints/limit-range.api";
|
||||
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { DrawerItem } from "../drawer/drawer-item";
|
||||
import { Badge } from "../badge";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<LimitRange> {
|
||||
}
|
||||
|
||||
function renderLimit(limit: LimitRangeItem, part: LimitPart, resource: Resource) {
|
||||
|
||||
const resourceLimit = limit[part]?.[resource];
|
||||
|
||||
if (!resourceLimit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Badge label={`${part}:${resourceLimit}`}/>;
|
||||
}
|
||||
|
||||
function renderResourceLimits(limit: LimitRangeItem, resource: Resource) {
|
||||
return (
|
||||
<React.Fragment key={limit.type + resource}>
|
||||
{renderLimit(limit, LimitPart.MIN, resource)}
|
||||
{renderLimit(limit, LimitPart.MAX, resource)}
|
||||
{renderLimit(limit, LimitPart.DEFAULT, resource)}
|
||||
{renderLimit(limit, LimitPart.DEFAULT_REQUEST, resource)}
|
||||
{renderLimit(limit, LimitPart.MAX_LIMIT_REQUEST_RATIO, resource)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function renderLimitDetails(limits: LimitRangeItem[], resources: Resource[]) {
|
||||
|
||||
return resources.map(resource =>
|
||||
<DrawerItem key={resource} name={resource}>
|
||||
{
|
||||
limits.map(limit =>
|
||||
renderResourceLimits(limit, resource)
|
||||
)
|
||||
}
|
||||
</DrawerItem>
|
||||
);
|
||||
}
|
||||
|
||||
@observer
|
||||
export class LimitRangeDetails extends React.Component<Props> {
|
||||
render() {
|
||||
const { object: limitRange } = this.props;
|
||||
|
||||
if (!limitRange) return null;
|
||||
const containerLimits = limitRange.getContainerLimits();
|
||||
const podLimits = limitRange.getPodLimits();
|
||||
const pvcLimits = limitRange.getPVCLimits();
|
||||
|
||||
return (
|
||||
<div className="LimitRangeDetails">
|
||||
<KubeObjectMeta object={limitRange}/>
|
||||
|
||||
{containerLimits.length > 0 &&
|
||||
<DrawerItem name={`Container Limits`} labelsOnly>
|
||||
{
|
||||
renderLimitDetails(containerLimits, [Resource.CPU, Resource.MEMORY, Resource.EPHEMERAL_STORAGE])
|
||||
}
|
||||
</DrawerItem>
|
||||
}
|
||||
{podLimits.length > 0 &&
|
||||
<DrawerItem name={`Pod Limits`} labelsOnly>
|
||||
{
|
||||
renderLimitDetails(podLimits, [Resource.CPU, Resource.MEMORY, Resource.EPHEMERAL_STORAGE])
|
||||
}
|
||||
</DrawerItem>
|
||||
}
|
||||
{pvcLimits.length > 0 &&
|
||||
<DrawerItem name={`Persistent Volume Claim Limits`} labelsOnly>
|
||||
{
|
||||
renderLimitDetails(pvcLimits, [Resource.STORAGE])
|
||||
}
|
||||
</DrawerItem>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
kubeObjectDetailRegistry.add({
|
||||
kind: "LimitRange",
|
||||
apiVersions: ["v1"],
|
||||
components: {
|
||||
Details: (props) => <LimitRangeDetails {...props} />
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import type { RouteProps } from "react-router";
|
||||
import { buildURL } from "../../../common/utils/buildUrl";
|
||||
|
||||
export const limitRangesRoute: RouteProps = {
|
||||
path: "/limitranges"
|
||||
};
|
||||
|
||||
export interface LimitRangeRouteParams {
|
||||
}
|
||||
|
||||
export const limitRangeURL = buildURL<LimitRangeRouteParams>(limitRangesRoute.path);
|
||||
@ -0,0 +1,7 @@
|
||||
.LimitRanges {
|
||||
.TableCell {
|
||||
&.warning {
|
||||
@include table-cell-warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import { autobind } from "../../../common/utils/autobind";
|
||||
import { KubeObjectStore } from "../../kube-object.store";
|
||||
import { apiManager } from "../../api/api-manager";
|
||||
import { LimitRange, limitRangeApi } from "../../api/endpoints/limit-range.api";
|
||||
|
||||
@autobind()
|
||||
export class LimitRangesStore extends KubeObjectStore<LimitRange> {
|
||||
api = limitRangeApi;
|
||||
}
|
||||
|
||||
export const limitRangeStore = new LimitRangesStore();
|
||||
apiManager.registerStore(limitRangeStore);
|
||||
@ -0,0 +1,53 @@
|
||||
import "./limit-ranges.scss";
|
||||
|
||||
import { RouteComponentProps } from "react-router";
|
||||
import { observer } from "mobx-react";
|
||||
import { KubeObjectListLayout } from "../kube-object/kube-object-list-layout";
|
||||
import { limitRangeStore } from "./limit-ranges.store";
|
||||
import { LimitRangeRouteParams } from "./limit-ranges.route";
|
||||
import React from "react";
|
||||
import { KubeObjectStatusIcon } from "../kube-object-status-icon";
|
||||
import { LimitRange } from "../../api/endpoints/limit-range.api";
|
||||
|
||||
enum sortBy {
|
||||
name = "name",
|
||||
namespace = "namespace",
|
||||
age = "age",
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps<LimitRangeRouteParams> {
|
||||
}
|
||||
|
||||
@observer
|
||||
export class LimitRanges extends React.Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<KubeObjectListLayout
|
||||
className="LimitRanges"
|
||||
store={limitRangeStore}
|
||||
sortingCallbacks={{
|
||||
[sortBy.name]: (item: LimitRange) => item.getName(),
|
||||
[sortBy.namespace]: (item: LimitRange) => item.getNs(),
|
||||
[sortBy.age]: (item: LimitRange) => item.metadata.creationTimestamp,
|
||||
}}
|
||||
searchFilters={[
|
||||
(item: LimitRange) => item.getName(),
|
||||
(item: LimitRange) => item.getNs(),
|
||||
]}
|
||||
renderHeaderTitle={"Limit Ranges"}
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: sortBy.name },
|
||||
{ className: "warning" },
|
||||
{ title: "Namespace", className: "namespace", sortBy: sortBy.namespace },
|
||||
{ title: "Age", className: "age", sortBy: sortBy.age },
|
||||
]}
|
||||
renderTableContents={(limitRange: LimitRange) => [
|
||||
limitRange.getName(),
|
||||
<KubeObjectStatusIcon key="icon" object={limitRange}/>,
|
||||
limitRange.getNs(),
|
||||
limitRange.getAge(),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ import { resourceQuotaRoute, ResourceQuotas, resourceQuotaURL } from "../+config
|
||||
import { pdbRoute, pdbURL, PodDisruptionBudgets } from "../+config-pod-disruption-budgets";
|
||||
import { HorizontalPodAutoscalers, hpaRoute, hpaURL } from "../+config-autoscalers";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
import { LimitRanges, limitRangesRoute, limitRangeURL } from "../+config-limit-ranges";
|
||||
|
||||
@observer
|
||||
export class Config extends React.Component {
|
||||
@ -42,6 +43,15 @@ export class Config extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
if (isAllowedResource("limitranges")) {
|
||||
routes.push({
|
||||
title: "Limit Ranges",
|
||||
component: LimitRanges,
|
||||
url: limitRangeURL({ query }),
|
||||
routePath: limitRangesRoute.path.toString(),
|
||||
});
|
||||
}
|
||||
|
||||
if (isAllowedResource("horizontalpodautoscalers")) {
|
||||
routes.push({
|
||||
title: "HPA",
|
||||
|
||||
@ -12,6 +12,7 @@ import { Spinner } from "../spinner";
|
||||
import { resourceQuotaStore } from "../+config-resource-quotas/resource-quotas.store";
|
||||
import { KubeObjectMeta } from "../kube-object/kube-object-meta";
|
||||
import { kubeObjectDetailRegistry } from "../../api/kube-object-detail-registry";
|
||||
import { limitRangeStore } from "../+config-limit-ranges/limit-ranges.store";
|
||||
|
||||
interface Props extends KubeObjectDetailsProps<Namespace> {
|
||||
}
|
||||
@ -24,8 +25,15 @@ export class NamespaceDetails extends React.Component<Props> {
|
||||
return resourceQuotaStore.getAllByNs(namespace);
|
||||
}
|
||||
|
||||
@computed get limitranges() {
|
||||
const namespace = this.props.object.getName();
|
||||
|
||||
return limitRangeStore.getAllByNs(namespace);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
resourceQuotaStore.loadAll();
|
||||
limitRangeStore.loadAll();
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -52,6 +60,16 @@ export class NamespaceDetails extends React.Component<Props> {
|
||||
);
|
||||
})}
|
||||
</DrawerItem>
|
||||
<DrawerItem name={`Limit Ranges`}>
|
||||
{!this.limitranges && limitRangeStore.isLoading && <Spinner/>}
|
||||
{this.limitranges.map(limitrange => {
|
||||
return (
|
||||
<Link key={limitrange.getId()} to={getDetailsUrl(limitrange.selfLink)}>
|
||||
{limitrange.getName()}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</DrawerItem>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -25,4 +25,5 @@ export const ResourceNames: Record<KubeResource, string> = {
|
||||
"horizontalpodautoscalers": "Horizontal Pod Autoscalers",
|
||||
"podsecuritypolicies": "Pod Security Policies",
|
||||
"poddisruptionbudgets": "Pod Disruption Budgets",
|
||||
"limitranges": "Limit Ranges",
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user