diff --git a/src/common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-classes.injectable.ts b/src/common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-classes.injectable.ts new file mode 100644 index 0000000000..4d6ca1757d --- /dev/null +++ b/src/common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-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 runtimeClassesRouteInjectable from "./runtime-classes-route.injectable"; +import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; + +const navigateToRuntimeClassesInjectable = getInjectable({ + id: "navigate-to-runtime-classes", + + instantiate: (di) => { + const navigateToRoute = di.inject(navigateToRouteInjectionToken); + const route = di.inject(runtimeClassesRouteInjectable); + + return () => navigateToRoute(route); + }, +}); + +export default navigateToRuntimeClassesInjectable; diff --git a/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts b/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts new file mode 100644 index 0000000000..72934f9158 --- /dev/null +++ b/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-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 runtimeClassesRouteInjectable = getInjectable({ + id: "runtime-classes-route", + + instantiate: (di) => { + const isAllowedResource = di.inject(isAllowedResourceInjectable, "runtimeclasses"); + + return { + path: "/runtimeclasses", + clusterFrame: true, + isEnabled: isAllowedResource, + }; + }, + + injectionToken: frontEndRouteInjectionToken, +}); + +export default runtimeClassesRouteInjectable; diff --git a/src/common/k8s-api/endpoints/index.ts b/src/common/k8s-api/endpoints/index.ts index 0e2e677521..e13a969faa 100644 --- a/src/common/k8s-api/endpoints/index.ts +++ b/src/common/k8s-api/endpoints/index.ts @@ -34,6 +34,7 @@ export * from "./replica-set.api"; export * from "./resource-quota.api"; export * from "./role.api"; export * from "./role-binding.api"; +export * from "./runtime-class.api"; export * from "./secret.api"; export * from "./self-subject-rules-reviews.api"; export * from "./service.api"; diff --git a/src/common/k8s-api/endpoints/pod.api.ts b/src/common/k8s-api/endpoints/pod.api.ts index 95dd694c1e..862a28da94 100644 --- a/src/common/k8s-api/endpoints/pod.api.ts +++ b/src/common/k8s-api/endpoints/pod.api.ts @@ -677,6 +677,10 @@ export class Pod extends KubeObject< return this.spec?.priorityClassName || ""; } + getRuntimeClassName() { + return this.spec?.runtimeClassName || ""; + } + getServiceAccountName() { return this.spec?.serviceAccountName || ""; } diff --git a/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts b/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts new file mode 100644 index 0000000000..1b13d13270 --- /dev/null +++ b/src/common/k8s-api/endpoints/runtime-class.api.injectable.ts @@ -0,0 +1,22 @@ +/** + * 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 { RuntimeClassApi } from "./runtime-class.api"; +import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; + +const runtimeClassApiInjectable = getInjectable({ + id: "runtime-class-api", + instantiate: (di) => { + assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "RuntimeClassApi is only available in certain environments"); + + return new RuntimeClassApi(); + }, + + injectionToken: kubeApiInjectionToken, +}); + +export default runtimeClassApiInjectable; diff --git a/src/common/k8s-api/endpoints/runtime-class.api.ts b/src/common/k8s-api/endpoints/runtime-class.api.ts new file mode 100644 index 0000000000..fa1867ea07 --- /dev/null +++ b/src/common/k8s-api/endpoints/runtime-class.api.ts @@ -0,0 +1,72 @@ +/** + * 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, Toleration } from "../kube-object"; +import { KubeObject } from "../kube-object"; + +export interface RuntimeClassData extends KubeJsonApiData, void, void> { + handler: string; + overhead?: RuntimeClassOverhead; + scheduling?: RuntimeClassScheduling; +} + +export interface RuntimeClassOverhead { + podFixed?: string; +} + +export interface RuntimeClassScheduling { + nodeSelector?: Partial>; + tolerations?: Toleration[]; +} + +export class RuntimeClass extends KubeObject< + ClusterScopedMetadata, + void, + void +> { + static readonly kind = "RuntimeClass"; + static readonly namespaced = false; + static readonly apiBase = "/apis/node.k8s.io/v1/runtimeclasses"; + + handler: string; + overhead?: RuntimeClassOverhead; + scheduling?: RuntimeClassScheduling; + + constructor({ handler, overhead, scheduling, ...rest }: RuntimeClassData) { + super(rest); + this.handler = handler; + this.overhead = overhead; + this.scheduling = scheduling; + } + + getHandler() { + return this.handler; + } + + getPodFixed() { + return this.overhead?.podFixed ?? ""; + } + + getNodeSelectors(): string[] { + return Object.entries(this.scheduling?.nodeSelector ?? {}) + .map(values => values.join(": ")); + } + + getTolerations() { + return this.scheduling?.tolerations ?? []; + } +} + +export class RuntimeClassApi extends KubeApi { + constructor(opts: DerivedKubeApiOptions = {}) { + super({ + objectConstructor: RuntimeClass, + ...opts, + }); + } +} diff --git a/src/common/rbac.ts b/src/common/rbac.ts index 965fff5e46..71c636613a 100644 --- a/src/common/rbac.ts +++ b/src/common/rbac.ts @@ -7,7 +7,8 @@ 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" | "priorityclasses" | + "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" | + "priorityclasses" | "runtimeclasses" | "roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts"; export interface KubeApiResource extends KubeApiResourceData { @@ -42,6 +43,7 @@ export const apiResourceRecord: Record = { "poddisruptionbudgets": { kind: "PodDisruptionBudget", group: "policy" }, "podsecuritypolicies": { kind: "PodSecurityPolicy", group: "policy" }, "priorityclasses": { kind: "PriorityClass", group: "scheduling.k8s.io" }, + "runtimeclasses": { kind: "RuntimeClass", group: "node.k8s.io" }, "resourcequotas": { kind: "ResourceQuota" }, "replicasets": { kind: "ReplicaSet", group: "apps" }, "roles": { kind: "Role", group: "rbac.authorization.k8s.io" }, 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 index bc2512714f..2823467fdf 100644 --- a/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx +++ b/src/renderer/components/+config-priority-classes/priority-classes-items.injectable.tsx @@ -27,7 +27,7 @@ const priorityClassesSidebarItemsInjectable = getInjectable({ onClick: navigateToPriorityClasses, isActive: routeIsActive, isVisible: route.isEnabled, - orderNumber: 60, + orderNumber: 70, }, ]); }, 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 index 01d7aceb24..258d3eb336 100644 --- 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 @@ -7,7 +7,7 @@ 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({ +const priorityClassesRouteComponentInjectable = getInjectable({ id: "priority-classes-route-component", instantiate: (di) => ({ @@ -18,4 +18,4 @@ const podDisruptionBudgetsRouteComponentInjectable = getInjectable({ injectionToken: routeSpecificComponentInjectionToken, }); -export default podDisruptionBudgetsRouteComponentInjectable; +export default priorityClassesRouteComponentInjectable; diff --git a/src/renderer/components/+config-runtime-classes/index.ts b/src/renderer/components/+config-runtime-classes/index.ts new file mode 100644 index 0000000000..ff91dc6016 --- /dev/null +++ b/src/renderer/components/+config-runtime-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 "./runtime-classes"; +export * from "./runtime-classes-details"; diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.scss b/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.scss new file mode 100644 index 0000000000..f72acb7bc9 --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.scss @@ -0,0 +1,28 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +.RuntimeClassDetailsTolerations { + grid-template-columns: auto; + + .RuntimeClassTolerations { + margin-top: var(--margin); + } + + // Expanding value cell to cover 2 columns (whole Drawer width) + + >.name { + grid-row-start: 1; + grid-column-start: 1; + } + + >.value { + grid-row-start: 1; + grid-column-start: 1; + } + + .DrawerParamToggler>.params { + margin-left: var(--drawer-item-title-width); + } +} \ No newline at end of file diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.tsx new file mode 100644 index 0000000000..c06e0c63bc --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-classes-details-tolerations.tsx @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./runtime-classes-details-tolerations.scss"; +import React from "react"; +import { DrawerParamToggler, DrawerItem } from "../drawer"; +import type { Toleration, KubeObject } from "../../../common/k8s-api/kube-object"; +import { RuntimeClassTolerations } from "./runtime-classes-tolerations"; + +export interface KubeObjectWithTolerations extends KubeObject { + getTolerations(): Toleration[]; +} + +export interface RuntimeClassDetailsTolerationsProps { + runtimeClass: KubeObjectWithTolerations; +} + +export function RuntimeClassDetailsTolerations({ runtimeClass: runtimeClass }: RuntimeClassDetailsTolerationsProps) { + const tolerations = runtimeClass.getTolerations(); + + if (!tolerations.length) return null; + + return ( + + + + + + ); +} diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details.scss b/src/renderer/components/+config-runtime-classes/runtime-classes-details.scss new file mode 100644 index 0000000000..55c5abcc09 --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-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. + */ + +.RuntimeClassesDetails {} \ No newline at end of file diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx new file mode 100644 index 0000000000..309c2a73bd --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-classes-details.tsx @@ -0,0 +1,48 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./runtime-classes.scss"; + +import React from "react"; +import { observer } from "mobx-react"; +import { DrawerItem } from "../drawer"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import type { RuntimeClass } from "../../../common/k8s-api/endpoints"; +import { KubeObjectMeta } from "../kube-object-meta"; +import { Badge } from "../badge"; +import { RuntimeClassDetailsTolerations } from "./runtime-classes-details-tolerations"; + +export interface RuntimeClassesDetailsProps extends KubeObjectDetailsProps { +} + +@observer +export class RuntimeClassesDetails extends React.Component { + + render() { + const { object: rc } = this.props; + const nodeSelector = rc.getNodeSelectors(); + + return ( +
+ + + + {rc.getHandler()} + + + + + + + + +
+ ); + } +} diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-items.injectable.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-items.injectable.tsx new file mode 100644 index 0000000000..352504ca00 --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-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 runtimeClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/runtime-classes/runtime-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 navigateToRuntimeClassesInjectable from "../../../common/front-end-routing/routes/cluster/config/runtime-classes/navigate-to-runtime-classes.injectable"; + +const runtimeClassesSidebarItemsInjectable = getInjectable({ + id: "runtime-classes-sidebar-items", + + instantiate: (di) => { + const route = di.inject(runtimeClassesRouteInjectable); + const navigateToRuntimeClasses = di.inject(navigateToRuntimeClassesInjectable); + const routeIsActive = di.inject(routeIsActiveInjectable, route); + + return computed(() => [ + { + id: "runtime-classes", + parentId: configSidebarItemId, + title: "Runtime Classes", + onClick: navigateToRuntimeClasses, + isActive: routeIsActive, + isVisible: route.isEnabled, + orderNumber: 70, + }, + ]); + }, + + injectionToken: sidebarItemsInjectionToken, +}); + +export default runtimeClassesSidebarItemsInjectable; diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-route-component.injectable.ts b/src/renderer/components/+config-runtime-classes/runtime-classes-route-component.injectable.ts new file mode 100644 index 0000000000..5118cdcc08 --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-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 { RuntimeClasses } from "./runtime-classes"; +import runtimeClassesRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable"; +import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; + +const runtimeClassesRouteComponentInjectable = getInjectable({ + id: "runtime-classes-route-component", + + instantiate: (di) => ({ + route: di.inject(runtimeClassesRouteInjectable), + Component: RuntimeClasses, + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); + +export default runtimeClassesRouteComponentInjectable; diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.scss b/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.scss new file mode 100644 index 0000000000..e398f7424a --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.scss @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +.RuntimeClassTolerations { + .TableHead { + background-color: var(--drawerSubtitleBackground); + } + + .TableCell { + white-space: normal; + word-break: normal; + + &.key { + flex-grow: 3; + } + } +} \ No newline at end of file diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.tsx new file mode 100644 index 0000000000..bcb1776459 --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-classes-tolerations.tsx @@ -0,0 +1,69 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./runtime-classes-tolerations.scss"; +import React from "react"; +import uniqueId from "lodash/uniqueId"; + +import type { Toleration } from "../../../common/k8s-api/kube-object"; +import { Table, TableCell, TableHead, TableRow } from "../table"; + +export interface RuntimeClassTolerationsProps { + tolerations: Toleration[]; +} + +enum sortBy { + Key = "key", + Operator = "operator", + Effect = "effect", + Seconds = "seconds", + Value = "value", +} + +const getTableRow = (toleration: Toleration) => { + const { key, operator, effect, tolerationSeconds, value } = toleration; + + return ( + + {key} + {operator} + {value} + {effect} + {tolerationSeconds} + + ); +}; + +export function RuntimeClassTolerations({ tolerations }: RuntimeClassTolerationsProps) { + return ( + toleration.key, + [sortBy.Operator]: toleration => toleration.operator, + [sortBy.Effect]: toleration => toleration.effect, + [sortBy.Seconds]: toleration => toleration.tolerationSeconds, + }} + sortSyncWithUrl={false} + className="RuntimeClassTolerations" + renderRow={getTableRow} + > + + Key + Operator + Value + Effect + Seconds + +
+ ); +} diff --git a/src/renderer/components/+config-runtime-classes/runtime-classes.scss b/src/renderer/components/+config-runtime-classes/runtime-classes.scss new file mode 100644 index 0000000000..0f3b151059 --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-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. + */ + +.RuntimeClasses { + .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-runtime-classes/runtime-classes.tsx b/src/renderer/components/+config-runtime-classes/runtime-classes.tsx new file mode 100644 index 0000000000..990912f2d4 --- /dev/null +++ b/src/renderer/components/+config-runtime-classes/runtime-classes.tsx @@ -0,0 +1,83 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import "./runtime-classes.scss"; + +import * as React from "react"; +import { observer } from "mobx-react"; +import type { RuntimeClass } from "../../../common/k8s-api/endpoints/runtime-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 runtimeClassStoreInjectable from "./store.injectable"; +import type { RuntimeClassStore } from "./store"; + +enum columnId { + name = "name", + handler = "handler", + age = "age", +} + +export interface RuntimeClassesProps extends KubeObjectDetailsProps { +} + +interface Dependencies { + runtimeClassStore: RuntimeClassStore; +} + +@observer +class NonInjectedRuntimeClasses extends React.Component { + constructor(props: RuntimeClassesProps & Dependencies) { + super(props); + autoBind(this); + } + + render() { + const { runtimeClassStore } = this.props; + + return ( + + rc.getName(), + [columnId.handler]: rc => rc.getHandler(), + [columnId.age]: rc => -rc.getCreationTimestamp(), + }} + searchFilters={[ + rc => rc.getSearchFields(), + ]} + renderHeaderTitle="Runtime Classes" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { className: "warning", showWithColumn: columnId.name }, + { title: "Handler", className: "handler", sortBy: columnId.handler, id: columnId.handler }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={rc => [ + rc.getName(), + , + rc.getHandler(), + , + ]} + /> + + ); + } +} + +export const RuntimeClasses = withInjectables(NonInjectedRuntimeClasses, { + getProps: (di, props) => ({ + ...props, + runtimeClassStore: di.inject(runtimeClassStoreInjectable), + }), +}); diff --git a/src/renderer/components/+config-runtime-classes/store.injectable.ts b/src/renderer/components/+config-runtime-classes/store.injectable.ts new file mode 100644 index 0000000000..0a4a21d716 --- /dev/null +++ b/src/renderer/components/+config-runtime-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 runtimeClassApiInjectable from "../../../common/k8s-api/endpoints/runtime-class.api.injectable"; +import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; +import { RuntimeClassStore } from "./store"; + +const runtimeClassStoreInjectable = getInjectable({ + id: "runtime-class-store", + instantiate: (di) => { + assert(di.inject(storesAndApisCanBeCreatedInjectable), "runtimeClassStore is only available in certain environments"); + + const api = di.inject(runtimeClassApiInjectable); + + return new RuntimeClassStore(api); + }, + injectionToken: kubeObjectStoreInjectionToken, +}); + +export default runtimeClassStoreInjectable; diff --git a/src/renderer/components/+config-runtime-classes/store.ts b/src/renderer/components/+config-runtime-classes/store.ts new file mode 100644 index 0000000000..bdb849ac85 --- /dev/null +++ b/src/renderer/components/+config-runtime-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 { RuntimeClass, RuntimeClassApi } from "../../../common/k8s-api/endpoints/runtime-class.api"; + +export class RuntimeClassStore extends KubeObjectStore { +} diff --git a/src/renderer/components/+workloads-pods/pod-details.tsx b/src/renderer/components/+workloads-pods/pod-details.tsx index 081696bd12..da87515355 100644 --- a/src/renderer/components/+workloads-pods/pod-details.tsx +++ b/src/renderer/components/+workloads-pods/pod-details.tsx @@ -132,12 +132,15 @@ class NonInjectedPodDetails extends React.Component {pod.getServiceAccountName()} - + {pod.getQosClass()} + { + const kubeObject = di.inject(currentKubeObjectInDetailsInjectable); + + return { + Component: RuntimeClassesDetails, + enabled: computed(() => isRuntimeClass(kubeObject.get())), + orderNumber: 10, + }; + }, + + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +const isRuntimeClass = kubeObjectMatchesToKindAndApiVersion( + "RuntimeClass", + ["node.k8s.io/v1"], +); + +export default runtimeClassDetailItemInjectable; diff --git a/src/renderer/utils/rbac.ts b/src/renderer/utils/rbac.ts index 8f3745d3a5..9fcee6174c 100644 --- a/src/renderer/utils/rbac.ts +++ b/src/renderer/utils/rbac.ts @@ -32,6 +32,7 @@ export const ResourceNames: Record = { "podsecuritypolicies": "Pod Security Policies", "poddisruptionbudgets": "Pod Disruption Budgets", "priorityclasses": "Priority Classes", + "runtimeclasses": "Runtime Classes", "limitranges": "Limit Ranges", "roles": "Roles", "rolebindings": "Role Bindings", diff --git a/templates/create-resource/RuntimeClass.yaml b/templates/create-resource/RuntimeClass.yaml new file mode 100644 index 0000000000..e0311f1aae --- /dev/null +++ b/templates/create-resource/RuntimeClass.yaml @@ -0,0 +1,12 @@ +apiVersion: node.k8s.io/v1 +kind: RuntimeClass +metadata: + name: spot +handler: runc +scheduling: + nodeSelector: + eks.amazonaws.com/capacityType: "SPOT" + tolerations: + - key: eks.amazonaws.com/capacityType + value: SPOT + effect: NoSchedule