mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
refactor(k8s): namespace core logic (#12142)
Co-authored-by: testA113 <aliharriss1995@gmail.com> Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io> Co-authored-by: James Carppe <85850129+jamescarppe@users.noreply.github.com> Co-authored-by: Ali <83188384+testA113@users.noreply.github.com>
This commit is contained in:
parent
da010f3d08
commit
ea228c3d6d
276 changed files with 9241 additions and 3361 deletions
|
@ -9,6 +9,7 @@ import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNam
|
|||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import { useCurrentEnvironment } from '@/react/hooks/useCurrentEnvironment';
|
||||
import { useAuthorizations } from '@/react/hooks/useUser';
|
||||
import { isSystemNamespace } from '@/react/kubernetes/namespaces/queries/useIsSystemNamespace';
|
||||
|
||||
import { TableSettingsMenu } from '@@/datatables';
|
||||
import { useRepeater } from '@@/datatables/useRepeater';
|
||||
|
@ -18,6 +19,7 @@ import { ExpandableDatatable } from '@@/datatables/ExpandableDatatable';
|
|||
|
||||
import { NamespaceFilter } from '../ApplicationsStacksDatatable/NamespaceFilter';
|
||||
import { Namespace } from '../ApplicationsStacksDatatable/types';
|
||||
import { useApplications } from '../../application.queries';
|
||||
|
||||
import { Application, ConfigKind } from './types';
|
||||
import { useColumns } from './useColumns';
|
||||
|
@ -26,9 +28,7 @@ import { SubRow } from './SubRow';
|
|||
import { HelmInsightsBox } from './HelmInsightsBox';
|
||||
|
||||
export function ApplicationsDatatable({
|
||||
dataset,
|
||||
onRefresh,
|
||||
isLoading,
|
||||
onRemove,
|
||||
namespace = '',
|
||||
namespaces,
|
||||
|
@ -37,9 +37,7 @@ export function ApplicationsDatatable({
|
|||
onShowSystemChange,
|
||||
hideStacks,
|
||||
}: {
|
||||
dataset: Array<Application>;
|
||||
onRefresh: () => void;
|
||||
isLoading: boolean;
|
||||
onRemove: (selectedItems: Application[]) => void;
|
||||
namespace?: string;
|
||||
namespaces: Array<Namespace>;
|
||||
|
@ -50,7 +48,7 @@ export function ApplicationsDatatable({
|
|||
}) {
|
||||
const envId = useEnvironmentId();
|
||||
const envQuery = useCurrentEnvironment();
|
||||
const namespaceMetaListQuery = useNamespacesQuery(envId);
|
||||
const namespaceListQuery = useNamespacesQuery(envId);
|
||||
|
||||
const tableState = useKubeStore('kubernetes.applications', 'Name');
|
||||
useRepeater(tableState.autoRefreshRate, onRefresh);
|
||||
|
@ -58,7 +56,7 @@ export function ApplicationsDatatable({
|
|||
const hasWriteAuthQuery = useAuthorizations(
|
||||
'K8sApplicationsW',
|
||||
undefined,
|
||||
true
|
||||
false
|
||||
);
|
||||
|
||||
const { setShowSystemResources } = tableState;
|
||||
|
@ -67,27 +65,34 @@ export function ApplicationsDatatable({
|
|||
setShowSystemResources(showSystem || false);
|
||||
}, [showSystem, setShowSystemResources]);
|
||||
|
||||
const columns = useColumns(hideStacks);
|
||||
const applicationsQuery = useApplications(envId, {
|
||||
refetchInterval: tableState.autoRefreshRate * 1000,
|
||||
namespace,
|
||||
withDependencies: true,
|
||||
});
|
||||
const applications = applicationsQuery.data ?? [];
|
||||
const filteredApplications = showSystem
|
||||
? applications
|
||||
: applications.filter(
|
||||
(application) =>
|
||||
!isSystemNamespace(application.ResourcePool, namespaceListQuery.data)
|
||||
);
|
||||
|
||||
const filteredDataset = !showSystem
|
||||
? dataset.filter(
|
||||
(item) => !namespaceMetaListQuery.data?.[item.ResourcePool]?.IsSystem
|
||||
)
|
||||
: dataset;
|
||||
const columns = useColumns(hideStacks);
|
||||
|
||||
return (
|
||||
<ExpandableDatatable
|
||||
data-cy="k8sApp-appTable"
|
||||
noWidget
|
||||
dataset={filteredDataset}
|
||||
dataset={filteredApplications ?? []}
|
||||
settingsManager={tableState}
|
||||
columns={columns}
|
||||
title="Applications"
|
||||
titleIcon={BoxIcon}
|
||||
isLoading={isLoading}
|
||||
isLoading={applicationsQuery.isLoading}
|
||||
disableSelect={!hasWriteAuthQuery.authorized}
|
||||
isRowSelectable={(row) =>
|
||||
!namespaceMetaListQuery.data?.[row.original.ResourcePool]?.IsSystem
|
||||
!isSystemNamespace(row.original.ResourcePool, namespaceListQuery.data)
|
||||
}
|
||||
getRowCanExpand={(row) => isExpandable(row.original)}
|
||||
renderSubRow={(row) => (
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
import { isoDate, truncate } from '@/portainer/filters/filters';
|
||||
import { CellContext } from '@tanstack/react-table';
|
||||
|
||||
import { isoDate, truncate } from '@/portainer/filters/filters';
|
||||
import { useIsSystemNamespace } from '@/react/kubernetes/namespaces/queries/useIsSystemNamespace';
|
||||
|
||||
import { Link } from '@@/Link';
|
||||
import { SystemBadge } from '@@/Badge/SystemBadge';
|
||||
|
||||
import { Application } from './types';
|
||||
import { helper } from './columns.helper';
|
||||
|
||||
export const stackName = helper.accessor('StackName', {
|
||||
|
@ -9,9 +16,26 @@ export const stackName = helper.accessor('StackName', {
|
|||
|
||||
export const namespace = helper.accessor('ResourcePool', {
|
||||
header: 'Namespace',
|
||||
cell: ({ getValue }) => getValue() || '-',
|
||||
cell: NamespaceCell,
|
||||
});
|
||||
|
||||
function NamespaceCell({ row, getValue }: CellContext<Application, string>) {
|
||||
const value = getValue();
|
||||
const isSystem = useIsSystemNamespace(value);
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Link
|
||||
to="kubernetes.resourcePools.resourcePool"
|
||||
params={{ id: value }}
|
||||
data-cy={`app-namespace-link-${row.original.Name}`}
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
{isSystem && <SystemBadge />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const image = helper.accessor('Image', {
|
||||
header: 'Image',
|
||||
cell: ({ row: { original: item } }) => (
|
||||
|
|
|
@ -39,6 +39,12 @@ export interface Application {
|
|||
}>;
|
||||
Port: number;
|
||||
}>;
|
||||
Resource?: {
|
||||
CpuLimit?: number;
|
||||
CpuRequest?: number;
|
||||
MemoryLimit?: number;
|
||||
MemoryRequest?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export enum ConfigKind {
|
||||
|
|
|
@ -4,44 +4,41 @@ import { useEffect } from 'react';
|
|||
import { useAuthorizations } from '@/react/hooks/useUser';
|
||||
import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
|
||||
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import { isSystemNamespace } from '@/react/kubernetes/namespaces/queries/useIsSystemNamespace';
|
||||
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
|
||||
|
||||
import { ExpandableDatatable } from '@@/datatables/ExpandableDatatable';
|
||||
import { useRepeater } from '@@/datatables/useRepeater';
|
||||
import { useTableState } from '@@/datatables/useTableState';
|
||||
|
||||
import { KubernetesStack } from '../../types';
|
||||
import { useApplications } from '../../application.queries';
|
||||
|
||||
import { columns } from './columns';
|
||||
import { SubRows } from './SubRows';
|
||||
import { Namespace } from './types';
|
||||
import { Namespace, Stack } from './types';
|
||||
import { StacksSettingsMenu } from './StacksSettingsMenu';
|
||||
import { NamespaceFilter } from './NamespaceFilter';
|
||||
import { TableActions } from './TableActions';
|
||||
import { getStacksFromApplications } from './getStacksFromApplications';
|
||||
|
||||
const storageKey = 'kubernetes.applications.stacks';
|
||||
|
||||
const settingsStore = createStore(storageKey);
|
||||
|
||||
interface Props {
|
||||
dataset: Array<KubernetesStack>;
|
||||
onRemove(selectedItems: Array<KubernetesStack>): void;
|
||||
onRefresh(): Promise<void>;
|
||||
onRemove(selectedItems: Array<Stack>): void;
|
||||
namespace?: string;
|
||||
namespaces: Array<Namespace>;
|
||||
onNamespaceChange(namespace: string): void;
|
||||
isLoading?: boolean;
|
||||
showSystem?: boolean;
|
||||
setSystemResources(showSystem: boolean): void;
|
||||
}
|
||||
|
||||
export function ApplicationsStacksDatatable({
|
||||
dataset,
|
||||
onRemove,
|
||||
onRefresh,
|
||||
namespace = '',
|
||||
namespaces,
|
||||
onNamespaceChange,
|
||||
isLoading,
|
||||
showSystem,
|
||||
setSystemResources,
|
||||
}: Props) {
|
||||
|
@ -53,16 +50,32 @@ export function ApplicationsStacksDatatable({
|
|||
setShowSystemResources(showSystem || false);
|
||||
}, [showSystem, setShowSystemResources]);
|
||||
|
||||
const envId = useEnvironmentId();
|
||||
const applicationsQuery = useApplications(envId, {
|
||||
refetchInterval: tableState.autoRefreshRate * 1000,
|
||||
namespace,
|
||||
withDependencies: true,
|
||||
});
|
||||
const namespaceListQuery = useNamespacesQuery(envId);
|
||||
const applications = applicationsQuery.data ?? [];
|
||||
const filteredApplications = showSystem
|
||||
? applications
|
||||
: applications.filter(
|
||||
(item) =>
|
||||
!isSystemNamespace(item.ResourcePool, namespaceListQuery.data ?? [])
|
||||
);
|
||||
|
||||
const { authorized } = useAuthorizations('K8sApplicationsW');
|
||||
useRepeater(tableState.autoRefreshRate, onRefresh);
|
||||
|
||||
const stacks = getStacksFromApplications(filteredApplications);
|
||||
|
||||
return (
|
||||
<ExpandableDatatable
|
||||
getRowCanExpand={(row) => row.original.Applications.length > 0}
|
||||
title="Stacks"
|
||||
titleIcon={List}
|
||||
dataset={dataset}
|
||||
isLoading={isLoading}
|
||||
dataset={stacks}
|
||||
isLoading={applicationsQuery.isLoading || namespaceListQuery.isLoading}
|
||||
columns={columns}
|
||||
settingsManager={tableState}
|
||||
disableSelect={!authorized}
|
||||
|
|
|
@ -6,15 +6,9 @@ import KubernetesNamespaceHelper from '@/kubernetes/helpers/namespaceHelper';
|
|||
import { Link } from '@@/Link';
|
||||
import { ExternalBadge } from '@@/Badge/ExternalBadge';
|
||||
|
||||
import { KubernetesStack } from '../../types';
|
||||
import { Stack } from './types';
|
||||
|
||||
export function SubRows({
|
||||
stack,
|
||||
span,
|
||||
}: {
|
||||
stack: KubernetesStack;
|
||||
span: number;
|
||||
}) {
|
||||
export function SubRows({ stack, span }: { stack: Stack; span: number }) {
|
||||
return (
|
||||
<>
|
||||
{stack.Applications.map((app) => (
|
||||
|
|
|
@ -2,14 +2,14 @@ import { Authorized } from '@/react/hooks/useUser';
|
|||
|
||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
||||
|
||||
import { KubernetesStack } from '../../types';
|
||||
import { Stack } from './types';
|
||||
|
||||
export function TableActions({
|
||||
selectedItems,
|
||||
onRemove,
|
||||
}: {
|
||||
selectedItems: Array<KubernetesStack>;
|
||||
onRemove: (selectedItems: Array<KubernetesStack>) => void;
|
||||
selectedItems: Array<Stack>;
|
||||
onRemove: (selectedItems: Array<Stack>) => void;
|
||||
}) {
|
||||
return (
|
||||
<Authorized authorizations="K8sApplicationsW">
|
||||
|
|
|
@ -1,63 +1,70 @@
|
|||
import { FileText } from 'lucide-react';
|
||||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
import { CellContext, createColumnHelper } from '@tanstack/react-table';
|
||||
|
||||
import KubernetesNamespaceHelper from '@/kubernetes/helpers/namespaceHelper';
|
||||
import { useIsSystemNamespace } from '@/react/kubernetes/namespaces/queries/useIsSystemNamespace';
|
||||
|
||||
import { buildExpandColumn } from '@@/datatables/expand-column';
|
||||
import { Link } from '@@/Link';
|
||||
import { Icon } from '@@/Icon';
|
||||
import { SystemBadge } from '@@/Badge/SystemBadge';
|
||||
|
||||
import { KubernetesStack } from '../../types';
|
||||
import { Stack } from './types';
|
||||
|
||||
export const columnHelper = createColumnHelper<KubernetesStack>();
|
||||
export const columnHelper = createColumnHelper<Stack>();
|
||||
|
||||
const namespace = columnHelper.accessor('ResourcePool', {
|
||||
id: 'namespace',
|
||||
header: 'Namespace',
|
||||
cell: NamespaceCell,
|
||||
});
|
||||
|
||||
function NamespaceCell({ row, getValue }: CellContext<Stack, string>) {
|
||||
const value = getValue();
|
||||
const isSystem = useIsSystemNamespace(value);
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Link
|
||||
to="kubernetes.resourcePools.resourcePool"
|
||||
params={{ id: value }}
|
||||
data-cy={`app-stack-namespace-link-${row.original.Name}`}
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
{isSystem && <SystemBadge />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const name = columnHelper.accessor('Name', {
|
||||
id: 'name',
|
||||
header: 'Stack',
|
||||
});
|
||||
|
||||
const applications = columnHelper.accessor((row) => row.Applications.length, {
|
||||
id: 'applications',
|
||||
header: 'Applications',
|
||||
});
|
||||
|
||||
const actions = columnHelper.display({
|
||||
id: 'actions',
|
||||
header: 'Actions',
|
||||
cell: ({ row: { original: item } }) => (
|
||||
<Link
|
||||
to="kubernetes.stacks.stack.logs"
|
||||
params={{ namespace: item.ResourcePool, name: item.Name }}
|
||||
className="flex items-center gap-1"
|
||||
data-cy={`app-stack-logs-link-${item.Name}`}
|
||||
>
|
||||
<Icon icon={FileText} />
|
||||
Logs
|
||||
</Link>
|
||||
),
|
||||
});
|
||||
|
||||
export const columns = [
|
||||
buildExpandColumn<KubernetesStack>(),
|
||||
columnHelper.accessor('Name', {
|
||||
id: 'name',
|
||||
header: 'Stack',
|
||||
}),
|
||||
columnHelper.accessor('ResourcePool', {
|
||||
id: 'namespace',
|
||||
header: 'Namespace',
|
||||
cell: ({ getValue, row }) => {
|
||||
const value = getValue();
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
<Link
|
||||
to="kubernetes.resourcePools.resourcePool"
|
||||
params={{ id: value }}
|
||||
data-cy={`app-stack-namespace-link-${row.original.Name}`}
|
||||
>
|
||||
{value}
|
||||
</Link>
|
||||
{KubernetesNamespaceHelper.isSystemNamespace(value) && (
|
||||
<SystemBadge />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
|
||||
columnHelper.accessor((row) => row.Applications.length, {
|
||||
id: 'applications',
|
||||
header: 'Applications',
|
||||
}),
|
||||
|
||||
columnHelper.display({
|
||||
id: 'actions',
|
||||
header: 'Actions',
|
||||
cell: ({ row: { original: item } }) => (
|
||||
<Link
|
||||
to="kubernetes.stacks.stack.logs"
|
||||
params={{ namespace: item.ResourcePool, name: item.Name }}
|
||||
className="flex items-center gap-1"
|
||||
data-cy={`app-stack-logs-link-${item.Name}`}
|
||||
>
|
||||
<Icon icon={FileText} />
|
||||
Logs
|
||||
</Link>
|
||||
),
|
||||
}),
|
||||
buildExpandColumn<Stack>(),
|
||||
name,
|
||||
namespace,
|
||||
applications,
|
||||
actions,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
import { Application } from '../ApplicationsDatatable/types';
|
||||
|
||||
import { getStacksFromApplications } from './getStacksFromApplications';
|
||||
import { Stack } from './types';
|
||||
|
||||
describe('getStacksFromApplications', () => {
|
||||
test('should return an empty array when passed an empty array', () => {
|
||||
expect(getStacksFromApplications([])).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should return an empty array when passed a list of applications without stacks', () => {
|
||||
const appsWithoutStacks: Application[] = [
|
||||
{
|
||||
StackName: '',
|
||||
Id: '1',
|
||||
Name: 'app1',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
{
|
||||
StackName: '',
|
||||
Id: '1',
|
||||
Name: 'app2',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
{
|
||||
StackName: '',
|
||||
Id: '1',
|
||||
Name: 'app3',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
];
|
||||
expect(getStacksFromApplications(appsWithoutStacks)).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should return a list of stacks when passed a list of applications with stacks', () => {
|
||||
const appsWithStacks: Application[] = [
|
||||
{
|
||||
StackName: 'stack1',
|
||||
Id: '1',
|
||||
Name: 'app1',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
{
|
||||
StackName: 'stack1',
|
||||
Id: '1',
|
||||
Name: 'app2',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
{
|
||||
StackName: 'stack2',
|
||||
Id: '1',
|
||||
Name: 'app3',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const expectedStacksWithApps: Stack[] = [
|
||||
{
|
||||
Name: 'stack1',
|
||||
ResourcePool: 'namespace1',
|
||||
Applications: [
|
||||
{
|
||||
StackName: 'stack1',
|
||||
Id: '1',
|
||||
Name: 'app1',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
{
|
||||
StackName: 'stack1',
|
||||
Id: '1',
|
||||
Name: 'app2',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
],
|
||||
Highlighted: false,
|
||||
},
|
||||
{
|
||||
Name: 'stack2',
|
||||
ResourcePool: 'namespace1',
|
||||
Applications: [
|
||||
{
|
||||
StackName: 'stack2',
|
||||
Id: '1',
|
||||
Name: 'app3',
|
||||
CreationDate: '2021-10-01T00:00:00Z',
|
||||
ResourcePool: 'namespace1',
|
||||
Image: 'image1',
|
||||
ApplicationType: 'Pod',
|
||||
DeploymentType: 'Replicated',
|
||||
Status: 'status1',
|
||||
TotalPodsCount: 1,
|
||||
RunningPodsCount: 1,
|
||||
},
|
||||
],
|
||||
Highlighted: false,
|
||||
},
|
||||
];
|
||||
|
||||
expect(getStacksFromApplications(appsWithStacks)).toEqual(
|
||||
expectedStacksWithApps
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import { Application } from '../ApplicationsDatatable/types';
|
||||
|
||||
import { Stack } from './types';
|
||||
|
||||
export function getStacksFromApplications(applications: Application[]) {
|
||||
const res = applications.reduce<Stack[]>((stacks, app) => {
|
||||
const updatedStacks = stacks.map((stack) => {
|
||||
if (
|
||||
stack.Name === app.StackName &&
|
||||
stack.ResourcePool === app.ResourcePool
|
||||
) {
|
||||
return {
|
||||
...stack,
|
||||
Applications: [...stack.Applications, app],
|
||||
};
|
||||
}
|
||||
return stack;
|
||||
});
|
||||
|
||||
const stackExists = updatedStacks.some(
|
||||
(stack) =>
|
||||
stack.Name === app.StackName && stack.ResourcePool === app.ResourcePool
|
||||
);
|
||||
|
||||
if (!stackExists && app.StackName) {
|
||||
updatedStacks.push({
|
||||
Name: app.StackName,
|
||||
ResourcePool: app.ResourcePool,
|
||||
Applications: [app],
|
||||
Highlighted: false,
|
||||
});
|
||||
}
|
||||
return updatedStacks;
|
||||
}, []);
|
||||
return res;
|
||||
}
|
|
@ -5,6 +5,8 @@ import {
|
|||
RefreshableTableSettings,
|
||||
} from '@@/datatables/types';
|
||||
|
||||
import { Application } from '../ApplicationsDatatable/types';
|
||||
|
||||
export interface TableSettings
|
||||
extends BasicTableSettings,
|
||||
RefreshableTableSettings,
|
||||
|
@ -16,3 +18,10 @@ export interface Namespace {
|
|||
Yaml: string;
|
||||
IsSystem?: boolean;
|
||||
}
|
||||
|
||||
export type Stack = {
|
||||
Name: string;
|
||||
ResourcePool: string;
|
||||
Applications: Application[];
|
||||
Highlighted: boolean;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue