Skip to content

Commit

Permalink
d2-AI prompt:
Browse files Browse the repository at this point in the history
{
  "prompt": "开发 K8s 中 NetworkPolicy 资源的 UI,它在菜单中的名字为“Network Policies”,父级菜单是网络。\n在列表中,增加一列展示 ingress 和 egress rules 的总量,以及一列用于展示出 pod selector 规则。\n\r\n\r\n\r\n在详情区域,增加对 ingress 和 egress 的展示。\r\n\r\n在 ingress 详情中,每个 ingress 用一个 tab 展示。渲染每个 ingress 时,根据 ingress.from 中各条规则的类型分别渲染:\r\n- IP block 类型,展示 CIDR 和 exceptions 字段;\r\n- Namespace Selector 类型,展示 key、operator、value,并统计选中了多少个 namespace;\r\n- Pod selector 类型,展示 key、operator、value,并统计选中了多少个 pod。\r\n将 ingress.ports 展示出来,包含 port 和 protocol 信息。\r\n\r\n在 egress 详情中,每个 egress 用一个 tab 展示。渲染每个 egress 时,根据 egress.to 中各条规则的类型分别渲染:\r\n- IP block 类型,展示 CIDR 和 exceptions 字段;\r\n将 egress.ports 展示出来,包含 port 和 protocol 信息。\r\n",
  "images": [
    "https://github.com/webzard-io/dovetail-v2/assets/13651389/a4976e91-fd81-4560-8fc9-fa7695b25a92"
  ]
}
  • Loading branch information
Yuyz0112 committed Dec 15, 2023
1 parent db50e60 commit 9b5c17b
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/refine/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Route, Router } from 'react-router-dom';
import { Layout } from './components';
import { Dovetail } from './Dovetail';
import { ConfigMapConfig } from './pages/configmaps';
import { NetworkPolicyConfig } from './pages/networkpolicies';
import { CronJobForm, CronJobList, CronJobShow } from './pages/cronjobs';
import { DaemonSetForm, DaemonSetList, DaemonSetShow } from './pages/daemonsets';
import { DeploymentForm, DeploymentList, DeploymentShow } from './pages/deployments';
Expand Down Expand Up @@ -66,6 +67,7 @@ function App() {
ConfigMapConfig,
SecretsConfig,
ServicesConfig,
NetworkPolicyConfig,
];
}, []);

Expand Down
132 changes: 132 additions & 0 deletions packages/refine/src/components/ShowContent/fields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,135 @@ export const ServicePodsField = (_: i18n): ShowField<ResourceModel> => {
},
};
};
import { Collapse, List, Tabs, Tag } from 'antd';
import { get } from 'lodash';

const { TabPane } = Tabs;

const PortList: React.FC<{ ports: Array<{ port: number; protocol: string }> }> = ({
ports,
}) => {
return (
<List
bordered
dataSource={ports}
renderItem={portItem => (
<List.Item className="flex justify-between">
<span>{portItem.port}</span>
<Tag>{portItem.protocol}</Tag>
</List.Item>
)}
/>
);
};

const RuleList: React.FC<{ ruleType: string; data: any }> = ({ ruleType, data }) => {
switch (ruleType) {
case 'ipBlock':
return (
<div>
<div>
<strong>CIDR:</strong> {data.cidr}
</div>
{data.except && (
<div>
<strong>Except:</strong> {data.except.join(', ')}
</div>
)}
</div>
);
case 'namespaceSelector':
case 'podSelector':
// The rendering for namespaceSelector and podSelector would be similar,
// with keys being the label names and the values being the label values
const selectorPairs = Object.entries(data.matchLabels || {});
return (
<List
bordered
dataSource={selectorPairs}
renderItem={item => (
<List.Item className="max-w-20">
<span className="text-600-blue m-1 p-1">{item[0]}:</span> {item[1]}
</List.Item>
)}
/>
);
default:
return null;
}
};

