diff --git a/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarClosed.stories.storyshot b/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarClosed.stories.storyshot
index 0b4b6ee9318..4887c8279b3 100644
--- a/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarClosed.stories.storyshot
+++ b/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarClosed.stories.storyshot
@@ -592,6 +592,138 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarOpen.stories.storyshot b/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarOpen.stories.storyshot
index 1060221ac96..d9b1461831f 100644
--- a/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarOpen.stories.storyshot
+++ b/frontend/src/components/Sidebar/__snapshots__/Sidebar.InClusterSidebarOpen.stories.storyshot
@@ -627,6 +627,145 @@
+
+
+
+
+
+ Gateway
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Sidebar/__snapshots__/Sidebar.SelectedItemWithSidebarOmitted.stories.storyshot b/frontend/src/components/Sidebar/__snapshots__/Sidebar.SelectedItemWithSidebarOmitted.stories.storyshot
index 24473bbe9cb..8c4d7b09d1b 100644
--- a/frontend/src/components/Sidebar/__snapshots__/Sidebar.SelectedItemWithSidebarOmitted.stories.storyshot
+++ b/frontend/src/components/Sidebar/__snapshots__/Sidebar.SelectedItemWithSidebarOmitted.stories.storyshot
@@ -627,6 +627,145 @@
+
+
+
+
+
+ Gateway
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/Sidebar/prepareRoutes.ts b/frontend/src/components/Sidebar/prepareRoutes.ts
index 9216abeee84..7b08045723c 100644
--- a/frontend/src/components/Sidebar/prepareRoutes.ts
+++ b/frontend/src/components/Sidebar/prepareRoutes.ts
@@ -166,6 +166,29 @@ function prepareRoutes(
},
],
},
+ {
+ name: 'gateway',
+ label: t('glossary|Gateway'),
+ icon: 'mdi:lan-connect',
+ subList: [
+ {
+ name: 'k8sgateways',
+ label: t('glossary|Gateways'),
+ },
+ {
+ name: 'gatewayclasses',
+ label: t('glossary|Gateway Classes'),
+ },
+ {
+ name: 'httproutes',
+ label: t('glossary|HTTP Routes'),
+ },
+ {
+ name: 'grpcroutes',
+ label: t('glossary|GRPC Routes'),
+ },
+ ],
+ },
{
name: 'security',
label: t('glossary|Security'),
diff --git a/frontend/src/components/gateway/ClassDetails.tsx b/frontend/src/components/gateway/ClassDetails.tsx
new file mode 100644
index 00000000000..a35e1e88035
--- /dev/null
+++ b/frontend/src/components/gateway/ClassDetails.tsx
@@ -0,0 +1,38 @@
+import { useTranslation } from 'react-i18next';
+import { useParams } from 'react-router-dom';
+import GatewayClass, { KubeGatewayClass } from '../../lib/k8s/gatewayClass';
+import { ConditionsTable, DetailsGrid } from '../common/Resource';
+import SectionBox from '../common/SectionBox';
+
+export default function GatewayClassDetails() {
+ const { name } = useParams<{ name: string }>();
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ return (
+
+ gatewayClass && [
+ {
+ name: t('Controller Name'),
+ value: gatewayClass.controllerName,
+ },
+ ]
+ }
+ extraSections={(item: KubeGatewayClass) =>
+ item && [
+ {
+ id: 'headlamp.gatewayclass-conditions',
+ section: (
+
+
+
+ ),
+ },
+ ]
+ }
+ />
+ );
+}
diff --git a/frontend/src/components/gateway/ClassList.tsx b/frontend/src/components/gateway/ClassList.tsx
new file mode 100644
index 00000000000..8d0696d1570
--- /dev/null
+++ b/frontend/src/components/gateway/ClassList.tsx
@@ -0,0 +1,75 @@
+import { Box } from '@mui/material';
+import { useTranslation } from 'react-i18next';
+import GatewayClass from '../../lib/k8s/gatewayClass';
+import { LightTooltip, StatusLabel, StatusLabelProps } from '../common';
+import ResourceListView from '../common/Resource/ResourceListView';
+
+export function makeGatewayStatusLabel(conditions: any[] | null) {
+ if (!conditions) {
+ return null;
+ }
+
+ const conditionOptions = {
+ Accepted: {
+ status: 'success',
+ icon: 'mdi:check-bold',
+ },
+ };
+
+ const condition = conditions.find(
+ ({ status, type }: { status: string; type: string }) =>
+ type in conditionOptions && status === 'True'
+ );
+
+ if (!condition) {
+ return null;
+ }
+
+ const tooltip = '';
+
+ const conditionInfo = conditionOptions[condition.type as 'Accepted'];
+
+ return (
+
+
+
+ {condition.type}
+
+
+
+ );
+}
+
+export default function GatewayClassList() {
+ const { t } = useTranslation('glossary');
+
+ return (
+ gatewayClass.spec?.controllerName,
+ },
+ {
+ id: 'conditions',
+ label: t('translation|Conditions'),
+ getValue: (gatewayClass: GatewayClass) =>
+ gatewayClass.status?.conditions?.find(
+ ({ status }: { status: string }) => status === 'True'
+ ) ?? null,
+ render: (gatewayClass: GatewayClass) =>
+ makeGatewayStatusLabel(gatewayClass.status?.conditions),
+ },
+ 'age',
+ ]}
+ />
+ );
+}
diff --git a/frontend/src/components/gateway/GRPCRouteDetails.tsx b/frontend/src/components/gateway/GRPCRouteDetails.tsx
new file mode 100644
index 00000000000..0cd521c3ec9
--- /dev/null
+++ b/frontend/src/components/gateway/GRPCRouteDetails.tsx
@@ -0,0 +1,67 @@
+import { useTranslation } from 'react-i18next';
+import { useParams } from 'react-router-dom';
+import GRPCRoute from '../../lib/k8s/grpcRoute';
+import { GatewayParentReference } from '../../lib/k8s/httpRoute';
+import { Link, SimpleTable } from '../common';
+import { DetailsGrid } from '../common/Resource';
+import SectionBox from '../common/SectionBox';
+
+export default function GRPCRouteDetails(props: { name?: string; namespace?: string }) {
+ const params = useParams<{ namespace: string; name: string }>();
+ const { name = params.name, namespace = params.namespace } = props;
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ return (
+
+ item && [
+ {
+ id: 'headlamp.grpcroute-parentrefs',
+ section: (
+
+ (
+
+ {data.name}
+
+ ),
+ },
+ {
+ label: t('translation|Namespace'),
+ getter: (data: GatewayParentReference) => data.namespace,
+ },
+ {
+ label: t('translation|Kind'),
+ getter: (data: GatewayParentReference) => data.kind,
+ },
+ {
+ label: t('translation|Group'),
+ getter: (data: GatewayParentReference) => data.group,
+ },
+ ]}
+ data={item?.parentRefs || []}
+ reflectInURL="listeners"
+ />
+
+ ),
+ },
+ ]
+ }
+ />
+ );
+}
diff --git a/frontend/src/components/gateway/GRPCRouteList.tsx b/frontend/src/components/gateway/GRPCRouteList.tsx
new file mode 100644
index 00000000000..6472010741a
--- /dev/null
+++ b/frontend/src/components/gateway/GRPCRouteList.tsx
@@ -0,0 +1,25 @@
+import { useTranslation } from 'react-i18next';
+import GRPCRoute from '../../lib/k8s/grpcRoute';
+import ResourceListView from '../common/Resource/ResourceListView';
+
+export default function GRPCRouteList() {
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ return (
+ httpRoute.spec.rules.length,
+ },
+ 'age',
+ ]}
+ />
+ );
+}
diff --git a/frontend/src/components/gateway/GatewayDetails.tsx b/frontend/src/components/gateway/GatewayDetails.tsx
new file mode 100644
index 00000000000..e6574e4d469
--- /dev/null
+++ b/frontend/src/components/gateway/GatewayDetails.tsx
@@ -0,0 +1,134 @@
+import Box from '@mui/system/Box';
+import { useTranslation } from 'react-i18next';
+import { useParams } from 'react-router-dom';
+import { KubeCondition } from '../../lib/k8s/cluster';
+import Gateway, {
+ GatewayAddress,
+ GatewayListener,
+ GatewayListenerStatus,
+} from '../../lib/k8s/gateway';
+import { EmptyContent, StatusLabel, StatusLabelProps } from '../common';
+import Link from '../common/Link';
+import { ConditionsTable, DetailsGrid } from '../common/Resource';
+import SectionBox from '../common/SectionBox';
+import SimpleTable, { NameValueTable } from '../common/SimpleTable';
+
+function GatewayListenerTable(props: {
+ listener: GatewayListener;
+ status: GatewayListenerStatus | null;
+}) {
+ const { listener, status } = props;
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ function makeStatusLabel(condition: KubeCondition) {
+ let status: StatusLabelProps['status'] = '';
+ if (condition.type === 'Available') {
+ status = condition.status === 'True' ? 'success' : 'error';
+ }
+
+ return (
+ ({ paddingRight: theme.spacing(1) })}>
+ {condition.type}
+
+ );
+ }
+ const mainRows = [
+ {
+ name: listener.name,
+ withHighlightStyle: true,
+ },
+ {
+ name: t('translation|Hostname'),
+ value: listener.hostname,
+ },
+ {
+ name: t('translation|Protocol'),
+ value: listener.protocol,
+ },
+ {
+ name: t('translation|Conditions'),
+ value: status?.conditions.map(c => makeStatusLabel(c)),
+ },
+ ];
+ return ;
+}
+
+export default function GatewayDetails(props: { name?: string; namespace?: string }) {
+ const params = useParams<{ namespace: string; name: string }>();
+ const { name = params.name, namespace = params.namespace } = props;
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ return (
+
+ gateway && [
+ {
+ name: t('Class Name'),
+ value: gateway.spec?.gatewayClassName ? (
+
+ {gateway.spec?.gatewayClassName}
+
+ ) : null,
+ },
+ ]
+ }
+ extraSections={(item: Gateway) =>
+ item && [
+ {
+ id: 'headlamp.gateway-addresses',
+ section: item && (
+
+ data.type,
+ },
+ {
+ label: t('translation|Value'),
+ getter: (data: GatewayAddress) => data.value,
+ },
+ ]}
+ data={item?.getAddresses() || []}
+ reflectInURL="addresses"
+ />
+
+ ),
+ },
+ {
+ id: 'headlamp.gateway-listeners',
+ section: item && (
+
+ {item.getListeners().length === 0 ? (
+ {t('No data in this config map')}
+ ) : (
+ item
+ .getListeners()
+ .map((listener: GatewayListener) => (
+
+ ))
+ )}
+
+ ),
+ },
+ {
+ id: 'headlamp.gateway-conditions',
+ section: (
+
+
+
+ ),
+ },
+ ]
+ }
+ />
+ );
+}
diff --git a/frontend/src/components/gateway/GatewayList.tsx b/frontend/src/components/gateway/GatewayList.tsx
new file mode 100644
index 00000000000..94fb4d1ee99
--- /dev/null
+++ b/frontend/src/components/gateway/GatewayList.tsx
@@ -0,0 +1,47 @@
+import { useTranslation } from 'react-i18next';
+import Gateway from '../../lib/k8s/gateway';
+import Link from '../common/Link';
+import ResourceListView from '../common/Resource/ResourceListView';
+import { makeGatewayStatusLabel } from './ClassList';
+
+export default function GatewayList() {
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ return (
+ gateway.spec?.gatewayClassName,
+ render: gateway =>
+ gateway.spec?.gatewayClassName ? (
+
+ {gateway.spec?.gatewayClassName}
+
+ ) : null,
+ },
+ {
+ id: 'conditions',
+ label: t('translation|Conditions'),
+ getValue: (gateway: Gateway) =>
+ gateway.status?.conditions?.find(
+ ({ status }: { status: string }) => status === 'True'
+ ) ?? null,
+ render: (gateway: Gateway) => makeGatewayStatusLabel(gateway.status?.conditions),
+ },
+ {
+ id: 'listeners',
+ label: t('translation|Listeners'),
+ getValue: (gateway: Gateway) => gateway.spec.listeners.length,
+ },
+ 'age',
+ ]}
+ />
+ );
+}
diff --git a/frontend/src/components/gateway/HTTPRouteDetails.tsx b/frontend/src/components/gateway/HTTPRouteDetails.tsx
new file mode 100644
index 00000000000..cf2c6412b28
--- /dev/null
+++ b/frontend/src/components/gateway/HTTPRouteDetails.tsx
@@ -0,0 +1,112 @@
+import { useTranslation } from 'react-i18next';
+import { useParams } from 'react-router-dom';
+import { GatewayParentReference } from '../../lib/k8s/gateway';
+import HTTPRoute, { HTTPRouteRule } from '../../lib/k8s/httpRoute';
+import { EmptyContent, LabelListItem, Link, NameValueTable, SimpleTable } from '../common';
+import { DetailsGrid } from '../common/Resource';
+import SectionBox from '../common/SectionBox';
+
+function HTTPRouteRuleTable(props: { rule: HTTPRouteRule }) {
+ const { rule } = props;
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ const mainRows = [
+ {
+ name: t('translation|BackendRefs'),
+ value: rule.backendRefs?.length,
+ hide: (rule.backendRefs?.length || 0) === 0,
+ },
+ {
+ name: t('translation|Matches'),
+ value: rule.matches?.length,
+ hide: (rule.matches?.length || 0) === 0,
+ },
+ ];
+ return ;
+}
+
+export default function HTTPRouteDetails(props: { name?: string; namespace?: string }) {
+ const params = useParams<{ namespace: string; name: string }>();
+ const { name = params.name, namespace = params.namespace } = props;
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ return (
+
+ httpRoute && [
+ {
+ name: 'Hostnames',
+ value: `${tls}`)} />,
+ },
+ ]
+ }
+ withEvents
+ extraSections={(item: HTTPRoute) =>
+ item && [
+ {
+ id: 'headlamp.httproute-rules',
+ section: item && (
+
+ {item.rules.length === 0 ? (
+ {t('No data in this config map')}
+ ) : (
+ item.rules.map((rule: HTTPRouteRule, index: any) => (
+
+ ))
+ )}
+
+ ),
+ },
+ {
+ id: 'headlamp.httproute-parentrefs',
+ section: (
+
+ (
+
+ {data.name}
+
+ ),
+ },
+ {
+ label: t('translation|Namespace'),
+ getter: (data: GatewayParentReference) => data.namespace,
+ },
+ {
+ label: t('translation|Kind'),
+ getter: (data: GatewayParentReference) => data.kind,
+ },
+ {
+ label: t('translation|Group'),
+ getter: (data: GatewayParentReference) => data.group,
+ },
+ {
+ label: t('translation|Section Name'),
+ getter: (data: GatewayParentReference) => data.sectionName,
+ },
+ ]}
+ data={item?.parentRefs || []}
+ reflectInURL="listeners"
+ />
+
+ ),
+ },
+ ]
+ }
+ />
+ );
+}
diff --git a/frontend/src/components/gateway/HTTPRouteList.tsx b/frontend/src/components/gateway/HTTPRouteList.tsx
new file mode 100644
index 00000000000..654c702ded4
--- /dev/null
+++ b/frontend/src/components/gateway/HTTPRouteList.tsx
@@ -0,0 +1,34 @@
+import { useTranslation } from 'react-i18next';
+import HTTPRoute from '../../lib/k8s/httpRoute';
+import { LabelListItem } from '../common';
+import ResourceListView from '../common/Resource/ResourceListView';
+
+export default function HTTPRouteList() {
+ const { t } = useTranslation(['glossary', 'translation']);
+
+ return (
+ httpRoute.hostnames.join(''),
+ render: httpRoute => (
+ host || '*')} />
+ ),
+ },
+ {
+ id: 'rules',
+ label: t('translation|rules'),
+ getValue: (httpRoute: HTTPRoute) => httpRoute.spec.rules.length,
+ },
+ 'age',
+ ]}
+ />
+ );
+}
diff --git a/frontend/src/lib/k8s/gateway.ts b/frontend/src/lib/k8s/gateway.ts
new file mode 100644
index 00000000000..b2ede58dbfa
--- /dev/null
+++ b/frontend/src/lib/k8s/gateway.ts
@@ -0,0 +1,79 @@
+import { KubeCondition } from './cluster';
+import { KubeObject, KubeObjectInterface } from './KubeObject';
+
+export interface GatewayParentReference {
+ group: string;
+ kind: string;
+ namespace: string;
+ sectionName: string | null;
+ name: string;
+ [key: string]: any;
+}
+
+export interface GatewayListener {
+ hostname: string;
+ name: string;
+ protocol: string;
+ [key: string]: any;
+}
+export interface GatewayListenerStatus {
+ name: string;
+ conditions: KubeCondition[];
+ [key: string]: any;
+}
+export interface GatewayAddress {
+ type: string;
+ value: string;
+}
+
+export interface KubeGateway extends KubeObjectInterface {
+ spec: {
+ gatewayClassName?: string;
+ listeners: GatewayListener[];
+ [key: string]: any;
+ };
+ status: {
+ addresses: GatewayAddress[];
+ listeners: GatewayListenerStatus[];
+ [otherProps: string]: any;
+ };
+}
+
+class Gateway extends KubeObject {
+ static kind = 'Gateway';
+ static apiName = 'gateways';
+ static apiVersion = 'gateway.networking.k8s.io/v1beta1';
+ static isNamespaced = true;
+
+ get spec(): KubeGateway['spec'] {
+ return this.jsonData.spec;
+ }
+
+ get status() {
+ return this.jsonData.status;
+ }
+
+ getListeners(): GatewayListener[] {
+ return this.jsonData.spec.listeners;
+ }
+
+ getAddresses(): GatewayAddress[] {
+ return this.jsonData.status.addresses;
+ }
+
+ getListernerStatusByName(name: string): GatewayListenerStatus | null {
+ return this.jsonData.status.listeners.find(t => t.name === name) || null;
+ }
+
+ static get pluralName() {
+ return 'gateways';
+ }
+ get listRoute(): string {
+ return 'k8sgateways'; // fix magic name gateway
+ }
+ get detailsRoute(): string {
+ return 'k8sgateway'; // fix magic name gateway
+ }
+}
+
+export default Gateway;
diff --git a/frontend/src/lib/k8s/gatewayClass.ts b/frontend/src/lib/k8s/gatewayClass.ts
new file mode 100644
index 00000000000..f8822357086
--- /dev/null
+++ b/frontend/src/lib/k8s/gatewayClass.ts
@@ -0,0 +1,40 @@
+import { KubeObject, KubeObjectInterface } from './KubeObject';
+
+export interface KubeGatewayClass extends KubeObjectInterface {
+ spec: {
+ controllerName: string;
+ [key: string]: any;
+ };
+ status: {
+ [otherProps: string]: any;
+ };
+}
+
+class GatewayClass extends KubeObject {
+ static kind = 'GatewayClass';
+ static apiName = 'gatewayclasses';
+ static apiVersion = 'gateway.networking.k8s.io/v1beta1';
+ static isNamespaced = false;
+
+ get spec(): KubeGatewayClass['spec'] {
+ return this.jsonData.spec;
+ }
+
+ get status() {
+ return this.jsonData.status;
+ }
+
+ get controllerName() {
+ return this.spec!.controllerName;
+ }
+
+ static get listRoute() {
+ return 'GatewayClasses';
+ }
+
+ static get pluralName() {
+ return 'GatewayClasses';
+ }
+}
+
+export default GatewayClass;
diff --git a/frontend/src/lib/k8s/grpcRoute.ts b/frontend/src/lib/k8s/grpcRoute.ts
new file mode 100644
index 00000000000..fd6d5413804
--- /dev/null
+++ b/frontend/src/lib/k8s/grpcRoute.ts
@@ -0,0 +1,29 @@
+import { GatewayParentReference } from './gateway';
+import { KubeObject, KubeObjectInterface } from './KubeObject';
+
+export interface KubeGRPCRoute extends KubeObjectInterface {
+ spec: {
+ parentRefs: GatewayParentReference[];
+ [key: string]: any;
+ };
+}
+
+class GRPCRoute extends KubeObject {
+ static kind = 'GRPCRoute';
+ static apiName = 'grpcroutes';
+ static apiVersion = 'gateway.networking.k8s.io/v1beta1';
+ static isNamespaced = true;
+
+ get spec(): KubeGRPCRoute['spec'] {
+ return this.jsonData.spec;
+ }
+ get parentRefs(): GatewayParentReference[] {
+ return this.jsonData.spec.parentRefs;
+ }
+
+ static get pluralName() {
+ return 'grpcroutes';
+ }
+}
+
+export default GRPCRoute;
diff --git a/frontend/src/lib/k8s/httpRoute.ts b/frontend/src/lib/k8s/httpRoute.ts
new file mode 100644
index 00000000000..ade384acb01
--- /dev/null
+++ b/frontend/src/lib/k8s/httpRoute.ts
@@ -0,0 +1,46 @@
+import { GatewayParentReference } from './gateway';
+import { KubeObject, KubeObjectInterface } from './KubeObject';
+
+export interface HTTPRouteRule {
+ backendRefs: any[] | null;
+ matches: any[] | null;
+ [key: string]: any;
+}
+
+export interface KubeHTTPRoute extends KubeObjectInterface {
+ spec: {
+ hostnames: string[];
+ parentRefs: GatewayParentReference[];
+ rules: HTTPRouteRule[];
+ [key: string]: any;
+ };
+}
+
+class HTTPRoute extends KubeObject {
+ static kind = 'HTTPRoute';
+ static apiName = 'httproutes';
+ static apiVersion = 'gateway.networking.k8s.io/v1beta1';
+ static isNamespaced = true;
+
+ get spec(): KubeHTTPRoute['spec'] {
+ return this.jsonData.spec;
+ }
+
+ get hostnames(): string[] {
+ return this.jsonData.spec.hostnames;
+ }
+
+ get rules(): HTTPRouteRule[] {
+ return this.jsonData.spec.rules;
+ }
+
+ get parentRefs(): GatewayParentReference[] {
+ return this.jsonData.spec.parentRefs;
+ }
+
+ static get pluralName() {
+ return 'httproutes';
+ }
+}
+
+export default HTTPRoute;
diff --git a/frontend/src/lib/router.tsx b/frontend/src/lib/router.tsx
index dd533784b25..47fcacdc190 100644
--- a/frontend/src/lib/router.tsx
+++ b/frontend/src/lib/router.tsx
@@ -27,6 +27,14 @@ import DaemonSetList from '../components/daemonset/List';
import DeploymentsList from '../components/deployments/List';
import EndpointDetails from '../components/endpoints/Details';
import EndpointList from '../components/endpoints/List';
+import GatewayClassDetails from '../components/gateway/ClassDetails';
+import GatewayClassList from '../components/gateway/ClassList';
+import GatewayDetails from '../components/gateway/GatewayDetails';
+import GatewayList from '../components/gateway/GatewayList';
+import GRPCRouteDetails from '../components/gateway/GRPCRouteDetails';
+import GRPCRouteList from '../components/gateway/GRPCRouteList';
+import HTTPRouteDetails from '../components/gateway/HTTPRouteDetails';
+import HTTPRouteList from '../components/gateway/HTTPRouteList';
import HpaDetails from '../components/horizontalPodAutoscaler/Details';
import HpaList from '../components/horizontalPodAutoscaler/List';
import IngressClassDetails from '../components/ingress/ClassDetails';
@@ -327,6 +335,63 @@ const defaultRoutes: {
sidebar: 'NetworkPolicies',
component: () => ,
},
+ k8sgateways: {
+ // fix magic name gateway
+ path: '/k8sgateways',
+ exact: true,
+ name: 'Gateways',
+ sidebar: 'k8sgateways',
+ component: () => ,
+ },
+ k8sgateway: {
+ // fix magic name gateway
+ path: '/k8sgateways/:namespace/:name',
+ exact: true,
+ name: 'Gateways',
+ sidebar: 'k8sgateways',
+ component: () => ,
+ },
+ httproutes: {
+ path: '/httproutes',
+ exact: true,
+ name: 'HttpRoutes',
+ sidebar: 'httproutes',
+ component: () => ,
+ },
+ httproute: {
+ path: '/httproutes/:namespace/:name',
+ exact: true,
+ name: 'HttpRoutes',
+ sidebar: 'httproutes',
+ component: () => ,
+ },
+ grpcroutes: {
+ path: '/grpcroutes',
+ exact: true,
+ name: 'GRPCRoutes',
+ sidebar: 'grpcroutes',
+ component: () => ,
+ },
+ grpcroute: {
+ path: '/grpcroutes/:namespace/:name',
+ exact: true,
+ name: 'GRPCRoutes',
+ sidebar: 'grpcroutes',
+ component: () => ,
+ },
+ gatewayclasses: {
+ path: '/gatewayclasses',
+ exact: true,
+ name: 'GatewayClasses',
+ sidebar: 'gatewayclasses',
+ component: () => ,
+ },
+ gatewayclass: {
+ path: '/gatewayclasses/:name',
+ exact: true,
+ sidebar: 'gatewayclasses',
+ component: () => ,
+ },
DaemonSets: {
path: '/daemonsets',
exact: true,