Skip to content
This repository has been archived by the owner on Jan 2, 2025. It is now read-only.

Commit

Permalink
[#150] brokers-details: ability to get the cert
Browse files Browse the repository at this point in the history
Give a way to the user to download the certificate in case it was
generated by cert-manager. Also provide an ingress URL to reach and test
the connection.
  • Loading branch information
lavocatt authored and gaohoward committed Jun 18, 2024
1 parent fc07450 commit a8719e2
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/brokers/broker-details/BrokerDetails.container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ const BrokerDetailsPage: FC = () => {
<OverviewContainer
name={brokerName}
namespace={namespace}
size={brokerDetails?.spec?.deploymentPlan?.size}
cr={brokerDetails}
loading={loading}
/>
</Tab>
Expand Down
188 changes: 184 additions & 4 deletions src/brokers/broker-details/components/Overview/Overview.container.tsx
Original file line number Diff line number Diff line change
@@ -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<IssuerResource>({
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<SecretResource>({
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<SecretDownloaLinkProps> = ({ secret }) => {
return (
<a
href={
'data:application/pem-certificate-chain;base64,' +
secret.data['tls.crt']
}
download={secret.metadata.name + '.pem'}
>
{secret.metadata.name + '.pem'}
</a>
);
};

type IssuerSecretsDownloaderProps = {
cr: K8sResourceCommon;
};

type HelperConnectAcceptorProps = {
cr: K8sResourceCommon;
acceptor: Acceptor;
};

const HelpConnectAcceptor: FC<HelperConnectAcceptorProps> = ({
cr,
acceptor,
}) => {
const secret = useGetTlsSecret(cr, acceptor);
const ingressHost = getIssuerIngressHostForAcceptor(cr, acceptor);
if (!secret) {
return <Bullseye></Bullseye>;
}
return (
<DescriptionListGroup>
<DescriptionListTerm>
Test the connection to {acceptor.name}
</DescriptionListTerm>
<DescriptionListDescription>
<List>
<ListItem>
Download the secret: <SecretDownloadLink secret={secret} />
</ListItem>
<ListItem>
Run the command with the secret (here in /tmp)
<CodeBlock>
$ ./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
</CodeBlock>
</ListItem>
</List>
</DescriptionListDescription>
</DescriptionListGroup>
);
};

const ConnectivityHelper: FC<IssuerSecretsDownloaderProps> = ({ 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 (
<>
<PageSection
hasOverflowScroll={true}
aria-label="secrets"
padding={{ default: 'noPadding' }}
className={
'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'
}
>
<Title headingLevel="h2">Connect using Artemis</Title>
<br />
<DescriptionList>
<DescriptionListGroup>
<DescriptionListTerm>Get Artemis</DescriptionListTerm>
<DescriptionListDescription>
Download the{' '}
<a href="https://activemq.apache.org/components/artemis/download/">
latest release
</a>{' '}
of ActiveMQ Artemis, decompress the tarball and locate the artemis
executable.
</DescriptionListDescription>
</DescriptionListGroup>
{cr.spec.acceptors.map((acceptor) => (
<HelpConnectAcceptor
cr={cr}
acceptor={acceptor}
key={acceptor.name}
/>
))}
</DescriptionList>
</PageSection>
</>
);
};

export type OverviewContainerProps = {
namespace: string;
name: string;
size: number;
cr: K8sResourceCommon;
loading: boolean;
};

export const OverviewContainer: FC<OverviewContainerProps> = ({
namespace,
name,
size,
cr,
loading,
}) => {
if (loading) return <Loading />;

return (
<Page>
<Metrics name={name} namespace={namespace} size={size} />
<Metrics
name={name}
namespace={namespace}
size={cr.spec?.deploymentPlan?.size}
/>
<ConnectivityHelper cr={cr} />
</Page>
);
};
22 changes: 22 additions & 0 deletions src/brokers/utils/add-broker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 '';
};
3 changes: 2 additions & 1 deletion src/metrics/components/MetricsLayout/MetricsLayout.tsx
Original file line number Diff line number Diff line change
@@ -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 = {
Expand All @@ -24,6 +24,7 @@ export const MetricsLayout: FC<MetricsLayoutProps> = ({
'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'
}
>
<Title headingLevel="h2">Metrics</Title>
<Grid hasGutter>
<GridItem>{metricsActions}</GridItem>
{(() => {
Expand Down
18 changes: 18 additions & 0 deletions src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
};

0 comments on commit a8719e2

Please sign in to comment.