1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-10 08:15:25 +02:00

feat(docker/networks): show sub networks

This commit is contained in:
Chaim Lev-Ari 2023-09-20 14:37:49 +03:00
parent 24341cd1ac
commit 8143fab676
8 changed files with 134 additions and 64 deletions

View file

@ -1,33 +0,0 @@
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
export function NetworkViewModel(data) {
this.Id = data.Id;
this.Name = data.Name;
this.Scope = data.Scope;
this.Driver = data.Driver;
this.Attachable = data.Attachable;
this.Internal = data.Internal;
this.IPAM = data.IPAM;
this.Containers = data.Containers;
this.Options = data.Options;
this.Ingress = data.Ingress;
this.Labels = data.Labels;
if (this.Labels && this.Labels['com.docker.compose.project']) {
this.StackName = this.Labels['com.docker.compose.project'];
} else if (this.Labels && this.Labels['com.docker.stack.namespace']) {
this.StackName = this.Labels['com.docker.stack.namespace'];
}
if (data.Portainer) {
if (data.Portainer.ResourceControl) {
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
}
if (data.Portainer.Agent && data.Portainer.Agent.NodeName) {
this.NodeName = data.Portainer.Agent.NodeName;
}
}
this.ConfigFrom = data.ConfigFrom;
this.ConfigOnly = data.ConfigOnly;
}

View file

@ -0,0 +1,79 @@
import { IPAM, Network, NetworkContainer } from 'docker-types/generated/1.41';
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { IResource } from '@/react/docker/components/datatable/createOwnershipColumn';
import { PortainerMetadata } from '@/react/docker/types';
export class NetworkViewModel implements IResource {
Id: string;
Name: string;
Scope: string;
Driver: string;
Attachable: boolean;
Internal: boolean;
IPAM?: IPAM;
Containers?: Record<string, NetworkContainer>;
Options?: Record<string, string>;
Ingress: boolean;
Labels: Record<string, string>;
StackName?: string;
NodeName?: string;
ConfigFrom?: { Network: string };
ConfigOnly?: boolean;
ResourceControl?: ResourceControlViewModel;
constructor(
data: Network & {
Portainer?: PortainerMetadata;
ConfigFrom?: { Network: string };
ConfigOnly?: boolean;
}
) {
this.Id = data.Id || '';
this.Name = data.Name || '';
this.Scope = data.Scope || '';
this.Driver = data.Driver || '';
this.Attachable = data.Attachable || false;
this.Internal = data.Internal || false;
this.IPAM = data.IPAM;
this.Containers = data.Containers;
this.Options = data.Options;
this.Ingress = data.Ingress || false;
this.Labels = data.Labels || {};
if (this.Labels && this.Labels['com.docker.compose.project']) {
this.StackName = this.Labels['com.docker.compose.project'];
} else if (this.Labels && this.Labels['com.docker.stack.namespace']) {
this.StackName = this.Labels['com.docker.stack.namespace'];
}
if (data.Portainer) {
if (data.Portainer.ResourceControl) {
this.ResourceControl = new ResourceControlViewModel(
data.Portainer.ResourceControl
);
}
if (data.Portainer.Agent && data.Portainer.Agent.NodeName) {
this.NodeName = data.Portainer.Agent.NodeName;
}
}
this.ConfigFrom = data.ConfigFrom;
this.ConfigOnly = data.ConfigOnly;
}
}

View file

