diff --git a/src/brokers/broker-details/BrokerDetails.container.tsx b/src/brokers/broker-details/BrokerDetails.container.tsx index 281f5e8e..2710d1de 100644 --- a/src/brokers/broker-details/BrokerDetails.container.tsx +++ b/src/brokers/broker-details/BrokerDetails.container.tsx @@ -144,7 +144,7 @@ const BrokerDetailsPage: FC = () => { diff --git a/src/brokers/broker-details/components/Overview/Overview.container.tsx b/src/brokers/broker-details/components/Overview/Overview.container.tsx index 805dba92..926104bc 100644 --- a/src/brokers/broker-details/components/Overview/Overview.container.tsx +++ b/src/brokers/broker-details/components/Overview/Overview.container.tsx @@ -1,26 +1,206 @@ import { FC } from 'react'; -import { Page } from '@patternfly/react-core'; +import { + Bullseye, + CodeBlock, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + List, + ListItem, + Page, + PageSection, + Title, +} from '@patternfly/react-core'; import { Metrics } from '../../../../metrics'; import { Loading } from '../../../../shared-components'; +import { + Acceptor, + IssuerResource, + K8sResourceCommon, + SecretResource, +} from '../../../../utils'; +import { + getIssuerForAcceptor, + getIssuerIngressHostForAcceptor, +} from '../../../../brokers/utils'; +import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; + +const useGetIssuerCa = ( + cr: K8sResourceCommon, + acceptor: Acceptor, +): [string, boolean, string] => { + const acceptorIssuer = getIssuerForAcceptor(cr, acceptor); + const [issuer, loadedIssuers, loadErrorIssuers] = + useK8sWatchResource({ + groupVersionKind: { + group: 'cert-manager.io', + version: 'v1', + kind: 'Issuer', + }, + namespace: cr.metadata.namespace, + name: acceptorIssuer, + }); + if (!loadedIssuers || loadErrorIssuers) { + return ['', loadedIssuers, loadErrorIssuers]; + } + const secret = issuer.spec?.ca?.secretName ? issuer.spec.ca.secretName : ''; + return [secret, loadedIssuers, loadErrorIssuers]; +}; + +const useGetTlsSecret = (cr: K8sResourceCommon, acceptor: Acceptor) => { + const [secretName, hasSecretName] = useGetIssuerCa(cr, acceptor); + const [secret] = useK8sWatchResource({ + groupVersionKind: { + version: 'v1', + kind: 'Secret', + }, + name: secretName, + namespace: cr.metadata.namespace, + }); + + if (hasSecretName && !secretName) { + return undefined; + } + if (!(secret && secret.data && secret.data['tls.crt'])) { + return undefined; + } + return secret; +}; + +type SecretDownloaLinkProps = { + secret: SecretResource; +}; + +const SecretDownloadLink: FC = ({ secret }) => { + return ( + + {secret.metadata.name + '.pem'} + + ); +}; + +type IssuerSecretsDownloaderProps = { + cr: K8sResourceCommon; +}; + +type HelperConnectAcceptorProps = { + cr: K8sResourceCommon; + acceptor: Acceptor; +}; + +const HelpConnectAcceptor: FC = ({ + cr, + acceptor, +}) => { + const secret = useGetTlsSecret(cr, acceptor); + const ingressHost = getIssuerIngressHostForAcceptor(cr, acceptor); + if (!secret) { + return ; + } + return ( + + + Test the connection to {acceptor.name} + + + + + Download the secret: + + + Run the command with the secret (here in /tmp) + + $ ./artemis check queue --name TEST --produce 10 --browse 10 + --consume 10 --url 'tcp://{ingressHost} + :443?sslEnabled=true&trustStorePath=/tmp/{secret.metadata.name} + .pem&trustStoreType=PEM&useTopologyForLoadBalancing=false' + --verbose + + + + + + ); +}; + +const ConnectivityHelper: FC = ({ cr }) => { + const oneAcceptorHasGeneratedSecrets = cr.spec?.acceptors + ? cr.spec.acceptors + .map((acceptor) => + acceptor.sslSecret ? acceptor.sslSecret.endsWith('-ptls') : false, + ) + .reduce((acc, hasGeneratedSecrets) => acc || hasGeneratedSecrets) + : false; + if (!oneAcceptorHasGeneratedSecrets) { + return <>bou; + } + return ( + <> + + Connect using Artemis +
+ + + Get Artemis + + Download the{' '} + + latest release + {' '} + of ActiveMQ Artemis, decompress the tarball and locate the artemis + executable. + + + {cr.spec.acceptors.map((acceptor) => ( + + ))} + +
+ + ); +}; export type OverviewContainerProps = { namespace: string; name: string; - size: number; + cr: K8sResourceCommon; loading: boolean; }; export const OverviewContainer: FC = ({ namespace, name, - size, + cr, loading, }) => { if (loading) return ; return ( - + + ); }; diff --git a/src/brokers/utils/add-broker.ts b/src/brokers/utils/add-broker.ts index 060fbe2b..3060bb2b 100644 --- a/src/brokers/utils/add-broker.ts +++ b/src/brokers/utils/add-broker.ts @@ -1870,3 +1870,25 @@ export const getIssuerForAcceptor = (cr: ArtemisCR, acceptor: Acceptor) => { } return ''; }; + +export const getIssuerIngressHostForAcceptor = ( + cr: ArtemisCR, + acceptor: Acceptor, +) => { + if (!acceptor) { + return ''; + } + // in case there are no resource templates in the CR + if (!cr.spec.resourceTemplates) { + cr.spec.resourceTemplates = []; + } + // find if there is already an annotation for this acceptor + const selector = certManagerSelector(cr, acceptor.name); + const rt = cr.spec.resourceTemplates.find( + (rt) => rt.selector?.name === selector, + ); + if (rt) { + return rt.patch.spec.tls[0].hosts[0]; + } + return ''; +}; diff --git a/src/metrics/components/MetricsLayout/MetricsLayout.tsx b/src/metrics/components/MetricsLayout/MetricsLayout.tsx index d6f48111..bcf2704a 100644 --- a/src/metrics/components/MetricsLayout/MetricsLayout.tsx +++ b/src/metrics/components/MetricsLayout/MetricsLayout.tsx @@ -1,5 +1,5 @@ import { FC, ReactElement } from 'react'; -import { Grid, GridItem, PageSection } from '@patternfly/react-core'; +import { Grid, GridItem, PageSection, Title } from '@patternfly/react-core'; import { MetricsType } from '../../utils'; export type MetricsLayoutProps = { @@ -24,6 +24,7 @@ export const MetricsLayout: FC = ({ 'pf-u-px-lg-on-xl pf-u-pt-sm-on-xl pf-u-pb-lg-on-xl pf-u-px-md pf-u-pb-md' } > + Metrics {metricsActions} {(() => { diff --git a/src/utils/types.ts b/src/utils/types.ts index 38f20b81..630bdec8 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -100,3 +100,21 @@ export enum BrokerConditionTypes { IngressReady = 'IngressReady', TriggerChannelReady = 'TriggerChannelReady', } + +export type IssuerResource = K8sResource & { + kind: 'Issuer'; + spec?: { + ca?: { + secretName: string; + }; + }; +}; + +export type SecretResource = K8sResource & { + kind: 'Secret'; + data?: { + 'ca.crt'?: string; + 'tls.crt'?: string; + 'tls.key'?: string; + }; +};