From 5238c7488bc0f326e6b30df2b9333a26a4cc1e6e Mon Sep 17 00:00:00 2001 From: yz-yu Date: Fri, 15 Dec 2023 11:17:44 +0800 Subject: [PATCH] =?UTF-8?q?d2-AI=20prompt:=20{=20=20=20"prompt":=20"?= =?UTF-8?q?=E5=BC=80=E5=8F=91=20K8s=20=E4=B8=AD=20NetworkPolicy=20?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E7=9A=84=20UI=EF=BC=8C=E5=AE=83=E5=9C=A8?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E4=B8=AD=E7=9A=84=E5=90=8D=E5=AD=97=E4=B8=BA?= =?UTF-8?q?=E2=80=9CNetwork=20Policies=E2=80=9D=EF=BC=8C=E7=88=B6=E7=BA=A7?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=98=AF=E7=BD=91=E7=BB=9C=E3=80=82\n?= =?UTF-8?q?=E5=9C=A8=E5=88=97=E8=A1=A8=E4=B8=AD=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=80=E5=88=97=E5=B1=95=E7=A4=BA=20ingress=20=E5=92=8C=20eg?= =?UTF-8?q?ress=20rules=20=E7=9A=84=E6=80=BB=E9=87=8F=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E4=B8=80=E5=88=97=E7=94=A8=E4=BA=8E=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=87=BA=20pod=20selector=20=E8=A7=84=E5=88=99=E3=80=82\n\r\n\?= =?UTF-8?q?r\n\r\n=E5=9C=A8=E8=AF=A6=E6=83=85=E5=8C=BA=E5=9F=9F=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9=20ingress=20=E5=92=8C=20egress=20?= =?UTF-8?q?=E7=9A=84=E5=B1=95=E7=A4=BA=E3=80=82\r\n\r\n=E5=9C=A8=20ingress?= =?UTF-8?q?=20=E8=AF=A6=E6=83=85=E4=B8=AD=EF=BC=8C=E6=AF=8F=E4=B8=AA=20ing?= =?UTF-8?q?ress=20=E7=94=A8=E4=B8=80=E4=B8=AA=20tab=20=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E3=80=82=E6=B8=B2=E6=9F=93=E6=AF=8F=E4=B8=AA=20ingress=20?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=A0=B9=E6=8D=AE=20ingress.from=20=E4=B8=AD?= =?UTF-8?q?=E5=90=84=E6=9D=A1=E8=A7=84=E5=88=99=E7=9A=84=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=88=86=E5=88=AB=E6=B8=B2=E6=9F=93=EF=BC=9A\r\n-=20IP=20block?= =?UTF-8?q?=20=E7=B1=BB=E5=9E=8B=EF=BC=8C=E5=B1=95=E7=A4=BA=20CIDR=20?= =?UTF-8?q?=E5=92=8C=20exceptions=20=E5=AD=97=E6=AE=B5=EF=BC=9B\r\n-=20Nam?= =?UTF-8?q?espace=20Selector=20=E7=B1=BB=E5=9E=8B=EF=BC=8C=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=20key=E3=80=81operator=E3=80=81value=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E7=BB=9F=E8=AE=A1=E9=80=89=E4=B8=AD=E4=BA=86=E5=A4=9A?= =?UTF-8?q?=E5=B0=91=E4=B8=AA=20namespace=EF=BC=9B\r\n-=20Pod=20selector?= =?UTF-8?q?=20=E7=B1=BB=E5=9E=8B=EF=BC=8C=E5=B1=95=E7=A4=BA=20key=E3=80=81?= =?UTF-8?q?operator=E3=80=81value=EF=BC=8C=E5=B9=B6=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E9=80=89=E4=B8=AD=E4=BA=86=E5=A4=9A=E5=B0=91=E4=B8=AA=20pod?= =?UTF-8?q?=E3=80=82\r\n=E5=B0=86=20ingress.ports=20=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=87=BA=E6=9D=A5=EF=BC=8C=E5=8C=85=E5=90=AB=20port=20?= =?UTF-8?q?=E5=92=8C=20protocol=20=E4=BF=A1=E6=81=AF=E3=80=82\r\n\r\n?= =?UTF-8?q?=E5=9C=A8=20egress=20=E8=AF=A6=E6=83=85=E4=B8=AD=EF=BC=8C?= =?UTF-8?q?=E6=AF=8F=E4=B8=AA=20egress=20=E7=94=A8=E4=B8=80=E4=B8=AA=20tab?= =?UTF-8?q?=20=E5=B1=95=E7=A4=BA=E3=80=82=E6=B8=B2=E6=9F=93=E6=AF=8F?= =?UTF-8?q?=E4=B8=AA=20egress=20=E6=97=B6=EF=BC=8C=E6=A0=B9=E6=8D=AE=20egr?= =?UTF-8?q?ess.to=20=E4=B8=AD=E5=90=84=E6=9D=A1=E8=A7=84=E5=88=99=E7=9A=84?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=88=86=E5=88=AB=E6=B8=B2=E6=9F=93=EF=BC=9A?= =?UTF-8?q?\r\n-=20IP=20block=20=E7=B1=BB=E5=9E=8B=EF=BC=8C=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=20CIDR=20=E5=92=8C=20exceptions=20=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=EF=BC=9B\r\n=E5=B0=86=20egress.ports=20=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=87=BA=E6=9D=A5=EF=BC=8C=E5=8C=85=E5=90=AB=20port=20?= =?UTF-8?q?=E5=92=8C=20protocol=20=E4=BF=A1=E6=81=AF=E3=80=82\r\n",=20=20?= =?UTF-8?q?=20"images":=20[=20=20=20=20=20"https://github.com/webzard-io/d?= =?UTF-8?q?ovetail-v2/assets/13651389/a4976e91-fd81-4560-8fc9-fa7695b25a92?= =?UTF-8?q?"=20=20=20]=20}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/refine/src/App.tsx | 2 + .../src/components/ShowContent/fields.tsx | 132 ++++++++++++++++++ .../src/hooks/useEagleTable/columns.tsx | 48 +++++++ .../refine/src/pages/networkpolicies/index.ts | 37 +++++ 4 files changed, 219 insertions(+) create mode 100644 packages/refine/src/pages/networkpolicies/index.ts diff --git a/packages/refine/src/App.tsx b/packages/refine/src/App.tsx index 542d4fde..5d8eed55 100644 --- a/packages/refine/src/App.tsx +++ b/packages/refine/src/App.tsx @@ -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'; @@ -66,6 +67,7 @@ function App() { ConfigMapConfig, SecretsConfig, ServicesConfig, + NetworkPolicyConfig, ]; }, []); diff --git a/packages/refine/src/components/ShowContent/fields.tsx b/packages/refine/src/components/ShowContent/fields.tsx index ce7ea255..b1a2c537 100644 --- a/packages/refine/src/components/ShowContent/fields.tsx +++ b/packages/refine/src/components/ShowContent/fields.tsx @@ -164,3 +164,135 @@ export const ServicePodsField = (_: i18n): ShowField => { }, }; }; +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 ( + ( + + {portItem.port} + {portItem.protocol} + + )} + /> + ); +}; + +const RuleList: React.FC<{ ruleType: string; data: any }> = ({ ruleType, data }) => { + switch (ruleType) { + case 'ipBlock': + return ( +
+
+ CIDR: {data.cidr} +
+ {data.except && ( +
+ Except: {data.except.join(', ')} +
+ )} +
+ ); + 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 ( + ( + + {item[0]}: {item[1]} + + )} + /> + ); + default: + return null; + } +}; + +export const IngressEgressField = (i18n: i18n): ShowField => { + return { + key: 'ingress-egress', + title: i18n.t('ingress-egress'), + path: ['rawYaml'], + render: yaml => { + const ingress = get(yaml, 'ingress', []) as Array; + const egress = get(yaml, 'egress', []) as Array; + + return ( + + + {ingress.map((ing, i) => ( + + + + {ing.from.map((fromItem, index) => { + const ruleType = fromItem.ipBlock + ? 'ipBlock' + : fromItem.namespaceSelector + ? 'namespaceSelector' + : 'podSelector'; + + return ( + + + + ); + })} + + + + + + + ))} + + + {egress.map((eg, i) => ( + + + + {eg.to + .map((toItem, index) => { + const ruleType = toItem.ipBlock + ? 'ipBlock' + : toItem.namespaceSelector + ? 'namespaceSelector' + : 'podSelector'; + + if (ruleType === 'ipBlock') { + return ( + + + + ); + } + + return null; + }) + .filter(tab => tab)} + + + + + + + ))} + + + ); + }, + }; +}; diff --git a/packages/refine/src/hooks/useEagleTable/columns.tsx b/packages/refine/src/hooks/useEagleTable/columns.tsx index 85b37fa6..0bdc7872 100644 --- a/packages/refine/src/hooks/useEagleTable/columns.tsx +++ b/packages/refine/src/hooks/useEagleTable/columns.tsx @@ -219,3 +219,51 @@ export const ServiceTypeColumnRenderer = ( 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 {ingressCount + egressCount} rules; +}; + +const PodSelector: React.FC<{ matchLabels?: object }> = ({ matchLabels }) => { + const labels = matchLabels + ? Object.entries(matchLabels) + .map(([key, value]) => `${key}: ${value}`) + .join(', ') + : 'None'; + return {labels}; +}; + +export const RulesCountColumnRenderer = (i18n: i18n): Column => { + return { + key: 'rulesCount', + display: true, + dataIndex: ['spec'], + title: i18n.t('rules_count'), + sortable: false, + render: spec => { + const ingress = get(spec, 'ingress', []); + const egress = get(spec, 'egress', []); + return ; + }, + }; +}; + +export const PodSelectorColumnRenderer = (i18n: i18n): Column => { + return { + key: 'podSelector', + display: true, + dataIndex: ['spec'], + title: i18n.t('pod_selector'), + sortable: false, + render: spec => { + const matchLabels = get(spec, 'podSelector.matchLabels', {}); + return ; + }, + }; +}; diff --git a/packages/refine/src/pages/networkpolicies/index.ts b/packages/refine/src/pages/networkpolicies/index.ts new file mode 100644 index 00000000..0d77cf98 --- /dev/null +++ b/packages/refine/src/pages/networkpolicies/index.ts @@ -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, 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, +};