export const IngressEgressField = (i18n: i18n): ShowField<ResourceModel> => {
return {
key: 'ingress-egress',
title: i18n.t('ingress-egress'),
path: ['rawYaml'],
render: yaml => {
const ingress = get(yaml, 'spec.ingress', []) as Array<any>;
const egress = get(yaml, 'spec.egress', []) as Array<any>;

return (
<Tabs defaultActiveKey="1" className="mb-5">
<TabPane tab="Ingress" key="1">
{ingress.map((ing, i) => (
<Collapse defaultActiveKey="0" key={i}>
<Collapse.Panel header={`Ingress ${i + 1}`} key="1">
<Tabs defaultActiveKey="1">
{ing.from.map((fromItem, index) => {
const ruleType = fromItem.ipBlock
? 'ipBlock'
: fromItem.namespaceSelector
? 'namespaceSelector'
: 'podSelector';

return (
<TabPane tab={ruleType} key={String(index)}>
<RuleList ruleType={ruleType} data={fromItem[ruleType]} />
</TabPane>
);
})}
<TabPane tab="Ports" key="ports">
<PortList ports={get(ing, 'ports', [])} />
</TabPane>
</Tabs>
</Collapse.Panel>
</Collapse>
))}
</TabPane>
<TabPane tab="Egress" key="2">
{egress.map((eg, i) => (
<Collapse defaultActiveKey="0" key={i}>
<Collapse.Panel header={`Egress ${i + 1}`} key="1">
<Tabs defaultActiveKey="1">
{eg.to
.map((toItem, index) => {
const ruleType = toItem.ipBlock
? 'ipBlock'
: toItem.namespaceSelector
? 'namespaceSelector'
: 'podSelector';

if (ruleType === 'ipBlock') {
return (
<TabPane tab={ruleType} key={String(index)}>
<RuleList ruleType={ruleType} data={toItem[ruleType]} />
</TabPane>
);
}

return null;
})
.filter(tab => tab)}
<TabPane tab="Ports" key="ports">
<PortList ports={get(eg, 'ports', [])} />
</TabPane>
</Tabs>
</Collapse.Panel>
</Collapse>
))}
</TabPane>
</Tabs>
);
},
};
};
48 changes: 48 additions & 0 deletions packages/refine/src/hooks/useEagleTable/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,51 @@ export const ServiceTypeColumnRenderer = <Model extends ResourceModel>(
sorter: CommonSorter(dataIndex),
};
};
import { NetworkPolicy } from 'kubernetes-types/networking/v1';

const RulesCount: React.FC<{ ingress?: any[]; egress?: any[] }> = ({
ingress,
egress,
}) => {
const ingressCount = ingress ? ingress.length : 0;
const egressCount = egress ? egress.length : 0;
return <span className="italic">{ingressCount + egressCount} rules</span>;
};

const PodSelector: React.FC<{ matchLabels?: object }> = ({ matchLabels }) => {
const labels = matchLabels
? Object.entries(matchLabels)
.map(([key, value]) => `${key}: ${value}`)
.join(', ')
: 'None';
return <span className="font-mono bg-gray-100 rounded px-2 py-1">{labels}</span>;
};

export const RulesCountColumnRenderer = (i18n: i18n): Column<NetworkPolicy> => {
return {
key: 'rulesCount',
display: true,
dataIndex: ['rawYaml', 'spec'],
title: i18n.t('rules_count'),
sortable: false,
render: spec => {
const ingress = get(spec, 'ingress', []);
const egress = get(spec, 'egress', []);
return <RulesCount ingress={ingress} egress={egress} />;
},
};
};

export const PodSelectorColumnRenderer = (i18n: i18n): Column<NetworkPolicy> => {
return {
key: 'podSelector',
display: true,
dataIndex: ['rawYaml', 'spec'],
title: i18n.t('pod_selector'),
sortable: false,
render: spec => {
const matchLabels = get(spec, 'podSelector.matchLabels', {});
return <PodSelector matchLabels={matchLabels} />;
},
};
};
37 changes: 37 additions & 0 deletions packages/refine/src/pages/networkpolicies/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { RulesCountColumnRenderer } from '../../hooks/useEagleTable/columns';
import { PodSelectorColumnRenderer } from '../../hooks/useEagleTable/columns';
import { IngressEgressField } from '../../components/ShowContent/fields';
import { i18n } from 'i18next';
import { RESOURCE_GROUP, ResourceConfig, WithId } from '../../types';
import { ResourceModel } from '../../model';
import { NetworkPolicy } from 'kubernetes-types/networking/v1';

const NETWORKPOLICY_INIT_VALUE = {
apiVersion: 'networking.k8s.io/v1',
kind: 'NetworkPolicy',
metadata: {
name: '',
namespace: 'default',
annotations: {},
labels: {},
},
spec: {
podSelector: {},
policyTypes: [],
},
};

export const NetworkPolicyConfig: ResourceConfig<WithId<NetworkPolicy>, ResourceModel> = {
name: 'networkpolicies',
kind: 'NetworkPolicy',
basePath: '/apis/networking.k8s.io/v1',
apiVersion: 'v1',
parent: RESOURCE_GROUP.NETWORK,
label: 'Network Policies',
columns: (i18n: i18n) => [
RulesCountColumnRenderer(i18n),
PodSelectorColumnRenderer(i18n),
],
showFields: (i18n: i18n) => [[], [], [IngressEgressField(i18n)]],
initValue: NETWORKPOLICY_INIT_VALUE,
};

0 comments on commit 9b5c17b

Please sign in to comment.