diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/mutating-webhook-configurations-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/mutating-webhook-configurations-route.injectable.ts new file mode 100644 index 0000000000..5812f1258d --- /dev/null +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/mutating-webhook-configurations-route.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { + shouldShowResourceInjectionToken, +} from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; +import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; +import { getInjectable } from "@ogre-tools/injectable"; + +const mutatingWebhookConfigurationsRouteInjectable = getInjectable({ + id: "mutatingwebhookconfigurations", + + instantiate: (di) => ({ + path: "/mutatingwebhookconfigurations", + clusterFrame: true, + isEnabled: di.inject(shouldShowResourceInjectionToken, { + apiName: "mutatingwebhookconfigurations", + group: "admissionregistration.k8s.io", + }), + }), + + injectionToken: frontEndRouteInjectionToken, +}); + +export default mutatingWebhookConfigurationsRouteInjectable; diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/navigate-to-mutating-webhook-configurations.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/navigate-to-mutating-webhook-configurations.injectable.ts new file mode 100644 index 0000000000..ffb0e4b3d0 --- /dev/null +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/navigate-to-mutating-webhook-configurations.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 mutatingWebhookConfigurationsRouteInjectable from "./mutating-webhook-configurations-route.injectable"; +import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token"; + +const navigateToMutatingWebhookConfigurationsInjectable = getInjectable({ + id: "navigate-to-mutating-webhook-configurations", + + instantiate: (di) => { + const navigateToRoute = di.inject(navigateToRouteInjectionToken); + const route = di.inject(mutatingWebhookConfigurationsRouteInjectable); + + return () => navigateToRoute(route); + }, +}); + +export default navigateToMutatingWebhookConfigurationsInjectable; diff --git a/packages/core/src/common/k8s-api/endpoints/index.ts b/packages/core/src/common/k8s-api/endpoints/index.ts index 8fb6cbdabd..5a83bef417 100644 --- a/packages/core/src/common/k8s-api/endpoints/index.ts +++ b/packages/core/src/common/k8s-api/endpoints/index.ts @@ -22,6 +22,7 @@ export * from "./ingress-class.api"; export * from "./job.api"; export * from "./lease.api"; export * from "./limit-range.api"; +export * from "./mutating-webhook-configuration.api"; export * from "./namespace.api"; export * from "./network-policy.api"; export * from "./node.api"; diff --git a/packages/core/src/common/k8s-api/endpoints/mutating-webhook-configuration-api.injectable.ts b/packages/core/src/common/k8s-api/endpoints/mutating-webhook-configuration-api.injectable.ts new file mode 100644 index 0000000000..c307f9ddf1 --- /dev/null +++ b/packages/core/src/common/k8s-api/endpoints/mutating-webhook-configuration-api.injectable.ts @@ -0,0 +1,27 @@ +/** + * 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 { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import loggerInjectable from "../../logger.injectable"; +import maybeKubeApiInjectable from "../maybe-kube-api.injectable"; +import { MutatingWebhookConfigurationApi } from "./mutating-webhook-configuration.api"; + +const mutatingWebhookConfigurationApiInjectable = getInjectable({ + id: "mutating-webhook-configuration", + instantiate: (di) => { + assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "mutatingWebhookApi is only available in certain environments"); + + return new MutatingWebhookConfigurationApi({ + logger: di.inject(loggerInjectable), + maybeKubeApi: di.inject(maybeKubeApiInjectable), + }); + }, + + injectionToken: kubeApiInjectionToken, +}); + +export default mutatingWebhookConfigurationApiInjectable; diff --git a/packages/core/src/common/k8s-api/endpoints/mutating-webhook-configuration.api.ts b/packages/core/src/common/k8s-api/endpoints/mutating-webhook-configuration.api.ts new file mode 100644 index 0000000000..bdfde072bf --- /dev/null +++ b/packages/core/src/common/k8s-api/endpoints/mutating-webhook-configuration.api.ts @@ -0,0 +1,155 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { LabelSelector, NamespaceScopedMetadata, KubeObjectMetadata, KubeObjectScope } from "../kube-object"; +import { KubeObject } from "../kube-object"; +import type { DerivedKubeApiOptions, KubeApiDependencies } from "../kube-api"; +import { KubeApi } from "../kube-api"; +import type { KubeJsonApiData } from "../kube-json-api"; + +interface WebhookClientConfig { + // `url` gives the location of the webhook + url?: string; + + // `service` is a reference to the service for this webhook. Either `service` or `url` must be specified. + service?: ServiceReference; + + // `caBundle` is a PEM encoded CA bundle which will be used to validate the webhook's server certificate. + // If unspecified, system trust roots on the apiserver are used. + caBundle?: string; +} + +interface RuleWithOperations { + // APIGroups is the API groups the resources belong to. '*' is all groups. If '*' is present, the length of the slice must be one. + apiGroups: string[]; + + // APIVersions is the API versions the resources belong to. '*' is all versions. + apiVersions?: string[]; + + // Resources is a list of resources this rule applies to. + // For example: 'pods' means pods. + // '*' means all resources, but not subresources. + // 'pods/' means all subresources of pods. + // '*/scale' means all scale subresources. Allowed values are "Resource" / "Resource/Scale" / "Resource/Status". + resources: string[]; + + // Operations is a list of operations this rule applies to. + // The valid values are: "CREATE" / "UPDATE" / "DELETE" / "CONNECT". + operations: string[]; + + // Scope specifies the scope of this rule. Valid values are "Cluster" / "Namespace". + // Default is "Cluster". + scope?: string; +} + +interface MutatingWebhook { + // The name of the webhook configuration. + name: string; + + // ClientConfig defines how to communicate with the hook. + clientConfig: WebhookClientConfig; + + // Rules describes what operations on what resources/subresources the webhook cares about. + // The webhook cares about an operation if it matches _any_ Rule. + rules?: RuleWithOperations[]; + + // AdmissionReviewVersions is an ordered list of preferred `AdmissionReview` versions + // the webhook expects. API server will try to use first version in the list which it + // supports. If none of the versions specified in this list supported by API server, + // validation will fail for this object. + admissionReviewVersions?: string[]; + + // TimeoutSeconds specifies the timeout for this webhook. After the timeout passes, the webhook call will be ignored + // or the API call will fail depending on the failure policy. + timeoutSeconds?: number; + + // FailurePolicy specifies how unrecognized errors from the webhook are handled - allowed values are Ignore or Fail. + // Defaults to Fail. + failurePolicy?: string; + + // matchPolicy defines how the "rules" list is used to match incoming requests. Allowed values are "Exact" or "Equivalent". + // - Exact: match a request only if it exactly matches a specified rule. + // - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version. + // Defaults to "Equivalent". + matchPolicy?: string; + + // NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object + // matches the selector. If the object itself is a namespace, the matching is performed on object.metadata.labels. + // If both the object and the webhook configuration specify namespaceSelector, they must match. + namespaceSelector?: LabelSelector; + + // ObjectSelector decides whether to run the webhook based on if the object has matching labels. + // objectSelector and namespaceSelector are ANDed. An empty objectSelector matches all objects. + // A null objectSelector matches no objects. + objectSelector?: LabelSelector; + + // SideEffects states whether this webhookk should run when no mutating or validating webhook + // needs to run. This should be false when the webhook only applies to resources that have + // the sideEffects field set to None. Defaults to true. + sideEffects?: string; + + // reinvocationPolicy indicates whether this webhook should be called multiple times as part of a + // single admission evaluation. Allowed values are "Never" and "IfNeeded" + reinvocationPolicy?: "Never" | "IfNeeded"; +} + +interface ServiceReference { + // `namespace` is the namespace of the service. + namespace: string; + + // `name` is the name of the service. + name: string; + + // `path` is an optional URL path which will be sent in any request to this service. + path?: string; + + // `port` is an optional service port which will be used when accessing the service. + port?: number | string; +} + +interface MutatingWebhookConfigurationData extends KubeJsonApiData, void, void> { + webhooks?: MutatingWebhook[]; +} + +export class MutatingWebhookConfiguration extends KubeObject< + NamespaceScopedMetadata, + void, + void +> { + static kind = "MutatingWebhookConfiguration"; + static namespaced = false; + static apiBase = "/apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations"; + + webhooks?: MutatingWebhook[]; + + constructor({ webhooks, ...rest }: MutatingWebhookConfigurationData) { + super(rest); + this.webhooks = webhooks; + } + + getWebhooks(): MutatingWebhook[] { + return this.webhooks ?? []; + } + + getClientConfig(serviceName: string, serviceNamespace: string): WebhookClientConfig | undefined { + const webhooks = this.getWebhooks(); + + for (const webhook of webhooks) { + if (webhook.clientConfig.service?.name === serviceName && webhook.clientConfig.service?.namespace === serviceNamespace) { + return webhook.clientConfig; + } + } + + return undefined; + } +} + +export class MutatingWebhookConfigurationApi extends KubeApi { + constructor(deps: KubeApiDependencies, opts?: DerivedKubeApiOptions) { + super(deps, { + ...opts ?? {}, + objectConstructor: MutatingWebhookConfiguration, + }); + } +} diff --git a/packages/core/src/common/rbac.ts b/packages/core/src/common/rbac.ts index 03b4fd1de9..b1a7ceb550 100644 --- a/packages/core/src/common/rbac.ts +++ b/packages/core/src/common/rbac.ts @@ -9,7 +9,7 @@ export type KubeResource = "pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "replicationcontrollers" | "jobs" | "cronjobs" | "endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "verticalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" | "priorityclasses" | "runtimeclasses" | - "roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts"; + "roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts" | "mutatingwebhookconfigurations"; export interface KubeApiResource { kind: string; @@ -116,6 +116,11 @@ export const apiResourceRecord: Record = { group: "", namespaced: true, }, + mutatingwebhookconfigurations: { + kind: "MutatingWebhookConfiguration", + group: "admissionregistration.k8s.io/v1", + namespaced: true, + }, networkpolicies: { kind: "NetworkPolicy", group: "networking.k8s.io", diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/index.ts b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/index.ts new file mode 100644 index 0000000000..e80e35d4b5 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/index.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 * from "./mutating-webhook-configurations"; +export * from "./mutating-webhook-configurations-details"; diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configs-details.module.css b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configs-details.module.css new file mode 100644 index 0000000000..f4090ece62 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configs-details.module.css @@ -0,0 +1,9 @@ +.matchLabels { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; +} + +.lastItem + .firstItem { + margin-top: 1rem; +} diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configuration-store.injectable.ts b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configuration-store.injectable.ts new file mode 100644 index 0000000000..6522360bd0 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configuration-store.injectable.ts @@ -0,0 +1,26 @@ +/** + * 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 { kubeObjectStoreInjectionToken } from "../../../common/k8s-api/api-manager/kube-object-store-token"; +import { MutatingWebhookConfigurationStore } from "./mutating-webhook-configuration-store"; +import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import mutatingWebhookConfigurationApiInjectable + from "../../../common/k8s-api/endpoints/mutating-webhook-configuration-api.injectable"; + +const mutatingWebhookConfigurationStoreInjectable = getInjectable({ + id: "mutating-webhook-configuration-store", + instantiate: (di) => { + const api = di.inject(mutatingWebhookConfigurationApiInjectable); + + return new MutatingWebhookConfigurationStore({ + context: di.inject(clusterFrameContextForNamespacedResourcesInjectable), + logger: di.inject(loggerInjectable), + }, api); + }, + injectionToken: kubeObjectStoreInjectionToken, +}); + +export default mutatingWebhookConfigurationStoreInjectable; diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configuration-store.ts b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configuration-store.ts new file mode 100644 index 0000000000..fbd30b10e3 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configuration-store.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 type { + MutatingWebhookConfiguration, + MutatingWebhookConfigurationApi, +} from "../../../common/k8s-api/endpoints"; +import type { + KubeObjectStoreDependencies, + KubeObjectStoreOptions, +} from "../../../common/k8s-api/kube-object.store"; +import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store"; + +export interface MutatingWebhookConfigurationStoreDependencies extends KubeObjectStoreDependencies { +} + +export class MutatingWebhookConfigurationStore extends KubeObjectStore { + constructor(protected readonly dependencies: MutatingWebhookConfigurationStoreDependencies, api: MutatingWebhookConfigurationApi, opts?: KubeObjectStoreOptions) { + super(dependencies, api, opts); + } +} diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-details.tsx b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-details.tsx new file mode 100644 index 0000000000..d40a00ad81 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-details.tsx @@ -0,0 +1,182 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import styles from "./mutating-webhook-configs-details.module.css"; + +import React from "react"; +import { observer } from "mobx-react"; +import { DrawerItem, DrawerTitle } from "../drawer"; +import type { KubeObjectDetailsProps } from "../kube-object-details"; +import type { MutatingWebhookConfiguration } from "../../../common/k8s-api/endpoints"; +import { Badge } from "../badge"; + +export interface MutatingWebhookDetailsProps extends KubeObjectDetailsProps { +} + +@observer +export class MutatingWebhookDetails extends React.Component { + render() { + const { object: webhookConfig } = this.props; + + return ( +
+ + {webhookConfig.apiVersion} + + Webhooks + {webhookConfig.getWebhooks()?.length == 0 && ( +
No webhooks set
+ )} + {webhookConfig.getWebhooks()?.map((webhook) => ( + + + {webhook.name} + + + {webhook.clientConfig?.service?.name && ( +
+
+ Name: + {" "} + {webhook.clientConfig.service.name} +
+
+ Namespace: + {" "} + {webhook.clientConfig.service.namespace} +
+
+ )} +
+ + {webhook.matchPolicy} + + + {webhook.failurePolicy} + + + {webhook.admissionReviewVersions?.join(", ")} + + + {webhook.reinvocationPolicy} + + + {webhook.sideEffects} + + + {webhook.timeoutSeconds} + + + {webhook.namespaceSelector && ( +
+
Match Expressions:
+ {webhook.namespaceSelector.matchExpressions?.map((expression, index) => ( +
+
+ Key: + {" "} + {expression.key} +
+
+ Operator: + {" "} + {expression.operator} +
+
+ Values: + {" "} + {expression.values?.join(", ")} +
+
+ ))} + {webhook.namespaceSelector.matchLabels && ( +
+
Match Labels:
+
+ {Object.entries(webhook.namespaceSelector.matchLabels).map(([key, value], index) => ( + + ))} +
+
+ )} +
+ )} +
+ + {webhook.objectSelector && ( +
+
Match Expressions:
+ {webhook.objectSelector.matchExpressions?.map((expression, index) => ( +
+
+ Key: + {" "} + {expression.key} +
+
+ Operator: + {" "} + {expression.operator} +
+
+ Values: + {" "} + {expression.values?.join(", ")} +
+
+ ))} + {webhook.objectSelector.matchLabels && ( +
+
Match Labels:
+
+ {Object.entries(webhook.objectSelector.matchLabels).map(([key, value], index) => ( + + ))} +
+
+ )} +
+ )} +
+ + {webhook.rules?.map((rule, index) => ( +
+
+ API Groups: + {" "} + {rule.apiGroups.join(", ")} +
+
+ API Versions: + {" "} + {rule.apiVersions?.join(", ")} +
+
+ Operations: + {" "} + {rule.operations.join(", ")} +
+ {rule.resources && ( +
+ Resources: + {" "} + {rule.resources.join(", ")} +
+ )} + {rule.scope && ( +
+ Scope: + {" "} + {rule.scope} +
+ )} +
+ ))} +
+
+ ))} +
+ ); + } +} diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-route-component.injectable.ts b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-route-component.injectable.ts new file mode 100644 index 0000000000..9f16b45cc6 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-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 mutatingWebhookConfigurationsRouteInjectable + from "../../../common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/mutating-webhook-configurations-route.injectable"; +import { MutatingWebhookConfigurations } from "./mutating-webhook-configurations"; + +const mutatingWebhookConfigurationsRouteComponentInjectable = getInjectable({ + id: "mutating-webhook-configuration-route-component", + + instantiate: (di) => ({ + route: di.inject(mutatingWebhookConfigurationsRouteInjectable), + Component: MutatingWebhookConfigurations, + }), + + injectionToken: routeSpecificComponentInjectionToken, +}); + +export default mutatingWebhookConfigurationsRouteComponentInjectable; diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-sidebar-items.injectable.ts b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-sidebar-items.injectable.ts new file mode 100644 index 0000000000..5104c10c92 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations-sidebar-items.injectable.ts @@ -0,0 +1,39 @@ +/** + * 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 { sidebarItemsInjectionToken } from "../layout/sidebar-items.injectable"; +import routeIsActiveInjectable from "../../routes/route-is-active.injectable"; +import mutatingWebhookConfigurationsRouteInjectable + from "../../../common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/mutating-webhook-configurations-route.injectable"; +import navigateToMutatingWebhookConfigurationsInjectable + from "../../../common/front-end-routing/routes/cluster/config/mutating-webhook-configurations/navigate-to-mutating-webhook-configurations.injectable"; +import { configSidebarItemId } from "../+config/config-sidebar-items.injectable"; + +const mutatingWebhookConfigurationsSidebarItemsInjectable = getInjectable({ + id: "mutating-webhook-configurations-sidebar-items", + + instantiate: (di) => { + const route = di.inject(mutatingWebhookConfigurationsRouteInjectable); + const navigateToPage = di.inject(navigateToMutatingWebhookConfigurationsInjectable); + const routeIsActive = di.inject(routeIsActiveInjectable, route); + + return computed(() => [ + { + id: "mutating-webhook-configurations", + parentId: configSidebarItemId, + title: "Mutating Webhook Configs", + onClick: navigateToPage, + isActive: routeIsActive, + isVisible: route.isEnabled, + orderNumber: 100, + }, + ]); + }, + + injectionToken: sidebarItemsInjectionToken, +}); + +export default mutatingWebhookConfigurationsSidebarItemsInjectable; diff --git a/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations.tsx b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations.tsx new file mode 100644 index 0000000000..5d3a4b51a3 --- /dev/null +++ b/packages/core/src/renderer/components/+config-mutating-webhook-configurations/mutating-webhook-configurations.tsx @@ -0,0 +1,74 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +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 type { MutatingWebhookConfigurationStore } from "./mutating-webhook-configuration-store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import mutatingWebhookConfigurationsStoreInjectable from "./mutating-webhook-configuration-store.injectable"; +import { KubeObjectAge } from "../kube-object/age"; + +enum columnId { + name = "name", + webhooks = "webhooks", + age = "age", +} + +interface Dependencies { + store: MutatingWebhookConfigurationStore; +} + +const NonInjectedMutatingWebhookConfigurations = observer((props: Dependencies) => { + return ( + + ({ + ...rest, + searchProps: { + ...searchProps, + placeholder: "Search...", + }, + })} + tableId="config_mutating_webhook_configurations" + className={"MutatingWebhookConfigurations"} + store={props.store} + sortingCallbacks={{ + [columnId.name]: item => item.getName(), + [columnId.webhooks]: item => item.getWebhooks().length, + [columnId.age]: item => -item.getCreationTimestamp(), + }} + searchFilters={[ + item => item.getSearchFields(), + item => item.getLabels(), + ]} + renderHeaderTitle="Mutating Webhook Configs" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { + title: "Webhooks", + sortBy: columnId.webhooks, + id: columnId.webhooks, + }, + { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, + ]} + renderTableContents={item => [ + item.getName(), + item.getWebhooks().length, + , + ]} + /> + + ); +}); + +export const MutatingWebhookConfigurations = withInjectables(NonInjectedMutatingWebhookConfigurations, { + getProps: (di, props) => ({ + ...props, + store: di.inject(mutatingWebhookConfigurationsStoreInjectable), + }), +}); diff --git a/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/mutating-webhook-configurations-details-item.injectable.ts b/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/mutating-webhook-configurations-details-item.injectable.ts new file mode 100644 index 0000000000..94b5fdac42 --- /dev/null +++ b/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/mutating-webhook-configurations-details-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 { kubeObjectMatchesToKindAndApiVersion } from "../kube-object-matches-to-kind-and-api-version"; +import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable"; +import { MutatingWebhookDetails } from "../../../+config-mutating-webhook-configurations"; + +const mutatingWebhookConfigurationDetailItemInjectable = getInjectable({ + id: "mutating-webhook-configuration-detail-item", + + instantiate(di) { + const kubeObject = di.inject(currentKubeObjectInDetailsInjectable); + + return { + Component: MutatingWebhookDetails, + enabled: computed(() => isMutatingWebhookConfiguration(kubeObject.value.get()?.object)), + orderNumber: 10, + }; + }, + + injectionToken: kubeObjectDetailItemInjectionToken, +}); + +export const isMutatingWebhookConfiguration = kubeObjectMatchesToKindAndApiVersion( + "MutatingWebhookConfiguration", + ["v1", "admissionregistration.k8s.io/v1beta1", "admissionregistration.k8s.io/v1"], +); + +export default mutatingWebhookConfigurationDetailItemInjectable; diff --git a/packages/core/src/renderer/utils/rbac.ts b/packages/core/src/renderer/utils/rbac.ts index b4aa9a6a81..488a8479bf 100644 --- a/packages/core/src/renderer/utils/rbac.ts +++ b/packages/core/src/renderer/utils/rbac.ts @@ -44,6 +44,7 @@ export const ResourceNames: Record = { "clusterroles": "Cluster Roles", "serviceaccounts": "Service Accounts", "verticalpodautoscalers": "Vertical Pod Autoscalers", + "mutatingwebhookconfigurations": "Mutating Webhook Configurations", }; export const ResourceKindMap = object.fromEntries(