UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

169 lines (168 loc) 10.2 kB
/* * Copyright 2025 The Kubernetes Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { useMemo } from 'react'; import BackendTLSPolicy from '../../../../lib/k8s/backendTLSPolicy'; import BackendTrafficPolicy from '../../../../lib/k8s/backendTrafficPolicy'; import ConfigMap from '../../../../lib/k8s/configMap'; import CustomResourceDefinition from '../../../../lib/k8s/crd'; import CronJob from '../../../../lib/k8s/cronJob'; import DaemonSet from '../../../../lib/k8s/daemonSet'; import Deployment from '../../../../lib/k8s/deployment'; import Endpoints from '../../../../lib/k8s/endpoints'; import EndpointSlice from '../../../../lib/k8s/endpointSlices'; import Gateway from '../../../../lib/k8s/gateway'; import GatewayClass from '../../../../lib/k8s/gatewayClass'; import HPA from '../../../../lib/k8s/hpa'; import HTTPRoute from '../../../../lib/k8s/httpRoute'; import Ingress from '../../../../lib/k8s/ingress'; import Job from '../../../../lib/k8s/job'; import JobSet from '../../../../lib/k8s/jobSet'; import MutatingWebhookConfiguration from '../../../../lib/k8s/mutatingWebhookConfiguration'; import NetworkPolicy from '../../../../lib/k8s/networkpolicy'; import PersistentVolumeClaim from '../../../../lib/k8s/persistentVolumeClaim'; import Pod from '../../../../lib/k8s/pod'; import ReplicaSet from '../../../../lib/k8s/replicaSet'; import Role from '../../../../lib/k8s/role'; import RoleBinding from '../../../../lib/k8s/roleBinding'; import Secret from '../../../../lib/k8s/secret'; import Service from '../../../../lib/k8s/service'; import ServiceAccount from '../../../../lib/k8s/serviceAccount'; import StatefulSet from '../../../../lib/k8s/statefulSet'; import ValidatingWebhookConfiguration from '../../../../lib/k8s/validatingWebhookConfiguration'; import { useNamespaces } from '../../../../redux/filterSlice'; import { makeKubeSourceId } from './graphDefinitionUtils'; /** * Check if the given item has matching labels */ export const matchesLabels = (matchLabels, item) => { return (matchLabels && item.metadata.labels && Object.entries(matchLabels).every(([key, value]) => item.metadata?.labels?.[key] === value)); }; const makeRelation = (from, to, selector) => ({ fromSource: makeKubeSourceId(from), toSource: makeKubeSourceId(to), predicate(fromNode, toNode) { const fromObject = fromNode.kubeObject; const toObject = toNode.kubeObject; const hasSameNamespace = !fromObject.metadata?.namespace || !toObject.metadata?.namespace ? true : fromObject.metadata.namespace === toObject.metadata.namespace; return (fromObject.cluster === toObject.cluster && hasSameNamespace && Boolean(selector(fromObject, toObject))); }, }); const makeOwnerRelation = (cl) => ({ fromSource: makeKubeSourceId(cl), predicate(from, to) { const obj = from.kubeObject; return (obj.metadata.ownerReferences?.find(owner => owner.uid === to.kubeObject?.metadata?.uid) !== undefined); }, }); const makeOwnerRelationReversed = (cl) => ({ fromSource: makeKubeSourceId(cl), predicate(from, to) { const obj = to.kubeObject; return (obj.metadata.ownerReferences?.find(owner => owner.uid === from.kubeObject?.metadata?.uid) !== undefined); }, }); const configMapUsedInPods = makeRelation(Pod, ConfigMap, (pod, configMap) => pod.spec.volumes?.find(volume => volume.configMap?.name === configMap.metadata.name)); const configMapUsedInJobs = makeRelation(Job, ConfigMap, (job, configMap) => job.spec.template.spec.volumes?.find(volume => volume?.configMap?.name === configMap.metadata.name)); const secretsUsedInPods = makeRelation(Pod, Secret, (pod, secret) => pod.spec.containers?.find(container => container.env?.find(env => secret.metadata.name === env.valueFrom?.secretKeyRef?.name)) ?? pod.spec.volumes?.find(volume => volume.projected?.sources?.find((source) => source.secret?.name === secret.metadata.name))); const secretsUsedInJobs = makeRelation(Job, Secret, (job, secret) => job.spec.template.spec.containers?.find(container => container.env?.find(env => secret.metadata.name === env.valueFrom?.secretKeyRef?.name))); const hpaToDeployment = makeRelation(HPA, Deployment, (hpa, deployment) => hpa.spec.scaleTargetRef?.apiVersion === Deployment.apiVersion && hpa.spec.scaleTargetRef?.kind === Deployment.kind && hpa.spec.scaleTargetRef?.name === deployment.metadata.name); const hpaToStatefulSet = makeRelation(HPA, StatefulSet, (hpa, statefulSet) => hpa.spec.scaleTargetRef?.apiVersion === StatefulSet.apiVersion && hpa.spec.scaleTargetRef?.kind === StatefulSet.kind && hpa.spec.scaleTargetRef?.name === statefulSet.metadata.name); const vwcToService = makeRelation(ValidatingWebhookConfiguration, Service, (vwc, service) => vwc.webhooks.find(webhook => service.metadata.name === webhook.clientConfig.service?.name)); const mwcToService = makeRelation(MutatingWebhookConfiguration, Service, (mwc, service) => mwc.webhooks.find(webhook => service.metadata.name === webhook.clientConfig.service?.name)); const serviceToPods = makeRelation(Service, Pod, (service, pod) => matchesLabels(service.spec.selector, pod)); const endpointsToServices = makeRelation(Endpoints, Service, (endpoint, service) => endpoint.getName() === service.getName()); const endpointSlicesToServices = makeRelation(EndpointSlice, Service, (endpoint, service) => endpoint.getOwnerServiceName() === service.getName()); const ingressToService = makeRelation(Ingress, Service, (ingress, service) => ingress.spec?.rules?.find((rule) => rule.http?.paths?.find((path) => service.metadata.name === path?.backend?.service?.name))); const ingressToSecret = makeRelation(Ingress, Secret, (ingress, secret) => ingress.spec.tls?.find(tls => tls.secretName === secret.metadata.name)); const networkPolicyToPod = makeRelation(NetworkPolicy, Pod, (np, pod) => matchesLabels(np.jsonData.spec.podSelector.matchLabels, pod)); const roleBindingsToRole = makeRelation(RoleBinding, Role, (binding, role) => role.metadata.name === binding.roleRef.name); const roleBindingToServiceAccount = makeRelation(RoleBinding, ServiceAccount, (binding, sa) => binding.subjects.find(subject => subject.kind === 'ServiceAccount' && sa.metadata.name === subject.name)); const serviceAccountToDeployments = makeRelation(ServiceAccount, Deployment, (sa, deployment) => (deployment.spec?.template?.spec?.serviceAccountName ?? 'default') === sa.metadata.name && deployment.metadata.namespace === sa.metadata.namespace); const serviceAccountToDaemonSets = makeRelation(ServiceAccount, DaemonSet, (sa, ds) => (ds.spec?.template?.spec?.serviceAccountName ?? 'default') === sa.metadata.name && ds.metadata.namespace === sa.metadata.namespace); const pvcToPods = makeRelation(PersistentVolumeClaim, Pod, (pvc, pod) => pod.spec.volumes?.find(volume => volume.persistentVolumeClaim?.claimName === pvc.metadata.name)); const podToOwner = makeOwnerRelation(Pod); const repliaceSetToOwner = makeOwnerRelation(ReplicaSet); const useGetCRToOwnerRelations = () => { const namespace = useNamespaces(); const { items: crds } = CustomResourceDefinition.useList({ namespace }); return useMemo(() => { if (!crds) return []; return crds.map(crd => { const CRClass = crd.makeCRClass(); // or makeCRClass(crd) return makeOwnerRelationReversed(CRClass); }); }, [crds]); }; const jobToCronJob = makeRelation(Job, CronJob, (job, cronJob) => job.metadata.ownerReferences?.find(owner => owner.uid === cronJob.metadata.uid)); const jobToJobSet = makeRelation(Job, JobSet, (job, jobSet) => job.metadata.ownerReferences?.find(owner => owner.uid === jobSet.metadata.uid)); const gatewayToGatewayClass = makeRelation(Gateway, GatewayClass, (gateway, gatewayClass) => gateway.spec.gatewayClassName === gatewayClass.metadata.name); const httpRouteToGateway = makeRelation(HTTPRoute, Gateway, (httpRoute, gateway) => httpRoute.spec.parentRefs?.find(ref => ref.name === gateway.metadata.name)); const httpRouteToService = makeRelation(HTTPRoute, Service, (httpRoute, service) => httpRoute.spec.rules?.find(rule => rule.backendRefs?.find(backend => backend.name === service.metadata.name))); const backendTLSPolicyToService = makeRelation(BackendTLSPolicy, Service, (tlsPolicy, service) => tlsPolicy.spec.targetRef?.name === service.metadata.name && tlsPolicy.spec.targetRef?.kind === 'Service'); const backendTrafficPolicyToService = makeRelation(BackendTrafficPolicy, Service, (trafficPolicy, service) => trafficPolicy.spec.targetRef?.name === service.metadata.name && trafficPolicy.spec.targetRef?.kind === 'Service'); const staticRelations = [ configMapUsedInPods, configMapUsedInJobs, secretsUsedInPods, secretsUsedInJobs, hpaToDeployment, hpaToStatefulSet, vwcToService, mwcToService, serviceToPods, endpointsToServices, endpointSlicesToServices, ingressToService, ingressToSecret, networkPolicyToPod, roleBindingsToRole, roleBindingToServiceAccount, serviceAccountToDeployments, serviceAccountToDaemonSets, pvcToPods, podToOwner, repliaceSetToOwner, jobToCronJob, jobToJobSet, gatewayToGatewayClass, httpRouteToGateway, httpRouteToService, backendTLSPolicyToService, backendTrafficPolicyToService, ]; export function useGetAllRelations() { const crdRelations = useGetCRToOwnerRelations(); // eslint-disable-next-line react-hooks/exhaustive-deps return useMemo(() => [...staticRelations, ...crdRelations], [crdRelations, staticRelations]); }