@ -10,9 +10,7 @@ import {
ResourceObject,
} from 'docker-types/generated/1.41';
type WithRequiredProperty<Type, Key extends keyof Type> = Type & {
[Property in Key]-?: Type[Property];
};
import { WithRequiredProperty } from '@/types';
export class NodeViewModel {
Model: Node;

View file

@ -0,0 +1,20 @@
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { NestedDatatable } from '@@/datatables/NestedDatatable';
import { useIsSwarm } from '../../proxy/queries/useInfo';
import { useColumns } from './columns';
import { DecoratedNetwork } from './types';
export function NestedNetworksDatatable({
dataset,
}: {
dataset: Array<DecoratedNetwork>;
}) {
const environmentId = useEnvironmentId();
const isSwarm = useIsSwarm(environmentId);
const columns = useColumns(isSwarm);
return <NestedDatatable columns={columns} dataset={dataset} />;
}

View file

@ -1,5 +1,4 @@
import { Plus, Share2, Trash2 } from 'lucide-react';
import clsx from 'clsx';
import { Authorized } from '@/react/hooks/useUser';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
@ -12,7 +11,7 @@ import {
RefreshableTableSettings,
} from '@@/datatables/types';
import { Button } from '@@/buttons';
import { TableRow, TableSettingsMenu } from '@@/datatables';
import { TableSettingsMenu } from '@@/datatables';
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
import { useRepeater } from '@@/datatables/useRepeater';
import { useTableState } from '@@/datatables/useTableState';
@ -20,8 +19,9 @@ import { Link } from '@@/Link';
import { useIsSwarm } from '../../proxy/queries/useInfo';
import { DockerNetworkViewModel } from './types';
import { useColumns } from './columns';
import { DecoratedNetwork } from './types';
import { NestedNetworksDatatable } from './NestedNetwordsTable';
const storageKey = 'docker.networks';
@ -35,7 +35,7 @@ const settingsStore = createPersistedStore<TableSettings>(
})
);
type DatasetType = Array<DockerNetworkViewModel>;
type DatasetType = Array<DecoratedNetwork>;
interface Props {
dataset: DatasetType;
onRemove(selectedItems: DatasetType): void;
@ -43,14 +43,17 @@ interface Props {
}
export function NetworksDatatable({ dataset, onRemove, onRefresh }: Props) {
const environmentId = useEnvironmentId();
const settings = useTableState(settingsStore, storageKey);
const environmentId = useEnvironmentId();
const isSwarm = useIsSwarm(environmentId);
const columns = useColumns(isSwarm);
useRepeater(settings.autoRefreshRate, onRefresh);
return (
<ExpandableDatatable<DockerNetworkViewModel>
<ExpandableDatatable<DecoratedNetwork>
settingsManager={settings}
title="Networks"
titleIcon={Share2}
@ -61,10 +64,13 @@ export function NetworksDatatable({ dataset, onRemove, onRefresh }: Props) {
}
renderSubRow={(row) => (
<>
{row.original.Subs &&
row.original.Subs.map((network, idx) => (
<TableRow<D> cells={cells} />
))}
{row.original.Subs && (
<tr>
<td colSpan={Number.MAX_SAFE_INTEGER}>
<NestedNetworksDatatable dataset={row.original.Subs} />
</td>
</tr>
)}
</>
)}
emptyContentLabel="No networks available."

View file

@ -1,5 +1,5 @@
import { createColumnHelper } from '@tanstack/react-table';
import { DockerNetworkViewModel } from '../types';
import { DecoratedNetwork } from '../types';
export const columnHelper = createColumnHelper<DockerNetworkViewModel>();
export const columnHelper = createColumnHelper<DecoratedNetwork>();

View file

@ -6,7 +6,7 @@ import { createOwnershipColumn } from '@/react/docker/components/datatable/creat
import { buildExpandColumn } from '@@/datatables/expand-column';
import { buildNameColumn } from '@@/datatables/buildNameColumn';
import { DockerNetworkViewModel } from '../types';
import { DecoratedNetwork } from '../types';
import { columnHelper } from './helper';
@ -14,8 +14,8 @@ export function useColumns(isHostColumnVisible?: boolean) {
return useMemo(
() =>
_.compact([
buildExpandColumn<DockerNetworkViewModel>(),
buildNameColumn<DockerNetworkViewModel>('Name', '.network'),
buildExpandColumn<DecoratedNetwork>(),
buildNameColumn<DecoratedNetwork>('Name', '.network'),
columnHelper.accessor((item) => item.StackName || '-', {
header: 'Stack',
}),
@ -29,25 +29,25 @@ export function useColumns(isHostColumnVisible?: boolean) {
header: 'IPAM Driver',
}),
columnHelper.accessor(
(item) => item.IPAM?.IPV4Configs[0]?.Subnet ?? '-',
(item) => item.IPAM?.IPV4Configs?.[0]?.Subnet ?? '-',
{
header: 'IPV4 IPAM Subnet',
}
),
columnHelper.accessor(
(item) => item.IPAM?.IPV4Configs[0]?.Gateway ?? '-',
(item) => item.IPAM?.IPV4Configs?.[0]?.Gateway ?? '-',
{
header: 'IPV4 IPAM Gateway',
}
),
columnHelper.accessor(
(item) => item.IPAM?.IPV6Configs[0]?.Subnet ?? '-',
(item) => item.IPAM?.IPV6Configs?.[0]?.Subnet ?? '-',
{
header: 'IPV6 IPAM Subnet',
}
),
columnHelper.accessor(
(item) => item.IPAM?.IPV6Configs[0]?.Gateway ?? '-',
(item) => item.IPAM?.IPV6Configs?.[0]?.Gateway ?? '-',
{
header: 'IPV6 IPAM Gateway',
}
@ -56,7 +56,7 @@ export function useColumns(isHostColumnVisible?: boolean) {
columnHelper.accessor('NodeName', {
header: 'Node',
}),
createOwnershipColumn<DockerNetworkViewModel>(),
createOwnershipColumn<DecoratedNetwork>(),
]),
[isHostColumnVisible]
);

View file

@ -1,11 +1,11 @@
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { IPAMConfig } from 'docker-types/generated/1.41';
import { DockerNetwork } from '../types';
import { NetworkViewModel } from '@/docker/models/network';
export type DockerNetworkViewModel = DockerNetwork & {
StackName?: string;
ResourceControl?: ResourceControlViewModel;
NodeName?: string;
Subs?: DockerNetworkViewModel[];
Highlighted: boolean;
export type DecoratedNetwork = NetworkViewModel & {
Subs?: DecoratedNetwork[];
IPAM: NetworkViewModel['IPAM'] & {
IPV4Configs?: Array<IPAMConfig>;
IPV6Configs?: Array<IPAMConfig>;
};
};