fix(ingress): handle system resources [EE-4775] (#9972)

* fix(ingress): handle system resources [EE-4775]
pull/10090/head
Ali 2023-08-23 09:13:35 +12:00 committed by GitHub
parent 5586910e9d
commit 1e61f7e305
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 105 additions and 60 deletions

View File

@ -3,11 +3,7 @@ import { FileCode, Plus, Trash2 } from 'lucide-react';
import { ConfigMap } from 'kubernetes-types/core/v1';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import {
Authorized,
useAuthorizations,
useCurrentUser,
} from '@/react/hooks/useUser';
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
import { useNamespaces } from '@/react/kubernetes/namespaces/queries';
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
@ -39,7 +35,9 @@ const settingsStore = createStore(storageKey);
export function ConfigMapsDatatable() {
const tableState = useTableState(settingsStore, storageKey);
const readOnly = !useAuthorizations(['K8sConfigMapsW']);
const { isAdmin } = useCurrentUser();
const canAccessSystemResources = useAuthorizations(
'K8sAccessSystemNamespaces'
);
const environmentId = useEnvironmentId();
const { data: namespaces, ...namespacesQuery } = useNamespaces(
@ -63,10 +61,10 @@ export function ConfigMapsDatatable() {
() =>
configMaps?.filter(
(configMap) =>
(isAdmin && tableState.showSystemResources) ||
(canAccessSystemResources && tableState.showSystemResources) ||
!isSystemNamespace(configMap.metadata?.namespace ?? '')
) || [],
[configMaps, tableState, isAdmin]
[configMaps, tableState, canAccessSystemResources]
);
const configMapRowData = useConfigMapRowData(
filteredConfigMaps,
@ -95,13 +93,15 @@ export function ConfigMapsDatatable() {
<TableSettingsMenu>
<DefaultDatatableSettings
settings={tableState}
hideShowSystemResources={!isAdmin}
hideShowSystemResources={!canAccessSystemResources}
/>
</TableSettingsMenu>
)}
description={
<SystemResourceDescription
showSystemResources={tableState.showSystemResources || !isAdmin}
showSystemResources={
tableState.showSystemResources || !canAccessSystemResources
}
/>
}
/>

View File

@ -3,11 +3,7 @@ import { Lock, Plus, Trash2 } from 'lucide-react';
import { Secret } from 'kubernetes-types/core/v1';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import {
Authorized,
useAuthorizations,
useCurrentUser,
} from '@/react/hooks/useUser';
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
import { useNamespaces } from '@/react/kubernetes/namespaces/queries';
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
@ -39,7 +35,9 @@ const settingsStore = createStore(storageKey);
export function SecretsDatatable() {
const tableState = useTableState(settingsStore, storageKey);
const readOnly = !useAuthorizations(['K8sSecretsW']);
const { isAdmin } = useCurrentUser();
const canAccessSystemResources = useAuthorizations(
'K8sAccessSystemNamespaces'
);
const environmentId = useEnvironmentId();
const { data: namespaces, ...namespacesQuery } = useNamespaces(
@ -63,10 +61,10 @@ export function SecretsDatatable() {
() =>
secrets?.filter(
(secret) =>
(isAdmin && tableState.showSystemResources) ||
(canAccessSystemResources && tableState.showSystemResources) ||
!isSystemNamespace(secret.metadata?.namespace ?? '')
) || [],
[secrets, tableState, isAdmin]
[secrets, tableState, canAccessSystemResources]
);
const secretRowData = useSecretRowData(
filteredSecrets,
@ -95,13 +93,15 @@ export function SecretsDatatable() {
<TableSettingsMenu>
<DefaultDatatableSettings
settings={tableState}
hideShowSystemResources={!isAdmin}
hideShowSystemResources={!canAccessSystemResources}
/>
</TableSettingsMenu>
)}
description={
<SystemResourceDescription
showSystemResources={tableState.showSystemResources || !isAdmin}
showSystemResources={
tableState.showSystemResources || !canAccessSystemResources
}
/>
}
/>

View File

@ -1,16 +1,20 @@
import { Plus, Trash2 } from 'lucide-react';
import { useRouter } from '@uirouter/react';
import { useMemo } from 'react';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { useNamespaces } from '@/react/kubernetes/namespaces/queries';
import { useAuthorizations, Authorized } from '@/react/hooks/useUser';
import Route from '@/assets/ico/route.svg?c';
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
import { isSystemNamespace } from '@/react/kubernetes/namespaces/utils';
import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
import { confirmDelete } from '@@/modals/confirm';
import { Datatable } from '@@/datatables';
import { Datatable, TableSettingsMenu } from '@@/datatables';
import { Button } from '@@/buttons';
import { Link } from '@@/Link';
import { createPersistedStore } from '@@/datatables/types';
import { useTableState } from '@@/datatables/useTableState';
import { DeleteIngressesRequest, Ingress } from '../types';
@ -26,26 +30,42 @@ interface SelectedIngress {
}
const storageKey = 'ingressClassesNameSpace';
const settingsStore = createPersistedStore(storageKey);
const settingsStore = createStore(storageKey);
export function IngressDatatable() {
const tableState = useTableState(settingsStore, storageKey);
const environmentId = useEnvironmentId();
const canAccessSystemResources = useAuthorizations(
'K8sAccessSystemNamespaces'
);
const { data: namespaces, ...namespacesQuery } = useNamespaces(environmentId);
const ingressesQuery = useIngresses(
const { data: ingresses, ...ingressesQuery } = useIngresses(
environmentId,
Object.keys(namespaces || {})
Object.keys(namespaces || {}),
{
autoRefreshRate: tableState.autoRefreshRate * 1000,
}
);
const filteredIngresses = useMemo(
() =>
ingresses?.filter(
(ingress) =>
(canAccessSystemResources && tableState.showSystemResources) ||
!isSystemNamespace(ingress.Namespace ?? '')
) || [],
[ingresses, tableState, canAccessSystemResources]
);
const deleteIngressesMutation = useDeleteIngresses();
const tableState = useTableState(settingsStore, storageKey);
const router = useRouter();
return (
<Datatable
settingsManager={tableState}
dataset={ingressesQuery.data || []}
dataset={filteredIngresses}
columns={columns}
isLoading={ingressesQuery.isLoading || namespacesQuery.isLoading}
emptyContentLabel="No supported ingresses found"
@ -53,6 +73,21 @@ export function IngressDatatable() {
titleIcon={Route}
getRowId={(row) => row.Name + row.Type + row.Namespace}
renderTableActions={tableActions}
renderTableSettings={() => (
<TableSettingsMenu>
<DefaultDatatableSettings
settings={tableState}
hideShowSystemResources={!canAccessSystemResources}
/>
</TableSettingsMenu>
)}
description={
<SystemResourceDescription
showSystemResources={
tableState.showSystemResources || !canAccessSystemResources
}
/>
}
disableSelect={useCheckboxes()}
/>
);
@ -62,7 +97,6 @@ export function IngressDatatable() {
<div className="ingressDatatable-actions">
<Authorized authorizations="AzureContainerGroupDelete">
<Button
className="btn-wrapper"
color="dangerlight"
disabled={selectedFlatRows.length === 0}
onClick={() => handleRemoveClick(selectedFlatRows)}
@ -77,11 +111,7 @@ export function IngressDatatable() {
to="kubernetes.ingresses.create"
className="space-left no-decoration"
>
<Button
icon={Plus}
className="btn-wrapper vertical-center"
color="secondary"
>
<Button icon={Plus} color="secondary">
Add with form
</Button>
</Link>
@ -92,9 +122,7 @@ export function IngressDatatable() {
className="space-left no-decoration"
params={{ referrer: 'kubernetes.ingresses' }}
>
<Button icon={Plus} className="btn-wrapper">
Create from manifest
</Button>
<Button icon={Plus}>Create from manifest</Button>
</Link>
</Authorized>
</div>

View File

@ -3,4 +3,5 @@ import { columnHelper } from './helper';
export const className = columnHelper.accessor('ClassName', {
header: 'Class Name',
id: 'className',
cell: ({ row }) => row.original.ClassName || '-',
});

View File

@ -32,7 +32,7 @@ function Cell({ row }: CellContext<Ingress, string>) {
}
return (
<div className="flex flex-col gap-y-0.5">
<div className="flex flex-col gap-y-0.5 whitespace-nowrap">
{paths.map((path) => (
<div key={`${path.Host}${path.Path}${path.ServiceName}:${path.Port}`}>
<span className="flex flex-nowrap items-center gap-1 px-2">

View File

@ -1,8 +1,10 @@
import { CellContext } from '@tanstack/react-table';
import { Authorized } from '@/react/hooks/useUser';
import { isSystemNamespace } from '@/react/kubernetes/namespaces/utils';
import { Link } from '@@/Link';
import { Badge } from '@@/Badge';
import { Ingress } from '../../types';
@ -16,20 +18,29 @@ export const name = columnHelper.accessor('Name', {
function Cell({ row, getValue }: CellContext<Ingress, string>) {
const name = getValue();
const namespace = row.original.Namespace;
const isSystemIngress = isSystemNamespace(namespace);
return (
<Authorized authorizations="K8sIngressesW" childrenUnauthorized={name}>
<Link
to="kubernetes.ingresses.edit"
params={{
uid: row.original.UID,
namespace: row.original.Namespace,
name,
}}
title={name}
>
{name}
</Link>
</Authorized>
<div className="flex whitespace-nowrap">
<Authorized authorizations="K8sIngressesW" childrenUnauthorized={name}>
<Link
to="kubernetes.ingresses.edit"
params={{
uid: row.original.UID,
namespace,
name,
}}
title={name}
>
{name}
</Link>
</Authorized>
{isSystemIngress && (
<Badge type="success" className="ml-2">
system
</Badge>
)}
</div>
);
}

View File

@ -3,4 +3,5 @@ import { columnHelper } from './helper';
export const type = columnHelper.accessor('Type', {
header: 'Type',
id: 'type',
cell: ({ row }) => row.original.Type || '-',
});

View File

@ -55,7 +55,8 @@ export function useIngress(
export function useIngresses(
environmentId: EnvironmentId,
namespaces?: string[]
namespaces?: string[],
options?: { autoRefreshRate?: number }
) {
return useQuery(
[
@ -117,6 +118,9 @@ export function useIngresses(
{
enabled: !!namespaces?.length,
...withError('Unable to get ingresses'),
refetchInterval() {
return options?.autoRefreshRate ?? false;
},
}
);
}

View File

@ -4,11 +4,7 @@ import clsx from 'clsx';
import { Row } from '@tanstack/react-table';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import {
Authorized,
useAuthorizations,
useCurrentUser,
} from '@/react/hooks/useUser';
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
import { pluralize } from '@/portainer/helpers/strings';
@ -48,11 +44,13 @@ export function ServicesDatatable() {
);
const readOnly = !useAuthorizations(['K8sServiceW']);
const { isAdmin } = useCurrentUser();
const canAccessSystemResources = useAuthorizations(
'K8sAccessSystemNamespaces'
);
const filteredServices = services?.filter(
(service) =>
(isAdmin && tableState.showSystemResources) ||
(canAccessSystemResources && tableState.showSystemResources) ||
!isSystemNamespace(service.Namespace)
);
@ -75,13 +73,15 @@ export function ServicesDatatable() {
<TableSettingsMenu>
<DefaultDatatableSettings
settings={tableState}
hideShowSystemResources={!isAdmin}
hideShowSystemResources={!canAccessSystemResources}
/>
</TableSettingsMenu>
)}
description={
<SystemResourceDescription
showSystemResources={tableState.showSystemResources || !isAdmin}
showSystemResources={
tableState.showSystemResources || !canAccessSystemResources
}
/>
}
renderRow={servicesRenderRow}