diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 15223a3492..e4063525ae 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,6 +2,5 @@ \ No newline at end of file diff --git a/.idea/jsLinters/eslint.xml b/.idea/jsLinters/eslint.xml deleted file mode 100644 index 541945bb08..0000000000 --- a/.idea/jsLinters/eslint.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts b/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts new file mode 100644 index 0000000000..e036d4b6a6 --- /dev/null +++ b/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts @@ -0,0 +1,30 @@ +/** + * 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 { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; +import { + shouldShowResourceInjectionToken, +} from "../../../../../cluster-store/allowed-resources-injection-token"; + +const ingressClassesesRouteInjectable = getInjectable({ + id: "ingress-classes-route", + + instantiate: (di) => { + const isEnabled = di.inject(shouldShowResourceInjectionToken, { + apiName: "ingressclasses", + group: "networking.k8s.io", + }); + + return { + path: "/ingress-classes", + clusterFrame: true, + isEnabled, + }; + }, + + injectionToken: frontEndRouteInjectionToken, +}); + +export default ingressClassesesRouteInjectable; diff --git a/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts b/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable.ts new file mode 100644 index 0000000000..22c666d5ae --- /dev/null +++ b/src/common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-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 { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; +import ingressClassesesRouteInjectable from "./ingress-classeses-route.injectable"; + +const navigateToIngressesInjectable = getInjectable({ + id: "navigate-to-ingress-classes", + + instantiate: (di) => { + const navigateToRoute = di.inject(navigateToRouteInjectionToken); + const route = di.inject(ingressClassesesRouteInjectable); + + return () => navigateToRoute(route); + }, +}); + +export default navigateToIngressesInjectable; diff --git a/src/common/k8s-api/endpoints/index.ts b/src/common/k8s-api/endpoints/index.ts index c0493e6370..33ec108a31 100644 --- a/src/common/k8s-api/endpoints/index.ts +++ b/src/common/k8s-api/endpoints/index.ts @@ -18,6 +18,7 @@ export * from "./endpoint.api"; export * from "./events.api"; export * from "./horizontal-pod-autoscaler.api"; export * from "./ingress.api"; +export * from "./ingress-class.api"; export * from "./job.api"; export * from "./lease.api"; export * from "./limit-range.api"; diff --git a/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts b/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts new file mode 100644 index 0000000000..33f8d69485 --- /dev/null +++ b/src/common/k8s-api/endpoints/ingress-class.api.injectable.ts @@ -0,0 +1,18 @@ +/** + * 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 { IngressClassApi } from "./ingress-class.api"; +import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; + +const ingressClassApiInjectable = getInjectable({ + id: "ingress-class-api", + instantiate: () => { + return new IngressClassApi(); + }, + + injectionToken: kubeApiInjectionToken, +}); + +export default ingressClassApiInjectable; diff --git a/src/common/k8s-api/endpoints/ingress-class.api.ts b/src/common/k8s-api/endpoints/ingress-class.api.ts new file mode 100644 index 0000000000..680fb7f686 --- /dev/null +++ b/src/common/k8s-api/endpoints/ingress-class.api.ts @@ -0,0 +1,101 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { KubeObjectMetadata, KubeObjectScope } from "../kube-object"; +import { KubeObject } from "../kube-object"; +import type { ResourceDescriptor } from "../kube-api"; +import { KubeApi } from "../kube-api"; + +export class IngressClassApi extends KubeApi { + constructor() { + super({ + objectConstructor: IngressClass, + checkPreferredVersion: true, + fallbackApiBases: ["/apis/extensions/v1beta1/ingressclasses"], + }); + } + + setAsDefault({ name }: ResourceDescriptor, isDefault = true) { + const reqUrl = this.formatUrlForNotListing({ name }); + + return this.request.patch(reqUrl, { + data: { + metadata: { + annotations: { + [IngressClass.ANNOTATION_IS_DEFAULT]: JSON.stringify(isDefault), + }, + }, + }, + }, { + headers: { + "content-type": "application/strategic-merge-patch+json", + }, + }); + } +} + +// API docs: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#ingressclass-v1-networking-k8s-io +export type IngressClassMetadata = KubeObjectMetadata & { + "name": string; + "labels"?: { + [name: string]: string | undefined; + "app.kubernetes.io/component"?: "controller"; + }; + "annotations"?: { + [name: string]: string | undefined; + "ingressclass.kubernetes.io/is-default-class"?: "true"; + }; +}; + +export interface IngressClassParametersReference { + "apiGroup": string; // k8s.example.net + "scope": "Namespace" | "Cluster"; + "kind": "ClusterIngressParameter" | "IngressParameter"; + "name": string; // external-config-1 + "namespace"?: string; // namespaced for IngressClass must be defined in `spec.parameters.namespace` instead of `metadata.namespace` (!) +} + +export interface IngressClassSpec { + controller: string; // "example.com/ingress-controller" + parameters?: IngressClassParametersReference; +} + +export interface IngressClassStatus { +} + +export class IngressClass extends KubeObject { + static readonly kind = "IngressClass"; + static readonly namespaced = false; + static readonly apiBase = "/apis/networking.k8s.io/v1/ingressclasses"; + static readonly ANNOTATION_IS_DEFAULT = "ingressclass.kubernetes.io/is-default-class"; + + getController(): string { + return this.spec.controller; + } + + getCtrlApiGroup() { + return this.spec?.parameters?.apiGroup; + } + + getCtrlScope() { + return this.spec?.parameters?.scope; + } + + getCtrlNs() { + return this.spec?.parameters?.namespace; + } + + getCtrlKind() { + return this.spec?.parameters?.kind; + } + + getCtrlName() { + return this.spec?.parameters?.name as string; + } + + get isDefault() { + return this.metadata.annotations?.[IngressClass.ANNOTATION_IS_DEFAULT] === "true"; + } +} diff --git a/src/common/rbac.ts b/src/common/rbac.ts index 99e564a377..39edc49b60 100644 --- a/src/common/rbac.ts +++ b/src/common/rbac.ts @@ -5,7 +5,7 @@ export type KubeResource = "namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "leases" | - "secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" | + "secrets" | "configmaps" | "ingresses" | "ingressclasses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" | "pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "jobs" | "cronjobs" | "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" | "priorityclasses" | "runtimeclasses" | @@ -87,6 +87,11 @@ export const apiResourceRecord: Record = { group: "networking.k8s.io", namespaced: true, }, + ingressclasses: { + kind: "IngressClass", + group: "networking.k8s.io", + namespaced: false, + }, jobs: { kind: "Job", group: "batch", diff --git a/src/extensions/renderer-api/k8s-api.ts b/src/extensions/renderer-api/k8s-api.ts index 6335559409..42b165602b 100644 --- a/src/extensions/renderer-api/k8s-api.ts +++ b/src/extensions/renderer-api/k8s-api.ts @@ -115,7 +115,8 @@ export type { PodDisruptionBudgetStore as PodDisruptionBudgetsStore } from "../. 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"; +export type { IngressStore } from "../../renderer/components/+network-ingresses/ingress-store"; +export type { IngressClassStore } from "../../renderer/components/+network-ingresses/ingress-class-store"; export type { NetworkPolicyStore } from "../../renderer/components/+network-policies/store"; export type { PersistentVolumeStore as PersistentVolumesStore } from "../../renderer/components/+storage-volumes/store"; export type { PersistentVolumeClaimStore as VolumeClaimStore } from "../../renderer/components/+storage-volume-claims/store"; diff --git a/src/renderer/components/+network-ingresses/index.ts b/src/renderer/components/+network-ingresses/index.ts index fabe5112ea..2b58e1f4d6 100644 --- a/src/renderer/components/+network-ingresses/index.ts +++ b/src/renderer/components/+network-ingresses/index.ts @@ -5,3 +5,5 @@ export * from "./ingresses"; export * from "./ingress-details"; +export * from "./ingress-classes"; +export * from "./ingress-class-details"; diff --git a/src/renderer/components/+network-ingresses/ingress-class-details.module.scss b/src/renderer/components/+network-ingresses/ingress-class-details.module.scss new file mode 100644 index 0000000000..5c2cfdf174 --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-class-details.module.scss @@ -0,0 +1,8 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +.IngressClassDetails { + --titles-color: var(--textColorSecondary); +} diff --git a/src/renderer/components/+network-ingresses/ingress-class-details.tsx b/src/renderer/components/+network-ingresses/ingress-class-details.tsx new file mode 100644 index 0000000000..2577f9e6b5 --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-class-details.tsx @@ -0,0 +1,64 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import styles from "./ingress-class-details.module.scss"; + +import React from "react"; +import { observer } from "mobx-react"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import { DrawerItem, DrawerTitle } from "../drawer"; +import type { IngressClass } from "../../../common/k8s-api/endpoints"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import { Badge } from "../badge"; + +export interface IngressClassDetailsProps extends KubeObjectDetailsProps { +} + +@observer +class NonInjectedIngressDetails extends React.Component { + renderParameters() { + const { object: ingressClass } = this.props; + + if (!ingressClass.spec.parameters) return; + + return ( + <> + Parameters + + {ingressClass.getCtrlName()} + + + {ingressClass.getCtrlNs()} + + + {ingressClass.getCtrlScope()} + + + {ingressClass.getCtrlKind()} + + + {ingressClass.getCtrlApiGroup()} + + + ); + } + + render() { + const { object: ingressClass } = this.props; + + return ( +
+ + + + {this.renderParameters()} +
+ ); + } +} + +export const IngressClassDetails = withInjectables<{}, IngressClassDetailsProps>(NonInjectedIngressDetails, { + getProps: (di, props) => (props), +}); diff --git a/src/renderer/components/+network-ingresses/ingress-class-menu.injectable.tsx b/src/renderer/components/+network-ingresses/ingress-class-menu.injectable.tsx new file mode 100644 index 0000000000..17b0db705e --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-class-menu.injectable.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 React from "react"; +import { getInjectable } from "@ogre-tools/injectable"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import { computed } from "mobx"; +import type { + KubeObjectMenuItemComponent, + KubeObjectMenuItem, +} from "../kube-object-menu/kube-object-menu-item-injection-token"; +import { + kubeObjectMenuItemInjectionToken, +} from "../kube-object-menu/kube-object-menu-item-injection-token"; +import { ingressClassSetDefaultInjectable } from "./ingress-class-set-default.injectable"; +import { MenuItem } from "../menu"; +import type { IngressClass } from "../../../common/k8s-api/endpoints/ingress-class.api"; +import type { KubeObjectMenuProps } from "../kube-object-menu"; +import { Icon } from "../icon"; +import hideDetailsInjectable from "../kube-detail-params/hide-details.injectable"; + +export interface IngressClassMenuProps extends KubeObjectMenuProps { +} + +export interface Dependencies { + setDefault: (item: IngressClass) => void; + hideDetails: () => void; +} + +export function NonInjectedIngressClassMenu( + { + object, + toolbar, + setDefault, + hideDetails, + }: IngressClassMenuProps & Dependencies) { + + function markItemAsDefaultIngressClass() { + setDefault(object); + hideDetails(); + } + + return ( + <> + + + Set as default + + + ); +} + +export const IngressClassMenu = withInjectables(NonInjectedIngressClassMenu, { + getProps: (di, props) => ({ + ...props, + setDefault: di.inject(ingressClassSetDefaultInjectable), + hideDetails: di.inject(hideDetailsInjectable), + }), +}); + + +const ingressClassMenuInjectable = getInjectable({ + id: "ingress-class-kube-object-menu", + + instantiate(): KubeObjectMenuItem { + return { + kind: "IngressClass", + apiVersions: ["networking.k8s.io/v1"], + Component: IngressClassMenu as KubeObjectMenuItemComponent, + enabled: computed(() => true), + orderNumber: 30, + }; + }, + + injectionToken: kubeObjectMenuItemInjectionToken, +}); + +export default ingressClassMenuInjectable; diff --git a/src/renderer/components/+network-ingresses/ingress-class-route-component.injectable.ts b/src/renderer/components/+network-ingresses/ingress-class-route-component.injectable.ts new file mode 100644 index 0000000000..fd7a5fbc9c --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-class-route-component.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 { + routeSpecificComponentInjectionToken, +} from "../../routes/route-specific-component-injection-token"; +import ingressClassesesRouteInjectable + from "../../../common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable"; +import { IngressClasses } from "./ingress-classes"; + +const ingressClassesRouteComponentInjectable = getInjectable({ + id: "ingress-classes-route-component", + + instantiate: (di) => ({ + route: di.inject(ingressClassesesRouteInjectable), + Component: IngressClasses, + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); + +export default ingressClassesRouteComponentInjectable; diff --git a/src/renderer/components/+network-ingresses/ingress-class-set-default.injectable.ts b/src/renderer/components/+network-ingresses/ingress-class-set-default.injectable.ts new file mode 100644 index 0000000000..c40ffd872f --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-class-set-default.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import type { IngressClass } from "../../../common/k8s-api/endpoints/ingress-class.api"; +import ingressClassApiInjectable + from "../../../common/k8s-api/endpoints/ingress-class.api.injectable"; +import ingressClassStoreInjectable from "./ingress-class-store.injectable"; + +export const ingressClassSetDefaultInjectable = getInjectable({ + id: "ingressClassSetDefaultInjectable", + + instantiate(di) { + const api = di.inject(ingressClassApiInjectable); + const store = di.inject(ingressClassStoreInjectable); + + return async (currentItem: IngressClass) => { + const defaultIngressClassesUpdate = store.items + .filter((item: IngressClass) => item.isDefault && currentItem !== item) + .map(item => api.setAsDefault({ name: item.getName() }, false)); + + await Promise.all(defaultIngressClassesUpdate); + await api.setAsDefault({ name: currentItem.getName() }); + }; + }, + + lifecycle: lifecycleEnum.singleton, +}); diff --git a/src/renderer/components/+network-ingresses/ingress-class-store.injectable.ts b/src/renderer/components/+network-ingresses/ingress-class-store.injectable.ts new file mode 100644 index 0000000000..20b7a4d4bc --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-class-store.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 assert from "assert"; +import { getInjectable } from "@ogre-tools/injectable"; +import { + kubeObjectStoreInjectionToken, +} from "../../../common/k8s-api/api-manager/manager.injectable"; +import ingressClassApiInjectable + from "../../../common/k8s-api/endpoints/ingress-class.api.injectable"; +import { IngressClassStore } from "./ingress-class-store"; +import clusterFrameContextForNamespacedResourcesInjectable + from "../../cluster-frame-context/for-namespaced-resources.injectable"; +import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; + +const ingressClassStoreInjectable = getInjectable({ + id: "ingress-class-store", + + instantiate: (di) => { + assert(di.inject(storesAndApisCanBeCreatedInjectable), "ingressClassStore is only available in certain environments"); + + const api = di.inject(ingressClassApiInjectable); + + return new IngressClassStore({ + context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + }, api); + }, + + injectionToken: kubeObjectStoreInjectionToken, +}); + +export default ingressClassStoreInjectable; diff --git a/src/renderer/components/+network-ingresses/ingress-class-store.ts b/src/renderer/components/+network-ingresses/ingress-class-store.ts new file mode 100644 index 0000000000..1467a9427a --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-class-store.ts @@ -0,0 +1,9 @@ +/** + * 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 { IngressClass, IngressClassApi } from "../../../common/k8s-api/endpoints/ingress-class.api"; + +export class IngressClassStore extends KubeObjectStore { +} diff --git a/src/renderer/components/+network-ingresses/ingress-classes.module.scss b/src/renderer/components/+network-ingresses/ingress-classes.module.scss new file mode 100644 index 0000000000..3756468ef8 --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-classes.module.scss @@ -0,0 +1,43 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +.IngressClasses { + :global(.TableCell) { + &.is_default { + :global(.Checkbox) { + align-items: flex-start; + } + } + + &.name { + } + + &.namespace { + } + + &.controller { + } + + &.apiGroup { + } + + &.scope { + } + + &.kind { + } + } + + :global(.Icon) { + &.set_default_icon { + filter: brightness(.75); + + &:hover { + filter: brightness(1.5); + } + } + } + +} diff --git a/src/renderer/components/+network-ingresses/ingress-classes.tsx b/src/renderer/components/+network-ingresses/ingress-classes.tsx new file mode 100644 index 0000000000..7b7fe8c0de --- /dev/null +++ b/src/renderer/components/+network-ingresses/ingress-classes.tsx @@ -0,0 +1,112 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import styles from "./ingress-classes.module.scss"; + +import React from "react"; +import { observer } from "mobx-react"; +import { KubeObjectListLayout } from "../kube-object-list-layout"; +import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import type { IngressClassStore } from "./ingress-class-store"; +import ingressClassStoreInjectable from "./ingress-class-store.injectable"; +import type { IngressClass } from "../../../common/k8s-api/endpoints/ingress-class.api"; +import { cssNames } from "../../utils"; +import { Icon } from "../icon"; + +enum columnId { + name = "name", + namespace = "namespace", + controller = "controller", + apiGroup = "apiGroup", + scope = "scope", // "Namespace" | "Cluster" + kind = "kind", // "ClusterIngressParameter" | "IngressParameter" +} + +interface Dependencies { + store: IngressClassStore; +} + +const NonInjectedIngressClasses = observer((props: Dependencies) => { + const { + store, + } = props; + + return ( + + resource.getCtrlName(), + [columnId.namespace]: (resource: IngressClass) => resource.getCtrlNs(), + [columnId.controller]: (resource: IngressClass) => resource.getController(), + [columnId.apiGroup]: (resource: IngressClass) => resource.getCtrlApiGroup(), + [columnId.scope]: (resource: IngressClass) => resource.getCtrlScope(), + [columnId.kind]: (resource: IngressClass) => resource.getCtrlKind(), + }} + searchFilters={[ + (resource: IngressClass) => resource.getSearchFields(), + (resource: IngressClass) => resource.getController(), + (resource: IngressClass) => resource.getCtrlApiGroup(), + (resource: IngressClass) => resource.getCtrlScope(), + (resource: IngressClass) => resource.getCtrlKind(), + ]} + renderHeaderTitle="Ingress Classes" + renderTableHeader={[ + { title: "Name", className: styles.name, sortBy: columnId.name, id: columnId.name }, + { + title: "Namespace", + className: styles.namespace, + sortBy: columnId.namespace, + id: columnId.namespace, + }, + { + title: "Controller", + className: styles.controller, + sortBy: columnId.controller, + id: columnId.controller, + }, + { + title: "API Group", + className: styles.apiGroup, + sortBy: columnId.apiGroup, + id: columnId.apiGroup, + }, + { title: "Scope", className: styles.scope, sortBy: columnId.scope, id: columnId.scope }, + { title: "Kind", className: styles.kind, sortBy: columnId.kind, id: columnId.kind }, + ]} + renderTableContents={(ingressClass: IngressClass) => [ +
+ {ingressClass.getName()} + {" "} + {ingressClass.isDefault && ( + + )} +
, + ingressClass.getCtrlNs(), + ingressClass.getController(), + ingressClass.getCtrlApiGroup(), + ingressClass.getCtrlScope(), + ingressClass.getCtrlKind(), + ]} + /> +
+ ); +}); + +export const IngressClasses = withInjectables(NonInjectedIngressClasses, { + getProps: (di, props) => ({ + ...props, + store: di.inject(ingressClassStoreInjectable), + }), +}); diff --git a/src/renderer/components/+network-ingresses/store.injectable.ts b/src/renderer/components/+network-ingresses/ingress-store.injectable.ts similarity index 95% rename from src/renderer/components/+network-ingresses/store.injectable.ts rename to src/renderer/components/+network-ingresses/ingress-store.injectable.ts index 0084a006a2..909c784b99 100644 --- a/src/renderer/components/+network-ingresses/store.injectable.ts +++ b/src/renderer/components/+network-ingresses/ingress-store.injectable.ts @@ -8,7 +8,7 @@ import { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manag import ingressApiInjectable from "../../../common/k8s-api/endpoints/ingress.api.injectable"; import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; -import { IngressStore } from "./store"; +import { IngressStore } from "./ingress-store"; const ingressStoreInjectable = getInjectable({ id: "ingress-store", diff --git a/src/renderer/components/+network-ingresses/store.ts b/src/renderer/components/+network-ingresses/ingress-store.ts similarity index 100% rename from src/renderer/components/+network-ingresses/store.ts rename to src/renderer/components/+network-ingresses/ingress-store.ts diff --git a/src/renderer/components/+network-ingresses/ingresses-sidebar-items.injectable.tsx b/src/renderer/components/+network-ingresses/ingresses-sidebar-items.injectable.tsx index 80e9a7939c..146447aaba 100644 --- a/src/renderer/components/+network-ingresses/ingresses-sidebar-items.injectable.tsx +++ b/src/renderer/components/+network-ingresses/ingresses-sidebar-items.injectable.tsx @@ -3,32 +3,50 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; import { computed } from "mobx"; - -import ingressesRouteInjectable from "../../../common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable"; -import { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; +import ingressesRouteInjectable + from "../../../common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable"; +import type { + SidebarItemRegistration } from "../layout/sidebar-items.injectable"; +import { + sidebarItemsInjectionToken, +} from "../layout/sidebar-items.injectable"; import { networkSidebarItemId } from "../+network/network-sidebar-items.injectable"; import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; -import navigateToIngressesInjectable from "../../../common/front-end-routing/routes/cluster/network/ingresses/navigate-to-ingresses.injectable"; +import navigateToIngressesInjectable + from "../../../common/front-end-routing/routes/cluster/network/ingresses/navigate-to-ingresses.injectable"; +import ingressClassesesRouteInjectable + from "../../../common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable"; +import navigateToIngressClassesInjectable + from "../../../common/front-end-routing/routes/cluster/network/ingress-class/navigate-to-ingress-classes.injectable"; const ingressesSidebarItemsInjectable = getInjectable({ id: "ingresses-sidebar-items", - instantiate: (di) => { - const route = di.inject(ingressesRouteInjectable); - const navigateToIngresses = di.inject(navigateToIngressesInjectable); - const routeIsActive = di.inject(routeIsActiveInjectable, route); + instantiate: (di): IComputedValue => { + const ingressRoute = di.inject(ingressesRouteInjectable); + const ingressClassRoute = di.inject(ingressClassesesRouteInjectable); return computed(() => [ { id: "ingresses", parentId: networkSidebarItemId, title: "Ingresses", - onClick: navigateToIngresses, - isActive: routeIsActive, - isVisible: route.isEnabled, + onClick: di.inject(navigateToIngressesInjectable), + isActive: di.inject(routeIsActiveInjectable, ingressRoute), + isVisible: ingressRoute.isEnabled, orderNumber: 30, }, + { + id: "ingressclasses", + parentId: networkSidebarItemId, + title: "Ingress Classes", + onClick: di.inject(navigateToIngressClassesInjectable), + isActive: di.inject(routeIsActiveInjectable, ingressClassRoute), + isVisible: ingressClassRoute.isEnabled, + orderNumber: 31, + }, ]); }, diff --git a/src/renderer/components/+network-ingresses/ingresses.tsx b/src/renderer/components/+network-ingresses/ingresses.tsx index 6956944483..e8c8745926 100644 --- a/src/renderer/components/+network-ingresses/ingresses.tsx +++ b/src/renderer/components/+network-ingresses/ingresses.tsx @@ -13,11 +13,11 @@ import { SiblingsInTabLayout } from "../layout/siblings-in-tab-layout"; import { KubeObjectAge } from "../kube-object/age"; import { computeRouteDeclarations } from "../../../common/k8s-api/endpoints"; import { prevDefault } from "../../utils"; -import type { IngressStore } from "./store"; +import type { IngressStore } from "./ingress-store"; import type { FilterByNamespace } from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import filterByNamespaceInjectable from "../+namespaces/namespace-select-filter-model/filter-by-namespace.injectable"; -import ingressStoreInjectable from "./store.injectable"; +import ingressStoreInjectable from "./ingress-store.injectable"; enum columnId { name = "name", diff --git a/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/ingress-class-detail-item.injectable.ts b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/ingress-class-detail-item.injectable.ts new file mode 100644 index 0000000000..4118c352e1 --- /dev/null +++ b/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/ingress-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 { IngressClassDetails } from "../../../+network-ingresses"; +import { kubeObjectMatchesToKindAndApiVersion } from "../kube-object-matches-to-kind-and-api-version"; +import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable"; + +const ingressClassDetailItemInjectable = getInjectable({ + id: "ingress-class-detail-item", + + instantiate: (di) => { + const kubeObject = di.inject(currentKubeObjectInDetailsInjectable); + + return { + Component: IngressClassDetails, + enabled: computed(() => isIngressClass(kubeObject.value.get()?.object)), + orderNumber: 10, + }; + }, + + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export const isIngressClass = kubeObjectMatchesToKindAndApiVersion("IngressClass", [ + "networking.k8s.io/v1", + "extensions/v1beta1", +]); + +export default ingressClassDetailItemInjectable; diff --git a/src/renderer/utils/rbac.ts b/src/renderer/utils/rbac.ts index 93c153435f..78fe66b273 100644 --- a/src/renderer/utils/rbac.ts +++ b/src/renderer/utils/rbac.ts @@ -17,6 +17,7 @@ export const ResourceNames: Record = { "secrets": "Secrets", "configmaps": "Config Maps", "ingresses": "Ingresses", + "ingressclasses": "Ingress Classes", "networkpolicies": "Network Policies", "persistentvolumeclaims": "Persistent Volume Claims", "persistentvolumes": "Persistent Volumes",