diff --git a/packages/k8s-ui/src/components/resources/renderers/UDPRouteDispatch.test.tsx b/packages/k8s-ui/src/components/resources/renderers/UDPRouteDispatch.test.tsx new file mode 100644 index 0000000000..058f76fe4e --- /dev/null +++ b/packages/k8s-ui/src/components/resources/renderers/UDPRouteDispatch.test.tsx @@ -0,0 +1,66 @@ +import { renderToStaticMarkup } from "react-dom/server"; +import { expect, it } from "vitest"; +import { + ResourceRendererDispatch, + getResourceStatus, +} from "../../shared/ResourceRendererDispatch"; + +it("dispatches official UDPRoute references while leaving a colliding foreign kind generic", () => { + const props = { + resource: { kind: "udproutes", namespace: "team", name: "route" }, + onCopy: () => {}, + copied: null, + }; + const data = { + kind: "UDPRoute", + metadata: { name: "route", namespace: "team" }, + spec: { + parentRefs: [{ name: "gateway" }], + rules: [{ backendRefs: [{ name: "dns", port: 53 }] }], + }, + }; + const native = { ...data, apiVersion: "gateway.networking.k8s.io/v1alpha2" }; + const foreign = { + ...data, + apiVersion: "other.example.io/v1", + kind: "PacketPolicy", + }; + const html = renderToStaticMarkup( + , + ); + expect(html).toContain("Rules (1)"); + expect(html).toContain("Parents"); + expect(html).toContain("dns"); + expect(html).not.toContain("Specification Details"); + const other = renderToStaticMarkup( + , + ); + expect(other).toContain("Specification Details"); + expect(other).not.toContain("Rules (1)"); + expect(getResourceStatus("udproutes", native)).toMatchObject({ + text: "Unknown", + }); + const reported = { + ...native, + status: { + parents: [ + { + parentRef: { name: "gateway" }, + conditions: [ + { type: "Accepted", status: "True" }, + { type: "ResolvedRefs", status: "True" }, + ], + }, + ], + }, + }; + expect(getResourceStatus("udproutes", reported)).toMatchObject({ + text: "Accepted", + }); + expect( + getResourceStatus("udproutes", { + ...reported, + apiVersion: foreign.apiVersion, + }), + ).not.toMatchObject({ text: "Accepted" }); +}); diff --git a/packages/k8s-ui/src/components/resources/resource-utils.ts b/packages/k8s-ui/src/components/resources/resource-utils.ts index 594265a10e..a604b161ee 100644 --- a/packages/k8s-ui/src/components/resources/resource-utils.ts +++ b/packages/k8s-ui/src/components/resources/resource-utils.ts @@ -2387,7 +2387,7 @@ export function getCellFilterValue(resource: any, column: string, kind: string): if (kindLower === 'hpas' || kindLower === 'horizontalpodautoscalers') return getHPAStatus(resource).text if (kindLower === 'gateways') return getGatewayStatus(resource).text if (kindLower === 'gatewayclasses') return getGatewayClassStatus(resource).text - if (['httproutes', 'grpcroutes', 'tcproutes', 'tlsroutes', 'udproutes'].includes(kindLower)) return getRouteStatus(resource).text + if (['httproutes', 'grpcroutes', 'tcproutes', 'tlsroutes'].includes(kindLower) || (kindLower === 'udproutes' && resource.apiVersion?.startsWith('gateway.networking.k8s.io/'))) return getRouteStatus(resource).text if (kindLower === 'sealedsecrets') return getSealedSecretStatus(resource).text if (kindLower === 'poddisruptionbudgets') return getPDBStatus(resource).text if (kindLower === 'applications') return getArgoApplicationStatus(resource).text diff --git a/packages/k8s-ui/src/components/shared/ResourceRendererDispatch.tsx b/packages/k8s-ui/src/components/shared/ResourceRendererDispatch.tsx index 71d34ad87d..0b9c97b041 100644 --- a/packages/k8s-ui/src/components/shared/ResourceRendererDispatch.tsx +++ b/packages/k8s-ui/src/components/shared/ResourceRendererDispatch.tsx @@ -454,7 +454,7 @@ const KNOWN_KINDS = new Set([ 'rollouts', 'analysisruns', 'analysistemplates', 'clusteranalysistemplates', 'experiments', 'certificates', 'workflows', 'persistentvolumes', 'storageclasses', 'certificaterequests', 'clusterissuers', 'issuers', 'orders', 'challenges', - 'gateways', 'gatewayclasses', 'httproutes', 'grpcroutes', 'tcproutes', 'tlsroutes', 'udproutes', 'sealedsecrets', 'workflowtemplates', 'clusterworkflowtemplates', + 'gateways', 'gatewayclasses', 'httproutes', 'grpcroutes', 'tcproutes', 'tlsroutes', 'sealedsecrets', 'workflowtemplates', 'clusterworkflowtemplates', 'networkpolicies', 'networkpolicy', 'globalnetworkpolicies', 'globalnetworkpolicy', 'stagednetworkpolicies', 'stagednetworkpolicy', @@ -751,7 +751,10 @@ export function ResourceRendererDispatch({ && !!data?.apiVersion && !data.apiVersion.startsWith('batch/') - const isKnownKind = KNOWN_KINDS.has(kind) || isCrossplaneMR || isCrossplaneClaim || isCrossplaneXR + // Another CRD can register the udproutes plural; it keeps the generic drawer. + const isUDPRoute = kind === 'udproutes' && isApiGroup(data?.apiVersion, 'gateway.networking.k8s.io') + + const isKnownKind = KNOWN_KINDS.has(kind) || isUDPRoute || isCrossplaneMR || isCrossplaneClaim || isCrossplaneXR const JobComp = rendererOverrides?.JobRenderer ?? JobRenderer const JobSetComp = rendererOverrides?.JobSetRenderer ?? JobSetRenderer @@ -860,7 +863,7 @@ export function ResourceRendererDispatch({ {kind === 'grpcroutes' && } {kind === 'tcproutes' && } {kind === 'tlsroutes' && } - {kind === 'udproutes' && } + {isUDPRoute && } {kind === 'sealedsecrets' && } {(kind === 'workflowtemplates' || kind === 'clusterworkflowtemplates') && } {isCoreNetworkPolicy && } @@ -1191,7 +1194,7 @@ export function getResourceStatus(kind: string, data: any): { text: string; colo return getGatewayStatus(data) } if (k === 'gatewayclasses') return getGatewayClassStatus(data) - if (k === 'httproutes' || k === 'grpcroutes' || k === 'tcproutes' || k === 'tlsroutes' || k === 'udproutes') return getRouteStatus(data) + if (k === 'httproutes' || k === 'grpcroutes' || k === 'tcproutes' || k === 'tlsroutes' || (k === 'udproutes' && isApiGroup(data?.apiVersion, 'gateway.networking.k8s.io'))) return getRouteStatus(data) if (k === 'sealedsecrets') return getSealedSecretStatus(data) if (k === 'poddisruptionbudgets') return getPDBStatus(data) if (k === 'gitrepositories') return getGitRepositoryStatus(data)