diff --git a/frontend/public/components/routes.tsx b/frontend/public/components/routes.tsx
index 4e6fc6546e5..5b641fa0276 100644
--- a/frontend/public/components/routes.tsx
+++ b/frontend/public/components/routes.tsx
@@ -1,5 +1,7 @@
import * as _ from 'lodash-es';
import * as React from 'react';
+import { Popover } from '@patternfly/react-core';
+import { QuestionCircleIcon } from '@patternfly/react-icons';
import { ColHead, DetailsPage, List, ListHeader, ListPage, ResourceRow } from './factory';
import { Kebab, CopyToClipboard, SectionHeading, ResourceKebab, detailsPage, navFactory, ResourceLink, ResourceSummary, StatusIcon, ExternalLink } from './utils';
@@ -12,6 +14,16 @@ import { Conditions, conditionProps } from './conditions';
const RoutesReference: K8sResourceKindReference = 'Route';
const menuActions = Kebab.factory.common;
+/* eslint-disable no-undef */
+export type IngressStatusProps = {
+ host: string;
+ routerName: string;
+ conditions: conditionProps[];
+ wildcardPolicy: string;
+ routerCanonicalHostname: string;
+};
+/* eslint-enable no-undef */
+
const getRouteHost = (route, onlyAdmitted) => {
let oldestAdmittedIngress = null;
_.each(route.status.ingress, (ingress) => {
@@ -186,13 +198,47 @@ const calcTrafficPercentage = (weight: number, route: any) => {
return `${percentage.toFixed(1)}%`;
};
+const getIngressStatusForHost = (hostname, ingresses): IngressStatusProps => {
+ return _.find(ingresses, {host: hostname}) as IngressStatusProps;
+};
+
+const showCustomRouteHelp = (ingress, annotations) => {
+ if (!ingress || !_.some(ingress.conditions, {type: 'Admitted', status: 'True'})) {
+ return false;
+ }
+
+ if (_.get(annotations, 'openshift.io/host.generated') === 'true') {
+ return false;
+ }
+
+ if (!ingress.host || !ingress.routerCanonicalHostname) {
+ return false;
+ }
+
+ return true;
+};
+
const RouteTargetRow = ({route, target}) =>
{target.weight}
{calcTrafficPercentage(target.weight, route)}
;
-const RouteIngressStatus: React.SFC = ({ingresses}) =>
+const CustomRouteHelp: React.SFC = ({host, routerCanonicalHostname}) =>
+ Custom Route}
+ bodyContent={
+
+
To use a custom route, you must update your DNS provider by creating a canonical name (CNAME) record. Your
+ CNAME record should point to your custom domain {host} , to the OpenShift canonical router
+ hostname, {routerCanonicalHostname} ,
+ as the alias.
+
+ }>
+ Do you need to set up custom DNS?
+ ;
+
+const RouteIngressStatus: React.SFC = ({ingresses, annotations}) =>
{_.map(ingresses, (ingress) =>
@@ -202,75 +248,93 @@ const RouteIngressStatus: React.SFC = ({ingresses}) =>
{ingress.host}
Wildcard Policy
{ingress.wildcardPolicy}
+ Canonical Router Hostname
+ {ingress.routerCanonicalHostname || '-'}
+ {showCustomRouteHelp(ingress, annotations) &&
+ }
Conditions
)}
;
-const RouteDetails: React.SFC = ({obj: route}) =>
-
-
-
-
-
- {route.spec.to.kind}
-
-
- Target Port
- {_.get(route, 'spec.port.targetPort', '-')}
-
-
-
-
- Location
-
- Status
-
-
-
- Hostname
- {route.spec.host}
- Path
- {route.spec.path || '-'}
-
+const RouteDetails: React.SFC
= ({obj: route}) => {
+ const primaryIngressStatus:IngressStatusProps = getIngressStatusForHost(route.spec.host, route.status.ingress);
+ return
+
+
+
+
+
+ {route.spec.to.kind}
+
+
+ Target Port
+ {_.get(route, 'spec.port.targetPort', '-')}
+
+
+
+
+ Location
+
+ Status
+
+
+
+ Hostname
+ {route.spec.host}
+ Path
+ {route.spec.path || '-'}
+ {primaryIngressStatus &&
+ Canonical Router Hostname
+ {primaryIngressStatus.routerCanonicalHostname || '-'}
+
+ }
+ {showCustomRouteHelp(primaryIngressStatus, route.metadata.annotations) &&
+
+ }
+
+
-
-
-
-
-
- { !_.isEmpty(route.spec.alternateBackends) &&
-
-
- This route splits traffic across multiple services.
-
-
-
-
-
- Service
- Weight
- Percent
-
-
-
-
- {_.map(route.spec.alternateBackends, (alternate, i) => )}
-
-
-
-
}
- {_.isEmpty(route.status.ingress)
- ?
-
No Route Status
+
+
+
- :
-
+ {!_.isEmpty(route.spec.alternateBackends) &&
+
+
+ This route splits traffic across multiple services.
+
+
+
+
+
+ Service
+ Weight
+ Percent
+
+
+
+
+ {_.map(route.spec.alternateBackends, (alternate, i) => )}
+
+
+
}
-;
+ {_.isEmpty(route.status.ingress)
+ ?
+ :
+
+
}
+ ;
+};
export const RoutesDetailsPage: React.SFC
= props => = props => {
/* eslint-disable no-undef */
export type RouteHostnameProps = {
- obj: K8sResourceKind
+ obj: K8sResourceKind;
};
export type RouteStatusProps = {
- obj: K8sResourceKind
+ obj: K8sResourceKind;
};
export type RoutesRowProps = {
- obj: K8sResourceKind
+ obj: K8sResourceKind;
};
export type RouteHeaderProps = {
- obj: K8sResourceKind
+ obj: K8sResourceKind;
};
export type RoutesPageProps = {
- obj: K8sResourceKind,
- namespace: string
+ obj: K8sResourceKind;
+ namespace: string;
};
export type RoutesDetailsProps = {
- obj: K8sResourceKind
+ obj: K8sResourceKind;
};
export type RoutesDetailsPageProps = {
- match: any
+ match: any;
};
export type TLSDataProps = {
- tls: any
+ tls: any;
};
export type TLSDataState = {
- showPrivateKey: boolean
+ showPrivateKey: boolean;
};
-export type IngressStatusProps = {
- host: string,
- routerName: string,
- conditions: conditionProps[],
- wildcardPolicy: string
+export type RouteIngressStatusProps = {
+ ingresses: IngressStatusProps[];
+ annotations: {[key: string]: string};
};
-export type RouteIngressStatusProps = {
- ingresses: IngressStatusProps[]
+export type CustomRouteHelpProps = {
+ host: string;
+ routerCanonicalHostname: string;
};
/* eslint-enable no-undef */