diff --git a/console-extensions.json b/console-extensions.json index 4598ee3..74e5912 100644 --- a/console-extensions.json +++ b/console-extensions.json @@ -21,6 +21,30 @@ "component": { "$codeRef": "KuadrantPoliciesPage" } } }, + { + "type": "console.page/route", + "properties": { + "exact": true, + "path": "/kuadrant/policy-topology", + "component": { "$codeRef": "PolicyTopologyPage" } + } + }, + { + "type": "console.page/route", + "properties": { + "exact": true, + "path": "/k8s/ns/:ns/tlspolicy/name/:name/edit", + "component": { "$codeRef": "KuadrantTLSCreatePage" } + } + }, + { + "type": "console.page/route", + "properties": { + "exact": true, + "path": "/k8s/ns/:ns/dnspolicy/name/:name/edit", + "component": { "$codeRef": "KuadrantDNSPolicyCreatePage" } + } + }, { "type": "console.resource/create", "properties": { @@ -32,6 +56,17 @@ "component": { "$codeRef": "KuadrantDNSPolicyCreatePage" } } }, + { + "type": "console.resource/create", + "properties": { + "model": { + "group": "kuadrant.io", + "version": "v1", + "kind": "DNSPolicy" + }, + "component": { "$codeRef": "KuadrantDNSPolicyCreatePage" } + } + }, { "type": "console.resource/create", "properties": { @@ -44,11 +79,14 @@ } }, { - "type": "console.page/route", + "type": "console.resource/create", "properties": { - "exact": true, - "path": "/kuadrant/policy-topology", - "component": { "$codeRef": "PolicyTopologyPage" } + "model": { + "group": "kuadrant.io", + "version": "v1", + "kind": "RateLimitPolicy" + }, + "component": { "$codeRef": "KuadrantRateLimitPolicyCreatePage" } } }, { @@ -63,19 +101,36 @@ } }, { - "type": "console.page/route", + "type": "console.resource/create", "properties": { - "exact": true, - "path": "/k8s/ns/:ns/tlspolicy/name/:name/edit", + "model": { + "group": "kuadrant.io", + "version": "v1", + "kind": "TLSPolicy" + }, "component": { "$codeRef": "KuadrantTLSCreatePage" } } }, { - "type": "console.page/route", + "type": "console.resource/create", "properties": { - "exact": true, - "path": "/k8s/ns/:ns/dnspolicy/name/:name/edit", - "component": { "$codeRef": "KuadrantDNSPolicyCreatePage" } + "model": { + "group": "kuadrant.io", + "version": "v1beta2", + "kind": "AuthPolicy" + }, + "component": { "$codeRef": "KuadrantAuthPolicyCreatePage" } + } + }, + { + "type": "console.resource/create", + "properties": { + "model": { + "group": "kuadrant.io", + "version": "v1", + "kind": "AuthPolicy" + }, + "component": { "$codeRef": "KuadrantAuthPolicyCreatePage" } } }, { @@ -155,16 +210,5 @@ "section": "kuadrant-section-dev", "badge": "dev" } - }, - { - "type": "console.resource/create", - "properties": { - "model": { - "group": "kuadrant.io", - "version": "v1beta2", - "kind": "AuthPolicy" - }, - "component": { "$codeRef": "KuadrantAuthPolicyCreatePage" } - } } ] diff --git a/install.yaml b/install.yaml index 08329c3..efe369e 100644 --- a/install.yaml +++ b/install.yaml @@ -130,7 +130,7 @@ spec: type: ClusterIP sessionAffinity: None --- -apiVersion: console.openshift.io/v1alpha1 +apiVersion: console.openshift.io/v1 kind: ConsolePlugin metadata: name: kuadrant-console-plugin diff --git a/src/components/KuadrantAuthPolicyCreatePage.tsx b/src/components/KuadrantAuthPolicyCreatePage.tsx index fb020bb..f587404 100644 --- a/src/components/KuadrantAuthPolicyCreatePage.tsx +++ b/src/components/KuadrantAuthPolicyCreatePage.tsx @@ -3,14 +3,15 @@ import Helmet from 'react-helmet'; import { Button, Modal, ModalBox, ModalBoxHeader, ModalBoxBody, ModalBoxFooter, ButtonVariant } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; import { ResourceYAMLEditor, useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk'; +import resourceGVKMapping from '../utils/latest'; const KuadrantAuthPolicyCreatePage: React.FC = () => { const { t } = useTranslation('plugin__kuadrant-console-plugin'); const [selectedNamespace] = useActiveNamespace(); const yamlResource = { - apiVersion: 'kuadrant.io/v1beta2', - kind: 'AuthPolicy', + apiVersion: resourceGVKMapping['AuthPolicy'].group + '/' + resourceGVKMapping['AuthPolicy'].version, + kind: resourceGVKMapping['AuthPolicy'].kind, metadata: { name: 'example-authpolicy', namespace: selectedNamespace, diff --git a/src/components/KuadrantDNSPolicyCreatePage.tsx b/src/components/KuadrantDNSPolicyCreatePage.tsx index bbaa05a..2676373 100644 --- a/src/components/KuadrantDNSPolicyCreatePage.tsx +++ b/src/components/KuadrantDNSPolicyCreatePage.tsx @@ -26,6 +26,7 @@ import GatewaySelect from './gateway/GatewaySelect'; import yaml from 'js-yaml'; import KuadrantCreateUpdate from './KuadrantCreateUpdate' import { handleCancel } from '../utils/cancel'; +import resourceGVKMapping from '../utils/latest'; const KuadrantDNSPolicyCreatePage: React.FC = () => { @@ -51,8 +52,8 @@ const KuadrantDNSPolicyCreatePage: React.FC = () => { const createDNSPolicy = () => { const hasHealthCheck = healthCheck.endpoint || healthCheck.failureThreshold || healthCheck.port || healthCheck.protocol; return { - apiVersion: 'kuadrant.io/v1alpha1', - kind: 'DNSPolicy', + apiVersion: resourceGVKMapping['DNSPolicy'].group + '/' + resourceGVKMapping['DNSPolicy'].version, + kind: resourceGVKMapping['DNSPolicy'].kind, metadata: { name: policyName, namespace: selectedNamespace, @@ -86,8 +87,11 @@ const KuadrantDNSPolicyCreatePage: React.FC = () => { const [yamlInput, setYamlInput] = React.useState(createDNSPolicy) const dnsPolicy = createDNSPolicy(); - const dnsPolicyGVK = getGroupVersionKindForResource({ apiVersion: 'kuadrant.io/v1alpha1', kind: 'DNSPolicy' }); - const [dnsPolicyModel] = useK8sModel({ group: dnsPolicyGVK.group, version: dnsPolicyGVK.version, kind: dnsPolicyGVK.kind }); + const dnsPolicyGVK = getGroupVersionKindForResource({ + apiVersion: `${resourceGVKMapping['DNSPolicy'].group}/${resourceGVKMapping['DNSPolicy'].version}`, + kind: resourceGVKMapping['DNSPolicy'].kind, + }); + const [dnsPolicyModel] = useK8sModel({ group: dnsPolicyGVK.group, version: dnsPolicyGVK.version, kind: dnsPolicyGVK.kind }); const history = useHistory(); diff --git a/src/components/KuadrantOverviewPage.tsx b/src/components/KuadrantOverviewPage.tsx index 9433c9a..59e2c9f 100644 --- a/src/components/KuadrantOverviewPage.tsx +++ b/src/components/KuadrantOverviewPage.tsx @@ -35,6 +35,7 @@ import './kuadrant.css'; import ResourceList from './ResourceList'; import { sortable } from '@patternfly/react-table'; import { INTERNAL_LINKS, EXTERNAL_LINKS } from '../constants/links'; +import resourceGVKMapping from '../utils/latest'; const KuadrantOverviewPage: React.FC = () => { const { t } = useTranslation('plugin__kuadrant-console-plugin'); @@ -226,10 +227,10 @@ const KuadrantOverviewPage: React.FC = () => { { { { const { t } = useTranslation('plugin__kuadrant-console-plugin'); @@ -17,8 +18,8 @@ const KuadrantRateLimitPolicyCreatePage: React.FC = () => { const [errorModalMsg] = React.useState(''); const rateLimitPolicy = { - apiVersion: 'kuadrant.io/v1beta2', - kind: 'RateLimitPolicy', + apiVersion: resourceGVKMapping['RateLimitPolicy'].group + '/' + resourceGVKMapping['RateLimitPolicy'].version, + kind: resourceGVKMapping['RateLimitPolicy'].kind, metadata: { name: 'example-ratelimitpolicy', namespace: selectedNamespace, diff --git a/src/components/KuadrantTLSCreatePage.tsx b/src/components/KuadrantTLSCreatePage.tsx index fb93ee9..c923af3 100644 --- a/src/components/KuadrantTLSCreatePage.tsx +++ b/src/components/KuadrantTLSCreatePage.tsx @@ -36,6 +36,7 @@ import { Gateway } from './gateway/types'; import GatewaySelect from './gateway/GatewaySelect'; import KuadrantCreateUpdate from './KuadrantCreateUpdate' import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk'; +import resourceGVKMapping from '../utils/latest'; const KuadrantTLSCreatePage: React.FC = () => { @@ -59,8 +60,8 @@ const KuadrantTLSCreatePage: React.FC = () => { // Creates TLS policy object to be used for form and yaml creation of the resource const createTlsPolicy = () => ({ - apiVersion: 'kuadrant.io/v1alpha1', - kind: 'TLSPolicy', + apiVersion: resourceGVKMapping['TLSPolicy'].group + '/' + resourceGVKMapping['TLSPolicy'].version, + kind: resourceGVKMapping['TLSPolicy'].kind, metadata: { name: policyName, namespace: selectedNamespace, @@ -87,7 +88,10 @@ const KuadrantTLSCreatePage: React.FC = () => { }) const tlsPolicy = createTlsPolicy(); - const tlsPolicyGVK = getGroupVersionKindForResource({ apiVersion: 'kuadrant.io/v1alpha1', kind: 'TLSPolicy' }); + const tlsPolicyGVK = getGroupVersionKindForResource({ + apiVersion: `${resourceGVKMapping['TLSPolicy'].group}/${resourceGVKMapping['TLSPolicy'].version}`, + kind: resourceGVKMapping['TLSPolicy'].kind, + }); const [tlsPolicyModel] = useK8sModel({ group: tlsPolicyGVK.group, version: tlsPolicyGVK.version, kind: tlsPolicyGVK.kind }); // K8sResourceCommon by default does not contain spec etc which is needed for updating resource forms diff --git a/src/components/PolicyTopologyPage.tsx b/src/components/PolicyTopologyPage.tsx index fecf6a6..58b463c 100644 --- a/src/components/PolicyTopologyPage.tsx +++ b/src/components/PolicyTopologyPage.tsx @@ -39,6 +39,7 @@ import { import { CubesIcon, CloudUploadAltIcon, TopologyIcon, RouteIcon } from '@patternfly/react-icons'; import * as dot from 'graphlib-dot'; import './kuadrant.css'; +import resourceGVKMapping from '../utils/latest'; // Fetch the config.js file dynamically at runtime // Normally served from /api/plugins/kuadrant-console/config.js @@ -76,19 +77,6 @@ export const kindToAbbr = (kind: string) => { return (kind.replace(/[^A-Z]/g, '') || kind.toUpperCase()).slice(0, 4); }; -// TODO: need a generic way to fetch latest versions of resources based on kind + group -const resourceGVKMapping: { [key: string]: { group: string; version: string; kind: string } } = { - Gateway: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'Gateway' }, - HTTPRoute: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'HTTPRoute' }, - TLSPolicy: { group: 'kuadrant.io', version: 'v1alpha1', kind: 'TLSPolicy' }, - DNSPolicy: { group: 'kuadrant.io', version: 'v1alpha1', kind: 'DNSPolicy' }, - AuthPolicy: { group: 'kuadrant.io', version: 'v1beta2', kind: 'AuthPolicy' }, - RateLimitPolicy: { group: 'kuadrant.io', version: 'v1beta2', kind: 'RateLimitPolicy' }, - ConfigMap: { group: '', version: 'v1', kind: 'ConfigMap' }, - Listener: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'Listener' }, - GatewayClass: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'GatewayClass' }, -}; - // Convert DOT graph to PatternFly node/edge models const parseDotToModel = (dotString: string): { nodes: any[]; edges: any[] } => { try { @@ -112,6 +100,18 @@ const parseDotToModel = (dotString: string): { nodes: any[]; edges: any[] } => { const connectedNodeIds = new Set(); + // Excluded resource kinds + const excludedKinds = ['Issuer', 'ClusterIssuer', 'WasmPlugin']; + + // Define separate groups + const unassociatedPolicies = new Set([ + 'TLSPolicy', + 'DNSPolicy', + 'AuthPolicy', + 'RateLimitPolicy', + ]); + const kuadrantInternals = new Set(['ConfigMap', 'WasmPlugin']); + graph.edges().forEach((edge) => { const sourceNode = graph.node(edge.v); const targetNode = graph.node(edge.w); @@ -121,9 +121,7 @@ const parseDotToModel = (dotString: string): { nodes: any[]; edges: any[] } => { connectedNodeIds.add(edge.v); connectedNodeIds.add(edge.w); - const isPolicy = ['TLSPolicy', 'DNSPolicy', 'AuthPolicy', 'RateLimitPolicy'].includes( - sourceNode.type, - ); + const isPolicy = unassociatedPolicies.has(sourceNode.type); edges.push({ id: `edge-${edge.v}-${edge.w}`, @@ -143,6 +141,9 @@ const parseDotToModel = (dotString: string): { nodes: any[]; edges: any[] } => { const nodeData = graph.node(nodeId); const [resourceType, resourceName] = nodeData.label.split('\\n'); + // Exclude Issuer and ClusterIssuer resources + if (excludedKinds.includes(resourceType)) return; + nodes.push({ id: nodeId, type: 'node', @@ -161,14 +162,34 @@ const parseDotToModel = (dotString: string): { nodes: any[]; edges: any[] } => { }); }); - const unconnectedNodes = nodes.filter((node) => !connectedNodeIds.has(node.id)); - if (unconnectedNodes.length > 0) { + // Group unconnected resources + const unassociatedPolicyNodes = nodes.filter( + (node) => !connectedNodeIds.has(node.id) && unassociatedPolicies.has(node.resourceType), + ); + if (unassociatedPolicyNodes.length > 0) { + groups.push({ + id: 'group-unattached', + children: unassociatedPolicyNodes.map((node) => node.id), + type: 'group', + group: true, + label: 'Unattached Policies', + style: { + padding: 40, + }, + }); + } + + // Group Kuadrant Internals (ConfigMap) + const kuadrantInternalNodes = nodes.filter( + (node) => !connectedNodeIds.has(node.id) && kuadrantInternals.has(node.resourceType), + ); + if (kuadrantInternalNodes.length > 0) { groups.push({ - id: 'group-unconnected', - children: unconnectedNodes.map((node) => node.id), + id: 'group-kuadrant-internals', + children: kuadrantInternalNodes.map((node) => node.id), type: 'group', group: true, - label: 'Unassociated Policies and Resources', + label: 'Kuadrant Internals', style: { padding: 40, }, @@ -190,6 +211,7 @@ const CustomNode: React.FC = ({ onContextMenu, contextMenuOpen, }) => { + const excludedKinds = ['GatewayClass', 'HTTPRouteRule', 'Listener']; const data = element.getData(); const { type, badge, badgeColor } = data; @@ -214,6 +236,9 @@ const CustomNode: React.FC = ({ case 'GatewayClass': IconComponent = CubesIcon; break; + case 'HttpRouteRule': + IconComponent = RouteIcon; + break; default: IconComponent = TopologyIcon; break; @@ -235,8 +260,9 @@ const CustomNode: React.FC = ({ onSelect={onSelect} selected={selected} className={isPolicyNode ? 'policy-node' : ''} - onContextMenu={onContextMenu} - contextMenuOpen={contextMenuOpen} + // Disable context menu for excluded kinds + onContextMenu={!excludedKinds.includes(type) ? onContextMenu : undefined} + contextMenuOpen={!excludedKinds.includes(type) && contextMenuOpen} > @@ -292,7 +318,7 @@ const goToResource = (resourceType: string, resourceName: string) => { const customLayoutFactory = (type: string, graph: any): any => { return new DagreLayout(graph, { rankdir: 'TB', - nodesep: 60, + nodesep: 0, ranksep: 0, nodeDistance: 80, }); diff --git a/src/utils/latest.tsx b/src/utils/latest.tsx new file mode 100644 index 0000000..b50a659 --- /dev/null +++ b/src/utils/latest.tsx @@ -0,0 +1,14 @@ +const resourceGVKMapping: { [key: string]: { group: string; version: string; kind: string } } = { + Gateway: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'Gateway' }, + HTTPRoute: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'HTTPRoute' }, + TLSPolicy: { group: 'kuadrant.io', version: 'v1alpha1', kind: 'TLSPolicy' }, + DNSPolicy: { group: 'kuadrant.io', version: 'v1alpha1', kind: 'DNSPolicy' }, + AuthPolicy: { group: 'kuadrant.io', version: 'v1beta2', kind: 'AuthPolicy' }, + RateLimitPolicy: { group: 'kuadrant.io', version: 'v1beta2', kind: 'RateLimitPolicy' }, + ConfigMap: { group: '', version: 'v1', kind: 'ConfigMap' }, + Listener: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'Listener' }, + GatewayClass: { group: 'gateway.networking.k8s.io', version: 'v1', kind: 'GatewayClass' }, + WasmPlugin: { group: 'extensions.istio.io', version: 'v1alpha1', kind: 'WasmPlugin' }, +}; + +export default resourceGVKMapping;