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)