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..df90cc38 100644 --- a/packages/refine/src/components/ShowContent/fields.tsx +++ b/packages/refine/src/components/ShowContent/fields.tsx @@ -164,3 +164,109 @@ export const ServicePodsField = (_: i18n): ShowField => { }, }; }; +import { List, Tabs, Table } from 'antd'; +import { get } from 'lodash'; +import { + NetworkPolicyEgressRule, + NetworkPolicyIngressRule, +} from 'kubernetes-types/networking/v1'; + +const { TabPane } = Tabs; + +// IP block renderer +const IPBlockRenderer: React.FC<{ data: { cidr: string; except?: string[] } }> = ({ + data, +}) => ( + <> +
CIDR: {data.cidr}
+ {data.except && ( +
+ Except: + {item}} + /> +
+ )} + +); + +// Namespace selector renderer +export const NamespaceSelectorField = (i18n: i18n): ShowField => ({ + key: 'namespaceSelector', + title: i18n.t('namespaceSelector'), + path: ['rawYaml', 'ingress', 'from', 'namespaceSelector'], + render: selector => { + // Table data will be fetched and provided based on the `selector` criteria + const columns = [ + { key: 'key', title: 'Key', dataIndex: 'key' }, + { key: 'operator', title: 'Operator', dataIndex: 'operator' }, + { key: 'value', title: 'Value', dataIndex: 'value' }, + ]; + return ( + + ); + }, +}); + +// Pod selector renderer +export const PodSelectorField = (i18n: i18n): ShowField => ({ + key: 'podSelector', + title: i18n.t('podSelector'), + path: ['rawYaml', 'ingress', 'from', 'podSelector'], + render: selector => { + // Table data will be fetched and provided based on the `selector` criteria + const columns = [ + { key: 'key', title: 'Key', dataIndex: 'key' }, + { key: 'operator', title: 'Operator', dataIndex: 'operator' }, + { key: 'value', title: 'Value', dataIndex: 'value' }, + ]; + return ( +
+ ); + }, +}); + +// Ingress ports renderer +export const IngressPortsField = (i18n: i18n): ShowField => ({ + key: 'ingressPorts', + title: i18n.t('ingressPorts'), + path: ['rawYaml', 'ingress', 'ports'], + render: ports => ( + ( + + Port: {port.port}, Protocol: {port.protocol} + + )} + /> + ), +}); + +// Egress ports renderer +export const EgressPortsField = (i18n: i18n): ShowField => ({ + key: 'egressPorts', + title: i18n.t('egressPorts'), + path: ['rawYaml', 'egress', 'ports'], + render: ports => ( + ( + + Port: {port.port}, Protocol: {port.protocol} + + )} + /> + ), +}); diff --git a/packages/refine/src/hooks/useEagleTable/columns.tsx b/packages/refine/src/hooks/useEagleTable/columns.tsx index 85b37fa6..4599ea6e 100644 --- a/packages/refine/src/hooks/useEagleTable/columns.tsx +++ b/packages/refine/src/hooks/useEagleTable/columns.tsx @@ -219,3 +219,56 @@ export const ServiceTypeColumnRenderer = ( sorter: CommonSorter(dataIndex), }; }; +import { NetworkPolicy } from 'kubernetes-types/networking/v1'; + +export const CountRulesColumnRenderer = (i18n: i18n): Column => { + return { + key: 'countRules', + display: true, + dataIndex: ['rawYaml'], + title: i18n.t('count_rules'), + sortable: true, + sorter: (a, b) => { + const aPolicy: NetworkPolicy = a.rawYaml; + const bPolicy: NetworkPolicy = b.rawYaml; + const aCount = + (get(aPolicy, 'spec.ingress', []).length || 0) + + (get(aPolicy, 'spec.egress', []).length || 0); + const bCount = + (get(bPolicy, 'spec.ingress', []).length || 0) + + (get(bPolicy, 'spec.egress', []).length || 0); + return aCount - bCount; + }, + render: (value: NetworkPolicy) => { + const ingressCount = get(value, 'spec.ingress', []).length; + const egressCount = get(value, 'spec.egress', []).length; + return {ingressCount + egressCount}; + }, + }; +}; + +export const PodSelectorColumnRenderer = (i18n: i18n): Column => { + return { + key: 'podSelector', + display: true, + dataIndex: ['rawYaml'], + title: i18n.t('pod_selector'), + sortable: true, + sorter: (a, b) => { + // For sorting purposes, converting labels object to string + const aPolicy: NetworkPolicy = a.rawYaml; + const bPolicy: NetworkPolicy = b.rawYaml; + const aSelectorString = JSON.stringify( + get(aPolicy, 'spec.podSelector.matchLabels', {}) + ); + const bSelectorString = JSON.stringify( + get(bPolicy, 'spec.podSelector.matchLabels', {}) + ); + return aSelectorString.localeCompare(bSelectorString); + }, + render: (value: NetworkPolicy) => { + const selectors = get(value, 'spec.podSelector.matchLabels', null); + return {selectors ? JSON.stringify(selectors) : i18n.t('none')}; + }, + }; +}; diff --git a/packages/refine/src/pages/networkpolicies/index.ts b/packages/refine/src/pages/networkpolicies/index.ts new file mode 100644 index 00000000..cd2328a5 --- /dev/null +++ b/packages/refine/src/pages/networkpolicies/index.ts @@ -0,0 +1,49 @@ +import { CountRulesColumnRenderer } from '../../hooks/useEagleTable/columns'; +import { PodSelectorColumnRenderer } from '../../hooks/useEagleTable/columns'; +import { NamespaceSelectorField } from '../../components/ShowContent/fields'; +import { PodSelectorField } from '../../components/ShowContent/fields'; +import { IngressPortsField } from '../../components/ShowContent/fields'; +import { EgressPortsField } 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.k8s.io/v1'; + +const NETWORKPOLICY_INIT_VALUE = { + apiVersion: 'networking.k8s.io/v1', + kind: 'NetworkPolicy', + metadata: { + name: '', + namespace: 'default', + annotations: {}, + labels: {}, + }, + spec: { + podSelector: {}, + policyTypes: ['Ingress', 'Egress'], + }, +}; + +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) => [ + CountRulesColumnRenderer(i18n), + PodSelectorColumnRenderer(i18n), + ], + showFields: (i18n: i18n) => [ + [], + [], + [ + NamespaceSelectorField(i18n), + PodSelectorField(i18n), + IngressPortsField(i18n), + EgressPortsField(i18n), + ], + ], + initValue: NETWORKPOLICY_INIT_VALUE, +};