From f4b7fea54a7f567d7bd50487612622879b2588a6 Mon Sep 17 00:00:00 2001 From: Piotr Roszatycki Date: Tue, 19 Jul 2022 07:41:07 +0200 Subject: [PATCH] feat: New view for PriorityClasses under Config menu (#5816) * feat: new view for PriorityClass under Config menu Signed-off-by: Piotr Roszatycki * Added priorityClassDetailItemInjectable Signed-off-by: Piotr Roszatycki * Removed uneccessary defensive code Signed-off-by: Piotr Roszatycki --- .../12-kube-state-metrics-clusterrole.yml | 7 ++ ...navigate-to-priority-classes.injectable.ts | 20 +++++ .../priority-classes-route.injectable.ts | 25 ++++++ src/common/k8s-api/endpoints/index.ts | 1 + .../priority-class.api.injectable.ts | 19 ++++ .../k8s-api/endpoints/priority-class.api.ts | 66 ++++++++++++++ .../endpoints/types/preemption-policy.ts | 6 ++ src/common/rbac.ts | 3 +- src/extensions/common-api/k8s-api.ts | 1 + src/extensions/renderer-api/k8s-api.ts | 3 + .../+config-priority-classes/index.ts | 7 ++ .../priority-classes-details.scss | 6 ++ .../priority-classes-details.tsx | 43 +++++++++ .../priority-classes-items.injectable.tsx | 38 ++++++++ ...rity-classes-route-component.injectable.ts | 21 +++++ .../priority-classes.scss | 24 +++++ .../priority-classes.tsx | 87 +++++++++++++++++++ .../store.injectable.ts | 24 +++++ .../+config-priority-classes/store.ts | 10 +++ .../internal-commands.injectable.tsx | 11 ++- .../priority-class-detail-item.injectable.ts | 33 +++++++ src/renderer/utils/rbac.ts | 1 + templates/create-resource/ClusterRole.yaml | 8 ++ templates/create-resource/PriorityClass.yaml | 8 ++ 24 files changed, 470 insertions(+), 2 deletions(-) create mode 100644 src/common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable.ts create mode 100644 src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts create mode 100644 src/common/k8s-api/endpoints/priority-class.api.injectable.ts create mode 100644 src/common/k8s-api/endpoints/priority-class.api.ts create mode 100644 src/common/k8s-api/endpoints/types/preemption-policy.ts create mode 100644 src/renderer/components/+config-priority-classes/index.ts create mode 100644 src/renderer/components/+config-priority-classes/priority-classes-details.scss create mode 100644 src/renderer/components/+config-priority-classes/priority-classes-details.tsx create mode 100644 src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx create mode 100644 src/renderer/components/+config-priority-classes/priority-classes-route-component.injectable.ts create mode 100644 src/renderer/components/+config-priority-classes/priority-classes.scss create mode 100644 src/renderer/components/+config-priority-classes/priority-classes.tsx create mode 100644 src/renderer/components/+config-priority-classes/store.injectable.ts create mode 100644 src/renderer/components/+config-priority-classes/store.ts create mode 100644 src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts create mode 100644 templates/create-resource/PriorityClass.yaml diff --git a/extensions/metrics-cluster-feature/resources/12-kube-state-metrics-clusterrole.yml b/extensions/metrics-cluster-feature/resources/12-kube-state-metrics-clusterrole.yml index 0568d6a560..8101bcc05d 100644 --- a/extensions/metrics-cluster-feature/resources/12-kube-state-metrics-clusterrole.yml +++ b/extensions/metrics-cluster-feature/resources/12-kube-state-metrics-clusterrole.yml @@ -119,3 +119,10 @@ rules: verbs: - list - watch + - apiGroups: + - scheduling.k8s.io + resources: + - priorityclasses + verbs: + - list + - watch diff --git a/src/common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable.ts b/src/common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable.ts new file mode 100644 index 0000000000..45b7a34eeb --- /dev/null +++ b/src/common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import priorityClassesRouteInjectable from "./priority-classes-route.injectable"; +import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; + +const navigateToPriorityClassesInjectable = getInjectable({ + id: "navigate-to-priority-classes", + + instantiate: (di) => { + const navigateToRoute = di.inject(navigateToRouteInjectionToken); + const route = di.inject(priorityClassesRouteInjectable); + + return () => navigateToRoute(route); + }, +}); + +export default navigateToPriorityClassesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts new file mode 100644 index 0000000000..1c3632c261 --- /dev/null +++ b/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable"; +import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; + +const priorityClassesRouteInjectable = getInjectable({ + id: "priority-classes-route", + + instantiate: (di) => { + const isAllowedResource = di.inject(isAllowedResourceInjectable, "priorityclasses"); + + return { + path: "/priorityclasses", + clusterFrame: true, + isEnabled: isAllowedResource, + }; + }, + + injectionToken: frontEndRouteInjectionToken, +}); + +export default priorityClassesRouteInjectable; diff --git a/src/common/k8s-api/endpoints/index.ts b/src/common/k8s-api/endpoints/index.ts index 5038915657..19a7cac378 100644 --- a/src/common/k8s-api/endpoints/index.ts +++ b/src/common/k8s-api/endpoints/index.ts @@ -29,6 +29,7 @@ export * from "./pod.api"; export * from "./pod-disruption-budget.api"; export * from "./pod-metrics.api"; export * from "./pod-security-policy.api"; +export * from "./priority-class.api"; export * from "./replica-set.api"; export * from "./resource-quota.api"; export * from "./role.api"; diff --git a/src/common/k8s-api/endpoints/priority-class.api.injectable.ts b/src/common/k8s-api/endpoints/priority-class.api.injectable.ts new file mode 100644 index 0000000000..4599839580 --- /dev/null +++ b/src/common/k8s-api/endpoints/priority-class.api.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import assert from "assert"; +import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; +import { PriorityClassApi } from "./priority-class.api"; + +const priorityClassApiInjectable = getInjectable({ + id: "priority-class-api", + instantiate: (di) => { + assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "PriorityClassApi is only available in certain environments"); + + return new PriorityClassApi(); + }, +}); + +export default priorityClassApiInjectable; diff --git a/src/common/k8s-api/endpoints/priority-class.api.ts b/src/common/k8s-api/endpoints/priority-class.api.ts new file mode 100644 index 0000000000..516ae07ef4 --- /dev/null +++ b/src/common/k8s-api/endpoints/priority-class.api.ts @@ -0,0 +1,66 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { DerivedKubeApiOptions } from "../kube-api"; +import { KubeApi } from "../kube-api"; +import type { KubeJsonApiData } from "../kube-json-api"; +import type { ClusterScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; +import { KubeObject } from "../kube-object"; +import type { PreemptionPolicy } from "./types/preemption-policy"; + +export interface PriorityClassData extends KubeJsonApiData, void, void> { + description?: string; + globalDefault?: boolean; + preemptionPolicy?: PreemptionPolicy; + value: number; +} + +export class PriorityClass extends KubeObject< + ClusterScopedMetadata, + void, + void +> { + static readonly kind = "PriorityClass"; + static readonly namespaced = false; + static readonly apiBase = "/apis/scheduling.k8s.io/v1/priorityclasses"; + + description?: string; + globalDefault?: boolean; + preemptionPolicy?: PreemptionPolicy; + value?: number; + + constructor({ description, globalDefault, preemptionPolicy, value, ...rest }: PriorityClassData) { + super(rest); + this.description = description; + this.globalDefault = globalDefault; + this.preemptionPolicy = preemptionPolicy; + this.value = value; + } + + getDescription() { + return this.description || ""; + } + + getGlobalDefault() { + return (this.globalDefault || false).toString(); + } + + getPreemptionPolicy() { + return this.preemptionPolicy || "PreemptLowerPriority"; + } + + getValue() { + return this.value; + } +} + +export class PriorityClassApi extends KubeApi { + constructor(opts: DerivedKubeApiOptions = {}) { + super({ + objectConstructor: PriorityClass, + ...opts, + }); + } +} diff --git a/src/common/k8s-api/endpoints/types/preemption-policy.ts b/src/common/k8s-api/endpoints/types/preemption-policy.ts new file mode 100644 index 0000000000..32d37b93a3 --- /dev/null +++ b/src/common/k8s-api/endpoints/types/preemption-policy.ts @@ -0,0 +1,6 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +export type PreemptionPolicy = "Never" | "PreemptLowerPriority"; diff --git a/src/common/rbac.ts b/src/common/rbac.ts index 6f5a282262..965fff5e46 100644 --- a/src/common/rbac.ts +++ b/src/common/rbac.ts @@ -7,7 +7,7 @@ export type KubeResource = "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" | + "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" | "priorityclasses" | "roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts"; export interface KubeApiResource extends KubeApiResourceData { @@ -41,6 +41,7 @@ export const apiResourceRecord: Record = { "pods": { kind: "Pod" }, "poddisruptionbudgets": { kind: "PodDisruptionBudget", group: "policy" }, "podsecuritypolicies": { kind: "PodSecurityPolicy", group: "policy" }, + "priorityclasses": { kind: "PriorityClass", group: "scheduling.k8s.io" }, "resourcequotas": { kind: "ResourceQuota" }, "replicasets": { kind: "ReplicaSet", group: "apps" }, "roles": { kind: "Role", group: "rbac.authorization.k8s.io" }, diff --git a/src/extensions/common-api/k8s-api.ts b/src/extensions/common-api/k8s-api.ts index 31977834a0..45d8230597 100644 --- a/src/extensions/common-api/k8s-api.ts +++ b/src/extensions/common-api/k8s-api.ts @@ -67,6 +67,7 @@ export { LimitRange, HorizontalPodAutoscaler, PodDisruptionBudget, + PriorityClass, Service, Endpoints as Endpoint, Ingress, IngressApi, diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts index aa7e4a5c02..3401d979f7 100644 --- a/src/extensions/renderer-api/k8s-api.ts +++ b/src/extensions/renderer-api/k8s-api.ts @@ -25,6 +25,7 @@ import resourceQuotaApiInjectable from "../../common/k8s-api/endpoints/resource- import limitRangeApiInjectable from "../../common/k8s-api/endpoints/limit-range.api.injectable"; import horizontalPodAutoscalerApiInjectable from "../../common/k8s-api/endpoints/horizontal-pod-autoscaler.api.injectable"; import podDisruptionBudgetApiInjectable from "../../common/k8s-api/endpoints/pod-disruption-budget.api.injectable"; +import priorityClassStoreApiInjectable from "../../common/k8s-api/endpoints/priority-class.api.injectable"; import serviceApiInjectable from "../../common/k8s-api/endpoints/service.api.injectable"; import endpointsApiInjectable from "../../common/k8s-api/endpoints/endpoint.api.injectable"; import ingressApiInjectable from "../../common/k8s-api/endpoints/ingress.api.injectable"; @@ -68,6 +69,7 @@ export const limitRangeApi = asLegacyGlobalForExtensionApi(limitRangeApiInjectab export const serviceApi = asLegacyGlobalForExtensionApi(serviceApiInjectable); export const hpaApi = asLegacyGlobalForExtensionApi(horizontalPodAutoscalerApiInjectable); export const pdbApi = asLegacyGlobalForExtensionApi(podDisruptionBudgetApiInjectable); +export const pcApi = asLegacyGlobalForExtensionApi(priorityClassStoreApiInjectable); export const endpointApi = asLegacyGlobalForExtensionApi(endpointsApiInjectable); export const ingressApi = asLegacyGlobalForExtensionApi(ingressApiInjectable); export const networkPolicyApi = asLegacyGlobalForExtensionApi(networkPolicyApiInjectable); @@ -102,6 +104,7 @@ export type { ResourceQuotaStore as ResourceQuotasStore } from "../../renderer/c export type { LimitRangeStore as LimitRangesStore } from "../../renderer/components/+config-limit-ranges/store"; export type { HorizontalPodAutoscalerStore as HPAStore } from "../../renderer/components/+config-autoscalers/store"; export type { PodDisruptionBudgetStore as PodDisruptionBudgetsStore } from "../../renderer/components/+config-pod-disruption-budgets/store"; +export type { PriorityClassStore as PriorityClassStoreStore } from "../../renderer/components/+config-priority-classes/store"; export type { ServiceStore } from "../../renderer/components/+network-services/store"; export type { EndpointsStore as EndpointStore } from "../../renderer/components/+network-endpoints/store"; export type { IngressStore } from "../../renderer/components/+network-ingresses/store"; diff --git a/src/renderer/components/+config-priority-classes/index.ts b/src/renderer/components/+config-priority-classes/index.ts new file mode 100644 index 0000000000..73538c3e18 --- /dev/null +++ b/src/renderer/components/+config-priority-classes/index.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +export * from "./priority-classes"; +export * from "./priority-classes-details"; diff --git a/src/renderer/components/+config-priority-classes/priority-classes-details.scss b/src/renderer/components/+config-priority-classes/priority-classes-details.scss new file mode 100644 index 0000000000..92697c079e --- /dev/null +++ b/src/renderer/components/+config-priority-classes/priority-classes-details.scss @@ -0,0 +1,6 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +.PriorityClassesDetails {} \ No newline at end of file diff --git a/src/renderer/components/+config-priority-classes/priority-classes-details.tsx b/src/renderer/components/+config-priority-classes/priority-classes-details.tsx new file mode 100644 index 0000000000..5bfcc4ca07 --- /dev/null +++ b/src/renderer/components/+config-priority-classes/priority-classes-details.tsx @@ -0,0 +1,43 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./priority-classes.scss"; + +import React from "react"; +import { observer } from "mobx-react"; +import { DrawerItem } from "../drawer"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import type { PriorityClass } from "../../../common/k8s-api/endpoints"; +import { KubeObjectMeta } from "../kube-object-meta"; + +export interface PriorityClassesDetailsProps extends KubeObjectDetailsProps { +} + +@observer +export class PriorityClassesDetails extends React.Component { + + render() { + const { object: pc } = this.props; + + return ( +
+ + + + {pc.getDescription()} + + + + {pc.getValue()} + + + + {pc.getGlobalDefault()} + + +
+ ); + } +} diff --git a/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx b/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx new file mode 100644 index 0000000000..bc2512714f --- /dev/null +++ b/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx @@ -0,0 +1,38 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; + +import priorityClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable"; +import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; +import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; +import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; +import navigateToPriorityClassesInjectable from "../../../common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable"; + +const priorityClassesSidebarItemsInjectable = getInjectable({ + id: "priority-classes-sidebar-items", + + instantiate: (di) => { + const route = di.inject(priorityClassesRouteInjectable); + const navigateToPriorityClasses = di.inject(navigateToPriorityClassesInjectable); + const routeIsActive = di.inject(routeIsActiveInjectable, route); + + return computed(() => [ + { + id: "priority-classes", + parentId: configSidebarItemId, + title: "Priority Classes", + onClick: navigateToPriorityClasses, + isActive: routeIsActive, + isVisible: route.isEnabled, + orderNumber: 60, + }, + ]); + }, + + injectionToken: sidebarItemsInjectionToken, +}); + +export default priorityClassesSidebarItemsInjectable; diff --git a/src/renderer/components/+config-priority-classes/priority-classes-route-component.injectable.ts b/src/renderer/components/+config-priority-classes/priority-classes-route-component.injectable.ts new file mode 100644 index 0000000000..01d7aceb24 --- /dev/null +++ b/src/renderer/components/+config-priority-classes/priority-classes-route-component.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { PriorityClasses } from "./priority-classes"; +import priorityClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable"; +import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; + +const podDisruptionBudgetsRouteComponentInjectable = getInjectable({ + id: "priority-classes-route-component", + + instantiate: (di) => ({ + route: di.inject(priorityClassesRouteInjectable), + Component: PriorityClasses, + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); + +export default podDisruptionBudgetsRouteComponentInjectable; diff --git a/src/renderer/components/+config-priority-classes/priority-classes.scss b/src/renderer/components/+config-priority-classes/priority-classes.scss new file mode 100644 index 0000000000..082f3ae68e --- /dev/null +++ b/src/renderer/components/+config-priority-classes/priority-classes.scss @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +.PriorityClasses { + .TableCell { + &.name { + flex: 2; + } + + &.warning { + @include table-cell-warning; + } + + &.keys { + flex: 2.5; + } + + &.age { + flex: .5; + } + } +} \ No newline at end of file diff --git a/src/renderer/components/+config-priority-classes/priority-classes.tsx b/src/renderer/components/+config-priority-classes/priority-classes.tsx new file mode 100644 index 0000000000..4706fd0269 --- /dev/null +++ b/src/renderer/components/+config-priority-classes/priority-classes.tsx @@ -0,0 +1,87 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./priority-classes.scss"; + +import * as React from "react"; +import { observer } from "mobx-react"; +import type { PriorityClass } from "../../../common/k8s-api/endpoints/priority-class.api"; +import { KubeObjectStatusIcon } from "../kube-object-status-icon"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { KubeObjectListLayout } from "../kube-object-list-layout"; +import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; +import { KubeObjectAge } from "../kube-object/age"; +import { autoBind } from "../../../common/utils"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import priorityClassStoreInjectable from "./store.injectable"; +import type { PriorityClassStore } from "./store"; + +enum columnId { + name = "name", + value = "value", + globalDefault = "global-default", + age = "age", +} + +export interface PriorityClassesProps extends KubeObjectDetailsProps { +} + +interface Dependencies { + priorityClassStore: PriorityClassStore; +} + +@observer +class NonInjectedPriorityClasses extends React.Component { + constructor(props: PriorityClassesProps & Dependencies) { + super(props); + autoBind(this); + } + + render() { + const { priorityClassStore } = this.props; + + return ( + + pc.getName(), + [columnId.value]: pc => pc.getValue(), + [columnId.globalDefault]: pc => pc.getGlobalDefault(), + [columnId.age]: pc => -pc.getCreationTimestamp(), + }} + searchFilters={[ + pc => pc.getSearchFields(), + ]} + renderHeaderTitle="Priority Classes" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Value", className: "value", sortBy: columnId.value, id: columnId.value }, + { title: "Global Default", className: "global-default", sortBy: columnId.globalDefault, id: columnId.globalDefault }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={pc => [ + pc.getName(), + , + pc.getValue(), + pc.getGlobalDefault(), + , + ]} + /> + + ); + } +} + +export const PriorityClasses = withInjectables(NonInjectedPriorityClasses, { + getProps: (di, props) => ({ + ...props, + priorityClassStore: di.inject(priorityClassStoreInjectable), + }), +}); diff --git a/src/renderer/components/+config-priority-classes/store.injectable.ts b/src/renderer/components/+config-priority-classes/store.injectable.ts new file mode 100644 index 0000000000..68677a3b5f --- /dev/null +++ b/src/renderer/components/+config-priority-classes/store.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import assert from "assert"; +import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/manager.injectable"; +import priorityClassApiInjectable from "../../../common/k8s-api/endpoints/priority-class.api.injectable"; +import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; +import { PriorityClassStore } from "./store"; + +const priorityClassStoreInjectable = getInjectable({ + id: "priority-class-store", + instantiate: (di) => { + assert(di.inject(storesAndApisCanBeCreatedInjectable), "priorityClassStore is only available in certain environments"); + + const api = di.inject(priorityClassApiInjectable); + + return new PriorityClassStore(api); + }, + injectionToken: kubeObjectStoreInjectionToken, +}); + +export default priorityClassStoreInjectable; diff --git a/src/renderer/components/+config-priority-classes/store.ts b/src/renderer/components/+config-priority-classes/store.ts new file mode 100644 index 0000000000..f11fdce838 --- /dev/null +++ b/src/renderer/components/+config-priority-classes/store.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; +import type { PriorityClass, PriorityClassApi } from "../../../common/k8s-api/endpoints/priority-class.api"; + +export class PriorityClassStore extends KubeObjectStore { +} diff --git a/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx b/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx index 5bb37de137..8bf2b196f5 100644 --- a/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx +++ b/src/renderer/components/command-palette/registered-commands/internal-commands.injectable.tsx @@ -24,13 +24,14 @@ import navigateToResourceQuotasInjectable from "../../../../common/front-end-rou import navigateToLimitRangesInjectable from "../../../../common/front-end-routing/routes/cluster/config/limit-ranges/navigate-to-limit-ranges.injectable"; import navigateToHorizontalPodAutoscalersInjectable from "../../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/navigate-to-horizontal-pod-autoscalers.injectable"; import navigateToPodDisruptionBudgetsInjectable from "../../../../common/front-end-routing/routes/cluster/config/pod-disruption-budgets/navigate-to-pod-disruption-budgets.injectable"; +import navigateToPriorityClassesInjectable from "../../../../common/front-end-routing/routes/cluster/config/priority-classes/navigate-to-priority-classes.injectable"; import navigateToServicesInjectable from "../../../../common/front-end-routing/routes/cluster/network/services/navigate-to-services.injectable"; import navigateToEndpointsInjectable from "../../../../common/front-end-routing/routes/cluster/network/endpoints/navigate-to-endpoints.injectable"; import navigateToIngressesInjectable from "../../../../common/front-end-routing/routes/cluster/network/ingresses/navigate-to-ingresses.injectable"; import navigateToNetworkPoliciesInjectable from "../../../../common/front-end-routing/routes/cluster/network/network-policies/navigate-to-network-policies.injectable"; import navigateToNodesInjectable from "../../../../common/front-end-routing/routes/cluster/nodes/navigate-to-nodes.injectable"; import navigateToPodsInjectable from "../../../../common/front-end-routing/routes/cluster/workloads/pods/navigate-to-pods.injectable"; -import navigateToDeploymentsInjectable from "../../../../common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable"; +import navigateToDeploymentsInjectable from "../../../../common/front-end-routing/routes/cluster/workloads/deployments/navigate-to-deployments.injectable"; import navigateToDaemonsetsInjectable from "../../../../common/front-end-routing/routes/cluster/workloads/daemonsets/navigate-to-daemonsets.injectable"; import navigateToStatefulsetsInjectable from "../../../../common/front-end-routing/routes/cluster/workloads/statefulsets/navigate-to-statefulsets.injectable"; import navigateToJobsInjectable from "../../../../common/front-end-routing/routes/cluster/workloads/jobs/navigate-to-jobs.injectable"; @@ -57,6 +58,7 @@ interface Dependencies { navigateToLimitRanges: () => void; navigateToHorizontalPodAutoscalers: () => void; navigateToPodDisruptionBudgets: () => void; + navigateToPriorityClasses: () => void; navigateToServices: () => void; navigateToEndpoints: () => void; navigateToIngresses: () => void; @@ -127,6 +129,12 @@ function getInternalCommands(dependencies: Dependencies): CommandRegistration[] isActive: isKubernetesClusterActive, action: () => dependencies.navigateToPodDisruptionBudgets(), }, + { + id: "cluster.viewPriorityClasses", + title: "Cluster: View PriorityClasses", + isActive: isKubernetesClusterActive, + action: () => dependencies.navigateToPriorityClasses(), + }, { id: "cluster.viewServices", title: "Cluster: View Services", @@ -258,6 +266,7 @@ const internalCommandsInjectable = getInjectable({ navigateToLimitRanges: di.inject(navigateToLimitRangesInjectable), navigateToHorizontalPodAutoscalers: di.inject(navigateToHorizontalPodAutoscalersInjectable), navigateToPodDisruptionBudgets: di.inject(navigateToPodDisruptionBudgetsInjectable), + navigateToPriorityClasses: di.inject(navigateToPriorityClassesInjectable), navigateToServices: di.inject(navigateToServicesInjectable), navigateToEndpoints: di.inject(navigateToEndpointsInjectable), navigateToIngresses: di.inject(navigateToIngressesInjectable), diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts new file mode 100644 index 0000000000..63fa5c18b8 --- /dev/null +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/priority-class-detail-item.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token"; +import { computed } from "mobx"; +import { PriorityClassesDetails } from "../../../+config-priority-classes"; +import { kubeObjectMatchesToKindAndApiVersion } from "../kube-object-matches-to-kind-and-api-version"; +import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable"; + +const priorityClassDetailItemInjectable = getInjectable({ + id: "priority-class-detail-item", + + instantiate: (di) => { + const kubeObject = di.inject(currentKubeObjectInDetailsInjectable); + + return { + Component: PriorityClassesDetails, + enabled: computed(() => isPriorityClass(kubeObject.get())), + orderNumber: 10, + }; + }, + + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +const isPriorityClass = kubeObjectMatchesToKindAndApiVersion( + "PriorityClass", + ["scheduling.k8s.io/v1"], +); + +export default priorityClassDetailItemInjectable; diff --git a/src/renderer/utils/rbac.ts b/src/renderer/utils/rbac.ts index e8d9ecb429..8f3745d3a5 100644 --- a/src/renderer/utils/rbac.ts +++ b/src/renderer/utils/rbac.ts @@ -31,6 +31,7 @@ export const ResourceNames: Record = { "horizontalpodautoscalers": "Horizontal Pod Autoscalers", "podsecuritypolicies": "Pod Security Policies", "poddisruptionbudgets": "Pod Disruption Budgets", + "priorityclasses": "Priority Classes", "limitranges": "Limit Ranges", "roles": "Roles", "rolebindings": "Role Bindings", diff --git a/templates/create-resource/ClusterRole.yaml b/templates/create-resource/ClusterRole.yaml index 0e66a6ada0..fde73845ed 100644 --- a/templates/create-resource/ClusterRole.yaml +++ b/templates/create-resource/ClusterRole.yaml @@ -196,3 +196,11 @@ rules: - get - list - watch + - apiGroups: + - scheduling.k8s.io + resources: + - priorityclass + verbs: + - get + - list + - watch diff --git a/templates/create-resource/PriorityClass.yaml b/templates/create-resource/PriorityClass.yaml new file mode 100644 index 0000000000..38301e4a4f --- /dev/null +++ b/templates/create-resource/PriorityClass.yaml @@ -0,0 +1,8 @@ +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: overprovisioning +description: Priority class used by overprovisioning. +preemptionPolicy: PreemptLowerPriority +globalDefault: false +value: -1