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 14, 2023
1 parent 5268a8d commit d822813
Show file tree
Hide file tree
Showing 4 changed files with 199 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
126 changes: 126 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,129 @@ export const ServicePodsField = (_: i18n): ShowField<ResourceModel> => {
},
};
};

import { Tabs, Tag, List } from 'antd';
import { get } from 'lodash';

const { TabPane } = Tabs;

// Utility component for rendering selectors like IPBlock, NamespaceSelector, etc.
const RuleList: React.FC<{ data: any; type: string }> = ({ data, type }) => {
switch (type) {
case 'IPBlock':
return (
<>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2">CIDR</label>
<input
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
value={data.cidr}
disabled
/>
</div>
{data.except && (
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2">
Exceptions
</label>
<List
size="small"
bordered
dataSource={data.except}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</div>
)}
</>
);
case 'NamespaceSelector':
case 'PodSelector':
return (
<>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2">
{type === 'NamespaceSelector' ? 'Namespace Selector' : 'Pod Selector'}
</label>
<List
size="small"
bordered
dataSource={Object.entries(data.matchLabels)}
renderItem={([key, value]) => (
<List.Item>
{key}={value}
</List.Item>
)}
/>
</div>
</>
);
default:
return null;
}
};

// Component for rendering ingress/egress details
const IngressEgressList: React.FC<{ data: any[]; type: 'ingress' | 'egress' }> = ({
data,
type,
}) => {
return (
<Tabs defaultActiveKey="0">
{data.map((rule, index) => (
<TabPane tab={`Rule ${index + 1}`} key={index}>
{type === 'ingress' &&
rule.from &&
rule.from.map((fromRule, fromIndex) => (
<RuleList key={fromIndex} data={fromRule} type={Object.keys(fromRule)[0]} />
))}
{type === 'egress' &&
rule.to &&
rule.to.map((toRule, toIndex) => (
<RuleList key={toIndex} data={toRule} type={Object.keys(toRule)[0]} />
))}
{rule.ports && (
<div className="mb-4">
<label className="block text-gray-700 text-sm font-bold mb-2">
Allowed ports
</label>
<List
size="small"
bordered
dataSource={rule.ports}
renderItem={port => (
<List.Item>
Port {port.port}: {port.protocol}
</List.Item>
)}
/>
</div>
)}
</TabPane>
))}
</Tabs>
);
};

// Field renderer for ingress
export const IngressField = (i18n: i18n): ShowField<ResourceModel> => {
return {
key: 'ingress',
title: i18n.t('ingress'),
path: ['rawYaml', 'spec', 'ingress'],
render: ingressRules => {
return <IngressEgressList data={ingressRules} type="ingress" />;
},
};
};

// Field renderer for egress
export const EgressField = (i18n: i18n): ShowField<ResourceModel> => {
return {
key: 'egress',
title: i18n.t('egress'),
path: ['rawYaml', 'spec', 'egress'],
render: egressRules => {
return <IngressEgressList data={egressRules} type="egress" />;
},
};
};
34 changes: 34 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,37 @@ export const ServiceTypeColumnRenderer = <Model extends ResourceModel>(
sorter: CommonSorter(dataIndex),
};
};
import { NetworkPolicy } from 'kubernetes-types/networking/v1';

const countRules = (rules: any[] | undefined): number => {
return rules ? rules.length : 0;
};

export const RulesColumnRenderer = (i18n: i18n): Column<ResourceModel> => {
return {
key: 'rules',
display: true,
title: i18n.t('rules'),
dataIndex: ['rawYaml'],
render: value => {
const networkPolicy: NetworkPolicy = value;
const ingressCount = countRules(get(networkPolicy, 'spec.ingress'));
const egressCount = countRules(get(networkPolicy, 'spec.egress'));
return `${ingressCount} ingress / ${egressCount} egress`;
},
};
};

export const PodSelectorColumnRenderer = (i18n: i18n): Column<ResourceModel> => {
return {
key: 'podSelector',
display: true,
title: i18n.t('pod_selector'),
dataIndex: ['rawYaml'],
render: value => {
const networkPolicy: NetworkPolicy = value;
const podSelector = get(networkPolicy, 'spec.podSelector', {});
return JSON.stringify(podSelector);
},
};
};
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 { RulesColumnRenderer } from '../../hooks/useEagleTable/columns';
import { PodSelectorColumnRenderer } from '../../hooks/useEagleTable/columns';
import { IngressField } from '../../components/ShowContent/fields';
import { EgressField } 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: ['Ingress', 'Egress'],
ingress: [],
egress: [],
},
};

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) => [RulesColumnRenderer(i18n), PodSelectorColumnRenderer(i18n)],
showFields: (i18n: i18n) => [[], [], [IngressField(i18n), EgressField(i18n)]],
initValue: NETWORKPOLICY_INIT_VALUE,
};

0 comments on commit d822813

Please sign in to comment.