diff --git a/frontend/src/components/common/Resource/ResourceTable.tsx b/frontend/src/components/common/Resource/ResourceTable.tsx index 3ac3d4a01f..684d5d7418 100644 --- a/frontend/src/components/common/Resource/ResourceTable.tsx +++ b/frontend/src/components/common/Resource/ResourceTable.tsx @@ -10,6 +10,7 @@ import { KubeObject } from '../../../lib/k8s/KubeObject'; import { KubeObjectClass } from '../../../lib/k8s/KubeObject'; import { useFilterFunc } from '../../../lib/util'; import { DefaultHeaderAction, RowAction } from '../../../redux/actionButtonsSlice'; +import { useNamespaces } from '../../../redux/filterSlice'; import { HeadlampEventType, useEventCallback } from '../../../redux/headlampEventSlice'; import { useTypedSelector } from '../../../redux/reducers/reducers'; import { useSettings } from '../../App/Settings/hook'; @@ -141,7 +142,7 @@ function TableFromResourceClass( props: ResourceTableFromResourceClassProps ) { const { resourceClass, id, ...otherProps } = props; - const { items, error, clusterErrors } = resourceClass.useList(); + const { items, error, clusterErrors } = resourceClass.useList({ namespace: useNamespaces() }); // throttle the update of the table to once per second const throttledItems = useThrottle(items, 1000); diff --git a/frontend/src/components/crd/CustomResourceInstancesList.tsx b/frontend/src/components/crd/CustomResourceInstancesList.tsx index c0f1186b19..2a7d3ba216 100644 --- a/frontend/src/components/crd/CustomResourceInstancesList.tsx +++ b/frontend/src/components/crd/CustomResourceInstancesList.tsx @@ -3,6 +3,7 @@ import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import CRD from '../../lib/k8s/crd'; import { KubeObject } from '../../lib/k8s/KubeObject'; +import { useNamespaces } from '../../redux/filterSlice'; import { Link, Loader, SectionBox, ShowHideLabel } from '../common/'; import Empty from '../common/EmptyContent'; import { ResourceListView } from '../common/Resource'; @@ -12,7 +13,7 @@ function CrInstancesView({ crds }: { crds: CRD[]; key: string }) { const dataClassCrds = crds.map(crd => { const crdClass = crd.makeCRClass(); - const data = crdClass.useList({ cluster: crd.cluster }); + const data = crdClass.useList({ cluster: crd.cluster, namespace: useNamespaces() }); return { data, crdClass, crd }; }); @@ -136,7 +137,11 @@ function CrInstancesView({ crds }: { crds: CRD[]; key: string }) { export function CrInstanceList() { const { t } = useTranslation(['glossary', 'translation']); - const { items: crds, error: crdsError, isLoading: isLoadingCRDs } = CRD.useList(); + const { + items: crds, + error: crdsError, + isLoading: isLoadingCRDs, + } = CRD.useList({ namespace: useNamespaces() }); if (crdsError) { return ( diff --git a/frontend/src/components/crd/List.tsx b/frontend/src/components/crd/List.tsx index 150e650634..b1adecac70 100644 --- a/frontend/src/components/crd/List.tsx +++ b/frontend/src/components/crd/List.tsx @@ -1,12 +1,13 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import CRD from '../../lib/k8s/crd'; +import { useNamespaces } from '../../redux/filterSlice'; import { Link, useThrottle } from '../common'; import ResourceListView from '../common/Resource/ResourceListView'; export default function CustomResourceDefinitionList() { const { t } = useTranslation(['glossary', 'frequent']); - const [items, error] = CRD.useList(); + const [items, error] = CRD.useList({ namespace: useNamespaces() }); const throttledItems = useThrottle(items, 1000); const categories = React.useMemo(() => { diff --git a/frontend/src/components/job/List.tsx b/frontend/src/components/job/List.tsx index 51d3af2bd9..1b44d07fae 100644 --- a/frontend/src/components/job/List.tsx +++ b/frontend/src/components/job/List.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { KubeContainer } from '../../lib/k8s/cluster'; import Job from '../../lib/k8s/job'; import { formatDuration } from '../../lib/util'; +import { useNamespaces } from '../../redux/filterSlice'; import { LightTooltip, SimpleTableProps, StatusLabel, StatusLabelProps } from '../common'; import ResourceListView from '../common/Resource/ResourceListView'; @@ -53,7 +54,7 @@ export function makeJobStatusLabel(job: Job) { } export default function JobsList() { - const [jobs, error] = Job.useList(); + const [jobs, error] = Job.useList({ namespace: useNamespaces() }); return ; } diff --git a/frontend/src/components/limitRange/List.tsx b/frontend/src/components/limitRange/List.tsx index 936eda7266..9745a27d25 100644 --- a/frontend/src/components/limitRange/List.tsx +++ b/frontend/src/components/limitRange/List.tsx @@ -1,6 +1,7 @@ import { useTranslation } from 'react-i18next'; import { ApiError } from '../../lib/k8s/apiProxy'; import { LimitRange } from '../../lib/k8s/limitRange'; +import { useNamespaces } from '../../redux/filterSlice'; import { SimpleTableProps } from '../common'; import ResourceListView from '../common/Resource/ResourceListView'; @@ -39,7 +40,7 @@ export function LimitRangeRenderer(props: LimitRangeProps) { } export function LimitRangeList() { - const [limitRanges, error] = LimitRange.useList(); + const [limitRanges, error] = LimitRange.useList({ namespace: useNamespaces() }); return ; } diff --git a/frontend/src/components/pod/List.tsx b/frontend/src/components/pod/List.tsx index a067e19f1f..f4887f3b8a 100644 --- a/frontend/src/components/pod/List.tsx +++ b/frontend/src/components/pod/List.tsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'; import { ApiError } from '../../lib/k8s/apiProxy'; import Pod from '../../lib/k8s/pod'; import { timeAgo } from '../../lib/util'; +import { useNamespaces } from '../../redux/filterSlice'; import { HeadlampEventType, useEventCallback } from '../../redux/headlampEventSlice'; import { LightTooltip, Link, SimpleTableProps } from '../common'; import { StatusLabel, StatusLabelProps } from '../common/Label'; @@ -216,7 +217,7 @@ export function PodListRenderer(props: PodListProps) { } export default function PodList() { - const { items, error, clusterErrors } = Pod.useList(); + const { items, error, clusterErrors } = Pod.useList({ namespace: useNamespaces() }); const dispatchHeadlampEvent = useEventCallback(HeadlampEventType.LIST_VIEW); diff --git a/frontend/src/components/resourceMap/sources/definitions/configurationSource.tsx b/frontend/src/components/resourceMap/sources/definitions/configurationSource.tsx index f58ccebcca..a6009238f3 100644 --- a/frontend/src/components/resourceMap/sources/definitions/configurationSource.tsx +++ b/frontend/src/components/resourceMap/sources/definitions/configurationSource.tsx @@ -7,6 +7,7 @@ import Pod from '../../../../lib/k8s/pod'; import Secret from '../../../../lib/k8s/secret'; import Service from '../../../../lib/k8s/service'; import ValidatingWebhookConfiguration from '../../../../lib/k8s/validatingWebhookConfiguration'; +import { useNamespaces } from '../../../../redux/filterSlice'; import { GraphEdge, GraphSource } from '../../graph/graphModel'; import { getKindGroupColor, KubeIcon } from '../../kubeIcon/KubeIcon'; import { makeKubeObjectNode, makeKubeToKubeEdge } from '../GraphSources'; @@ -17,9 +18,8 @@ const secretsSource: GraphSource = { icon: , isEnabledByDefault: false, useData() { - const [secrets] = Secret.useList(); - - const [pods] = Pod.useList(); + const [secrets] = Secret.useList({ namespace: useNamespaces() }); + const [pods] = Pod.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!secrets || !pods) return null; @@ -76,9 +76,9 @@ const configMapsSource: GraphSource = { isEnabledByDefault: false, icon: , useData() { - const [configMaps] = ConfigMap.useList(); - const [pods] = Pod.useList(); - const [jobs] = Job.useList(); + const [configMaps] = ConfigMap.useList({ namespace: useNamespaces() }); + const [pods] = Pod.useList({ namespace: useNamespaces() }); + const [jobs] = Job.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!configMaps || !pods || !jobs) return null; @@ -123,8 +123,8 @@ const validatingWebhookConfigurationSource: GraphSource = { icon: , isEnabledByDefault: false, useData() { - const [vwc] = ValidatingWebhookConfiguration.useList(); - const [services] = Service.useList(); + const [vwc] = ValidatingWebhookConfiguration.useList({ namespace: useNamespaces() }); + const [services] = Service.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!vwc || !services) return null; @@ -155,8 +155,8 @@ const mutatingWebhookConfigurationSource: GraphSource = { icon: , isEnabledByDefault: false, useData() { - const [mwc] = MutatingWebhookConfiguration.useList(); - const [services] = Service.useList(); + const [mwc] = MutatingWebhookConfiguration.useList({ namespace: useNamespaces() }); + const [services] = Service.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!mwc || !services) return null; diff --git a/frontend/src/components/resourceMap/sources/definitions/networkSource.tsx b/frontend/src/components/resourceMap/sources/definitions/networkSource.tsx index eb00ff123e..ece0063e42 100644 --- a/frontend/src/components/resourceMap/sources/definitions/networkSource.tsx +++ b/frontend/src/components/resourceMap/sources/definitions/networkSource.tsx @@ -7,6 +7,7 @@ import NetworkPolicy from '../../../../lib/k8s/networkpolicy'; import Pod from '../../../../lib/k8s/pod'; import Secret from '../../../../lib/k8s/secret'; import Service from '../../../../lib/k8s/service'; +import { useNamespaces } from '../../../../redux/filterSlice'; import { GraphEdge, GraphSource } from '../../graph/graphModel'; import { getKindGroupColor, KubeIcon } from '../../kubeIcon/KubeIcon'; import { makeKubeObjectNode, makeKubeToKubeEdge } from '../GraphSources'; @@ -17,8 +18,8 @@ const serviceSource: GraphSource = { label: 'Services', icon: , useData() { - const [services] = Service.useList(); - const [pods] = Pod.useList(); + const [services] = Service.useList({ namespace: useNamespaces() }); + const [pods] = Pod.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!services || !pods) return null; @@ -46,8 +47,8 @@ const endpointsSource: GraphSource = { label: 'Endpoints', icon: , useData() { - const [endpoints] = Endpoints.useList(); - const [services] = Service.useList(); + const [endpoints] = Endpoints.useList({ namespace: useNamespaces() }); + const [services] = Service.useList({ namespace: useNamespaces() }); return useMemo(() => { const nodes = endpoints?.map(makeKubeObjectNode) ?? []; @@ -71,9 +72,9 @@ const ingressListSource: GraphSource = { label: 'Ingress', icon: , useData() { - const [ingresses] = Ingress.useList(); - const [services] = Service.useList(); - const [secrets] = Secret.useList(); + const [ingresses] = Ingress.useList({ namespace: useNamespaces() }); + const [services] = Service.useList({ namespace: useNamespaces() }); + const [secrets] = Secret.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!ingresses || !services || !secrets) return null; @@ -115,8 +116,8 @@ const networkPoliciesSource: GraphSource = { label: 'Network Policies', icon: , useData() { - const [networkPolicies] = NetworkPolicy.useList(); - const [pods] = Pod.useList(); + const [networkPolicies] = NetworkPolicy.useList({ namespace: useNamespaces() }); + const [pods] = Pod.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!networkPolicies || !pods) return null; @@ -144,7 +145,7 @@ const ingressClassesSource: GraphSource = { label: 'Ingress Classes', icon: , useData() { - const [ingressClasses] = IngressClass.useList(); + const [ingressClasses] = IngressClass.useList({ namespace: useNamespaces() }); return useMemo(() => { return { diff --git a/frontend/src/components/resourceMap/sources/definitions/securitySource.tsx b/frontend/src/components/resourceMap/sources/definitions/securitySource.tsx index e555e329e5..55e081ec08 100644 --- a/frontend/src/components/resourceMap/sources/definitions/securitySource.tsx +++ b/frontend/src/components/resourceMap/sources/definitions/securitySource.tsx @@ -5,6 +5,7 @@ import Deployment from '../../../../lib/k8s/deployment'; import Role from '../../../../lib/k8s/role'; import RoleBinding from '../../../../lib/k8s/roleBinding'; import ServiceAccount from '../../../../lib/k8s/serviceAccount'; +import { useNamespaces } from '../../../../redux/filterSlice'; import { GraphEdge, GraphSource } from '../../graph/graphModel'; import { getKindGroupColor, KubeIcon } from '../../kubeIcon/KubeIcon'; import { makeKubeObjectNode, makeKubeToKubeEdge } from '../GraphSources'; @@ -14,7 +15,7 @@ const rolesSource: GraphSource = { label: 'Roles', icon: , useData() { - const [roles] = Role.useList(); + const [roles] = Role.useList({ namespace: useNamespaces() }); return useMemo( () => @@ -33,9 +34,9 @@ const roleBindingsSource: GraphSource = { label: 'Role Bindings', icon: , useData() { - const [roleBindings] = RoleBinding.useList(); - const [roles] = Role.useList(); - const [serviceAccounts] = ServiceAccount.useList(); + const [roleBindings] = RoleBinding.useList({ namespace: useNamespaces() }); + const [roles] = Role.useList({ namespace: useNamespaces() }); + const [serviceAccounts] = ServiceAccount.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!roleBindings || !roles || !serviceAccounts) return null; @@ -72,9 +73,9 @@ const serviceAccountsSource: GraphSource = { label: 'Service Accounts', icon: , useData() { - const [serviceAccounts] = ServiceAccount.useList(); - const [deployments] = Deployment.useList(); - const [daemonSets] = DaemonSet.useList(); + const [serviceAccounts] = ServiceAccount.useList({ namespace: useNamespaces() }); + const [deployments] = Deployment.useList({ namespace: useNamespaces() }); + const [daemonSets] = DaemonSet.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!serviceAccounts || !deployments || !daemonSets) return null; diff --git a/frontend/src/components/resourceMap/sources/definitions/storageSource.tsx b/frontend/src/components/resourceMap/sources/definitions/storageSource.tsx index af60ef0b41..3e2dafbef0 100644 --- a/frontend/src/components/resourceMap/sources/definitions/storageSource.tsx +++ b/frontend/src/components/resourceMap/sources/definitions/storageSource.tsx @@ -2,6 +2,7 @@ import { Icon } from '@iconify/react'; import { useMemo } from 'react'; import PersistentVolumeClaim from '../../../../lib/k8s/persistentVolumeClaim'; import Pod from '../../../../lib/k8s/pod'; +import { useNamespaces } from '../../../../redux/filterSlice'; import { GraphEdge, GraphSource } from '../../graph/graphModel'; import { getKindGroupColor, KubeIcon } from '../../kubeIcon/KubeIcon'; import { makeKubeObjectNode, makeKubeToKubeEdge } from '../GraphSources'; @@ -11,8 +12,8 @@ const pvcSource: GraphSource = { label: 'PVCs', icon: , useData() { - const [pvcs] = PersistentVolumeClaim.useList(); - const [pods] = Pod.useList(); + const [pvcs] = PersistentVolumeClaim.useList({ namespace: useNamespaces() }); + const [pods] = Pod.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!pvcs || !pods) return null; diff --git a/frontend/src/components/resourceMap/sources/definitions/workloadSource.tsx b/frontend/src/components/resourceMap/sources/definitions/workloadSource.tsx index 0593cbf492..b51feaca56 100644 --- a/frontend/src/components/resourceMap/sources/definitions/workloadSource.tsx +++ b/frontend/src/components/resourceMap/sources/definitions/workloadSource.tsx @@ -10,6 +10,7 @@ import Pod from '../../../../lib/k8s/pod'; import ReplicaSet from '../../../../lib/k8s/replicaSet'; import Secret from '../../../../lib/k8s/secret'; import StatefulSet from '../../../../lib/k8s/statefulSet'; +import { useNamespaces } from '../../../../redux/filterSlice'; import { GraphEdge, GraphSource } from '../../graph/graphModel'; import { getKindGroupColor, KubeIcon } from '../../kubeIcon/KubeIcon'; import { kubeOwnersEdges, makeKubeObjectNode, makeKubeToKubeEdge } from '../GraphSources'; @@ -27,8 +28,8 @@ const podsSource: GraphSource = { label: 'Pods', icon: , useData: () => { - const [pods] = Pod.useList(); - const [nodes] = Node.useList(); + const [pods] = Pod.useList({ namespace: useNamespaces() }); + const [nodes] = Node.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!pods || !nodes) return null; @@ -77,7 +78,7 @@ const deploymentsSource: GraphSource = { label: 'Deployments', icon: , useData() { - const [deployments] = Deployment.useList(); + const [deployments] = Deployment.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!deployments) return null; @@ -93,7 +94,7 @@ const cronJobSource: GraphSource = { label: 'CronJobs', icon: , useData() { - const [cronJobs] = CronJob.useList(); + const [cronJobs] = CronJob.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!cronJobs) return null; @@ -110,8 +111,8 @@ const jobsSource: GraphSource = { label: 'Jobs', icon: , useData() { - const [jobs] = Job.useList(); - const [secrets] = Secret.useList(); + const [jobs] = Job.useList({ namespace: useNamespaces() }); + const [secrets] = Secret.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!jobs || !secrets) return null; @@ -159,7 +160,7 @@ const replicaSetsSource: GraphSource = { label: 'Replica Sets', icon: , useData() { - const [replicaSets] = ReplicaSet.useList(); + const [replicaSets] = ReplicaSet.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!replicaSets) return null; @@ -183,7 +184,7 @@ const statefulSetSource: GraphSource = { label: 'Stateful Sets', icon: , useData() { - const [statefulSets] = StatefulSet.useList(); + const [statefulSets] = StatefulSet.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!statefulSets) return null; @@ -199,7 +200,7 @@ const daemonSetSource: GraphSource = { label: 'Daemon Sets', icon: , useData() { - const [daemonSets] = DaemonSet.useList(); + const [daemonSets] = DaemonSet.useList({ namespace: useNamespaces() }); return useMemo(() => { if (!daemonSets) return null; diff --git a/frontend/src/components/resourceQuota/List.tsx b/frontend/src/components/resourceQuota/List.tsx index 79dea2f195..a560695e8d 100644 --- a/frontend/src/components/resourceQuota/List.tsx +++ b/frontend/src/components/resourceQuota/List.tsx @@ -4,6 +4,7 @@ import { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { ApiError } from '../../lib/k8s/apiProxy'; import ResourceQuota from '../../lib/k8s/resourceQuota'; +import { useNamespaces } from '../../redux/filterSlice'; import { SimpleTableProps } from '../common'; import ResourceListView from '../common/Resource/ResourceListView'; @@ -85,7 +86,7 @@ export function ResourceQuotaRenderer(props: ResourceQuotaProps) { } export default function ResourceQuotaList() { - const [resourceQuotas, error] = ResourceQuota.useList(); + const [resourceQuotas, error] = ResourceQuota.useList({ namespace: useNamespaces() }); return ; } diff --git a/frontend/src/components/role/BindingList.tsx b/frontend/src/components/role/BindingList.tsx index 5c2fb4b662..81abac9437 100644 --- a/frontend/src/components/role/BindingList.tsx +++ b/frontend/src/components/role/BindingList.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'; import ClusterRoleBinding from '../../lib/k8s/clusterRoleBinding'; import RoleBinding from '../../lib/k8s/roleBinding'; import { combineClusterListErrors, getClusterGroup } from '../../lib/util'; +import { useNamespaces } from '../../redux/filterSlice'; import { Link } from '../common'; import LabelListItem from '../common/LabelListItem'; import ResourceListView from '../common/Resource/ResourceListView'; @@ -27,7 +28,9 @@ function RoleLink(props: { role: string; namespace?: string }) { export default function RoleBindingList() { const { t } = useTranslation(['glossary', 'translation']); - const { items: roles, clusterErrors: rolesErrors } = RoleBinding.useList(); + const { items: roles, clusterErrors: rolesErrors } = RoleBinding.useList({ + namespace: useNamespaces(), + }); const { items: clusterRoles, clusterErrors: clusterRolesErrors } = ClusterRoleBinding.useList(); const clusters = getClusterGroup(); diff --git a/frontend/src/components/role/List.tsx b/frontend/src/components/role/List.tsx index 1a9a8d8977..bfb9684be1 100644 --- a/frontend/src/components/role/List.tsx +++ b/frontend/src/components/role/List.tsx @@ -4,13 +4,14 @@ import { useClusterGroup } from '../../lib/k8s'; import ClusterRole from '../../lib/k8s/clusterRole'; import Role from '../../lib/k8s/role'; import { combineClusterListErrors } from '../../lib/util'; +import { useNamespaces } from '../../redux/filterSlice'; import Link from '../common/Link'; import ResourceListView from '../common/Resource/ResourceListView'; import { ColumnType } from '../common/Resource/ResourceTable'; export default function RoleList() { const { t } = useTranslation('glossary'); - const { items: roles, clusterErrors: rolesErrors } = Role.useList(); + const { items: roles, clusterErrors: rolesErrors } = Role.useList({ namespace: useNamespaces() }); const { items: clusterRoles, clusterErrors: clusterRolesErrors } = ClusterRole.useList(); const clusters = useClusterGroup(); diff --git a/frontend/src/redux/filterSlice.ts b/frontend/src/redux/filterSlice.ts index abd6b05655..d3a13d08a8 100644 --- a/frontend/src/redux/filterSlice.ts +++ b/frontend/src/redux/filterSlice.ts @@ -1,5 +1,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { JSONPath } from 'jsonpath-plus'; +import { useMemo } from 'react'; +import { useSelector } from 'react-redux'; import { KubeEvent } from '../lib/k8s/event'; import { KubeObjectInterface } from '../lib/k8s/KubeObject'; @@ -134,3 +136,13 @@ const filterSlice = createSlice({ export const { setNamespaceFilter, resetFilter } = filterSlice.actions; export default filterSlice.reducer; + +/** + * Get globally selected namespaces + * + * @returns An array of selected namespaces, empty means all namespaces are visible + */ +export const useNamespaces = () => { + const namespacesSet = useSelector(({ filter }: { filter: FilterState }) => filter.namespaces); + return useMemo(() => [...namespacesSet], [namespacesSet]); +};