Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions libs/locales/lib/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@
"ai:Control plane nodes: At least {{master_cpu_cores}} CPU cores, {{master_ram}} RAM, {{master_disksize}} GB disk size for every control plane node.": "Control plane nodes: At least {{master_cpu_cores}} CPU cores, {{master_ram}} RAM, {{master_disksize}} GB disk size for every control plane node.",
"ai:Control plane pods": "Control plane pods",
"ai:Controller availability policy": "Controller availability policy",
"ai:Could not download {{fileName}} file": "Could not download {{fileName}} file",
"ai:Could not download kubeconfig": "Could not download kubeconfig",
"ai:Could not fetch infra environments": "Could not fetch infra environments",
"ai:Could not fetch pull secret": "Could not fetch pull secret",
Expand Down Expand Up @@ -271,6 +272,7 @@
"ai:DNS domain": "DNS domain",
"ai:DNS wildcard not configured": "DNS wildcard not configured",
"ai:Do not use forbidden words, for example: \"localhost\".": "Do not use forbidden words, for example: \"localhost\".",
"ai:Download credentials": "Download credentials",
"ai:Download Discovery ISO": "Download Discovery ISO",
"ai:Download host discovery ISO dialog": "Download host discovery ISO dialog",
"ai:Download host logs": "Download host logs",
Expand Down Expand Up @@ -330,6 +332,7 @@
"ai:Failed to download the discovery Image": "Failed to download the discovery Image",
"ai:Failed to fetch cluster credentials.": "Failed to fetch cluster credentials.",
"ai:Failed to get Provisioning Configuration": "Failed to get Provisioning Configuration",
"ai:Failed to load Credentials Download step": "Failed to load Credentials Download step",
"ai:Failed to patch assisted-image-service route for new domain.": "Failed to patch assisted-image-service route for new domain.",
"ai:Failed to reset cluster installation": "Failed to reset cluster installation",
"ai:Failed to save ClusterDeployment": "Failed to save ClusterDeployment",
Expand Down Expand Up @@ -414,6 +417,7 @@
"ai:HTTPS proxy": "HTTPS proxy",
"ai:HTTPS proxy URL": "HTTPS proxy URL",
"ai:HTTPS Proxy URL": "HTTPS Proxy URL",
"ai:I understand that I need to download credentials files prior of proceeding with the cluster installation.": "I understand that I need to download credentials files prior of proceeding with the cluster installation.",
"ai:I understand, accept, and agree to the limitations associated with using Single Node OpenShift.": "I understand, accept, and agree to the limitations associated with using Single Node OpenShift.",
"ai:If hosts are behind a firewall that requires the use of a proxy, provide additional information about the proxy.": "If hosts are behind a firewall that requires the use of a proxy, provide additional information about the proxy.",
"ai:If not, please start your VMs with the following configuration:": "If not, start your VMs with the following configuration:",
Expand Down Expand Up @@ -491,6 +495,7 @@
"ai:Learn more": "Learn more",
"ai:Learn more about configuration.": "Learn more about configuration.",
"ai:Learn more about enabling CIM on AWS <1></1>": "Learn more about enabling CIM on AWS <1></1>",
"ai:Learn more about Kubeconfig": "Learn more about Kubeconfig",
"ai:Learn more about MTU (maximum transmission unit)": "Learn more about MTU (maximum transmission unit)",
"ai:Learn more about OpenShift releases": "Learn more about OpenShift releases",
"ai:Learn more about pull secrets and view examples": "Learn more about pull secrets and view examples",
Expand All @@ -514,6 +519,7 @@
"ai:Machine networks": "Machine networks",
"ai:Make sure that the VIP is unique and not used by any other device on your network.": "Make sure that the VIP is unique and not used by any other device on your network.",
"ai:Make sure that you expect and recognize the host before approving.": "Make sure that you expect and recognize the host before approving.",
"ai:Make sure you download and store your credentials files in a safe place": "Make sure you download and store your credentials files in a safe place",
"ai:Manage hosts": "Manage hosts",
"ai:Management": "Management",
"ai:Manually fix the host's NTP configuration or provide additional NTP sources.": "Manually fix the host's NTP configuration or provide additional NTP sources.",
Expand Down Expand Up @@ -945,7 +951,9 @@
"ai:You are approving multiple hosts. All hosts listed below will be approved to join the infrastructure environment if you continue. Make sure that you expect and recognize the hosts before approving.": "You are approving multiple hosts. All hosts listed will be approved to join the infrastructure environment, if you continue. Make sure that you expect and recognize the hosts before approving.",
"ai:You can either wait or investigate. A common issue can be misconfigured storage. Once you fix the issue, you can delete AgentServiceConfig to try again.": "You can either wait or investigate. A common issue can be misconfigured storage. After you fix the issue, delete AgentServiceConfig and try again.",
"ai:You can upload additional trusted certificates in PEM-encoded X.509 format if the cluster hosts are in a network with a re-encrypting (MITM) proxy or the cluster needs to trust certificates for other purposes (for example, container image registries).": "You can upload additional trusted certificates in PEM-encoded X.509 format if the cluster hosts are in a network with a re-encrypting (MITM) proxy or the cluster needs to trust certificates for other purposes (for example, container image registries).",
"ai:You can use username and password to log-in to your console through the UI.": "You can use username and password to log-in to your console through the UI.",
"ai:You have opted out of formatting bootable disks on some hosts. To ensure the hosts reboot into the expected installation disk, manual user intervention may be required during OpenShift installation.": "You have opted out of formatting bootable disks on some hosts. To ensure the hosts reboot into the expected installation disk, manual user intervention might be required during OpenShift installation.",
"ai:You should use your cluster's Kubeconfig file to gain access to the cluster.": "You should use your cluster's Kubeconfig file to gain access to the cluster.",
"ai:You'll first need to have a storage operator in order to create the storage class. If you don't have one installed, we recommend OpenShift Data Foundation operator, but you may use any.": "You need a storage operator installed, such as OpenShift Data Foundation, to create the storage class.",
"ai:Your libvirt virtual machines should be configured to restart automatically after a reboot. You can check this by running:": "Your libvirt virtual machines should be configured to restart automatically after a reboot. You can check this by running:",
"ai:Your own NTP (Network Time Protocol) sources": "Your own NTP (Network Time Protocol) sources"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import React from 'react';
import { GridItem, ClipboardCopy, clipboardCopyFunc, Button, Alert } from '@patternfly/react-core';
import {
GridItem,
ClipboardCopy,
clipboardCopyFunc,
Button,
Alert,
TextInput,
} from '@patternfly/react-core';
import { Credentials, Cluster } from '@openshift-assisted/types/assisted-installer-service';
import { LoadingState, ErrorState } from '../../components/ui/uiState';
import { DetailList, DetailItem } from '../../components/ui/DetailList';
Expand Down Expand Up @@ -98,7 +105,10 @@ const ClusterCredentials: React.FC<ClusterCredentialsProps> = ({
)}
{credentials.username && (
<>
<DetailItem title="Username" value={credentials.username} />
<DetailItem
title={t('ai:Username')}
value={<TextInput value={credentials.username} readOnlyVariant="default" />}
/>
<DetailItem
title={t('ai:Password')}
value={
Expand Down
3 changes: 3 additions & 0 deletions libs/ui-lib/lib/common/components/ui/WizardFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type WizardFooterGenericProps = {
isSubmitting?: boolean;
submittingText?: string;
nextButtonText?: string;
isNextButtonLoading?: boolean;
};

type WizardFooterProps = WizardFooterGenericProps & {
Expand All @@ -51,6 +52,7 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
nextButtonText,
cluster,
onFetchEvents,
isNextButtonLoading,
}) => {
const { t } = useTranslation();
submittingText = submittingText || t('ai:Saving changes...');
Expand All @@ -68,6 +70,7 @@ export const WizardFooter: React.FC<WizardFooterProps> = ({
name="next"
onClick={onNext}
isDisabled={isNextDisabled}
isLoading={isNextButtonLoading}
>
{nextButtonText || t('ai:Next')}
</Button>
Expand Down
3 changes: 3 additions & 0 deletions libs/ui-lib/lib/common/config/docs_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,6 @@ export const getServiceMeshLink = (ocpVersion?: string) =>

export const SERVERLESS_OPERATOR_LINK =
'https://docs.openshift.com/serverless/1.28/install/install-serverless-operator.html#serverless-install-web-console_install-serverless-operator';

export const KUBECONFIG_INFO_LINK =
'https://docs.redhat.com/en/documentation/openshift_container_platform/4.18/html/cli_tools/openshift-cli-oc#about-switches-between-cli-profiles_managing-cli-profiles';
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
InfraEnv,
InfraEnvUpdateParams,
} from '@openshift-assisted/types/assisted-installer-service';
import KubeconfigDownload from './KubeconfigDownload';
import CredentialsDownload from './CredentialsDownload';

type ClusterWizardProps = {
cluster: Cluster;
Expand Down Expand Up @@ -45,8 +45,8 @@ const ClusterWizard = ({ cluster, infraEnv, updateInfraEnv }: ClusterWizardProps
case 'static-ip-network-wide-configurations':
case 'static-ip-yaml-view':
return <StaticIp cluster={cluster} infraEnv={infraEnv} updateInfraEnv={updateInfraEnv} />;
case 'kubeconfig-download':
return <KubeconfigDownload cluster={cluster} />;
case 'credentials-download':
return <CredentialsDownload cluster={cluster} />;
case 'cluster-details':
default:
return <ClusterDetails cluster={cluster} infraEnv={infraEnv} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const getWizardStepIds = (
stepsCopy = removeStepFromClusterWizard(stepsCopy, 'custom-manifests', 1);
}
if (isSingleClusterFeatureEnabled) {
stepsCopy = addStepToClusterWizard(stepsCopy, 'networking', ['kubeconfig-download']);
stepsCopy = addStepToClusterWizard(stepsCopy, 'networking', ['credentials-download']);
}

return stepsCopy;
Expand Down
135 changes: 135 additions & 0 deletions libs/ui-lib/lib/ocm/components/clusterWizard/CredentialsDownload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React from 'react';
import {
ClusterCredentials,
ClustersAPI,
ClusterWizardStep,
ClusterWizardStepHeader,
getApiErrorMessage,
handleApiError,
KUBECONFIG_INFO_LINK,
useAlerts,
useTranslation,
} from '../../../common';
import { useClusterWizardContext } from './ClusterWizardContext';
import ClusterWizardFooter from './ClusterWizardFooter';
import ClusterWizardNavigation from './ClusterWizardNavigation';
import { WithErrorBoundary } from '../../../common/components/ErrorHandling/WithErrorBoundary';
import { Cluster, Credentials } from '@openshift-assisted/types/assisted-installer-service';
import { Alert, AlertVariant, Checkbox, Grid, Stack } from '@patternfly/react-core';
import { downloadFile, getErrorMessage } from '../../../common/utils';
import { ExternalLinkAltIcon } from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon';

const CredentialsDownload: React.FC<{ cluster: Cluster }> = ({ cluster }) => {
const clusterWizardContext = useClusterWizardContext();
const [isChecked, setIsChecked] = React.useState<boolean>(false);
const [isDownloading, setIsDownloading] = React.useState(false);
const [credentials, setCredentials] = React.useState<Credentials>();
const [credentialsError, setCredentialsError] = React.useState<string>();
const { addAlert } = useAlerts();
const { t } = useTranslation();

React.useEffect(() => {
const fetch = async () => {
setCredentialsError(undefined);
if (!cluster.id) {
return;
}
try {
const response = await ClustersAPI.getCredentials(cluster.id);
const data = { username: response.data.username, password: response.data.password };
setCredentials(data);
} catch (err) {
handleApiError(err, (err) => {
setCredentialsError(`Failed to fetch credentials, ${getErrorMessage(err)}`);
});
}
};
void fetch();
}, [cluster.id, setCredentials]);

const downloadSingleFile = async (fileName: string) => {
try {
const response = await ClustersAPI.downloadClusterCredentials(cluster.id, fileName);
downloadFile('', response.data, fileName);
return true;
} catch (e) {
handleApiError(e, (e) => {
addAlert({
title: t('ai:Could not download {{fileName}} file', { fileName }),
message: getApiErrorMessage(e),
});
});
return false;
}
};

const handleDownloadClick = async () => {
setIsDownloading(true);

const configSuccess = await downloadSingleFile('kubeconfig');
const passwordSuccess = await downloadSingleFile('kubeadmin-password');

const hasError = !configSuccess || !passwordSuccess;

setTimeout(() => {
setIsDownloading(false);

if (!hasError) {
clusterWizardContext.moveNext();
}
}, 500);
};

const footer = (
<ClusterWizardFooter
cluster={cluster}
onNext={() => void handleDownloadClick()}
onBack={() => clusterWizardContext.moveBack()}
isNextDisabled={!isChecked || isDownloading}
nextButtonText={t('ai:Download credentials')}
isNextButtonLoading={isDownloading}
/>
);

return (
<ClusterWizardStep navigation={<ClusterWizardNavigation cluster={cluster} />} footer={footer}>
<WithErrorBoundary title={t('ai:Failed to load Credentials Download step')}>
<Stack hasGutter>
<ClusterWizardStepHeader>{t('ai:Download credentials')}</ClusterWizardStepHeader>
<Alert
title={t('ai:Make sure you download and store your credentials files in a safe place')}
variant={AlertVariant.warning}
isInline
actionLinks={
<a href={KUBECONFIG_INFO_LINK} target="_blank" rel="noopener noreferrer">
{t('ai:Learn more about Kubeconfig')} <ExternalLinkAltIcon />
</a>
}
>
{t(`ai:You should use your cluster's Kubeconfig file to gain access to the cluster.`)}
<br />
{t(`ai:You can use username and password to log-in to your console through the UI.`)}
</Alert>
<Checkbox
id="credentials-download-agreement"
isChecked={isChecked}
onChange={() => setIsChecked(!isChecked)}
label={t(
'ai:I understand that I need to download credentials files prior of proceeding with the cluster installation.',
)}
/>
<Grid hasGutter style={{ justifyItems: 'start' }}>
<ClusterCredentials
cluster={cluster}
credentials={credentials}
credentialsError={credentialsError}
error={!!credentialsError}
/>
</Grid>
</Stack>
</WithErrorBoundary>
</ClusterWizardStep>
);
};

export default CredentialsDownload;
2 changes: 1 addition & 1 deletion libs/ui-lib/lib/ocm/components/clusterWizard/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const wizardStepNames: { [key in ClusterWizardStepsType]: string } = {
networking: 'Networking',
'custom-manifests': 'Custom manifests',
review: 'Review and create',
'kubeconfig-download': 'Download Kubeconfig',
'credentials-download': 'Download credentials',
};

export const defaultWizardSteps: ClusterWizardStepsType[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type ClusterWizardStepsType =
| 'networking'
| 'review'
| 'custom-manifests'
| 'kubeconfig-download';
| 'credentials-download';

const wizardStepsOrder: ClusterWizardStepsType[] = [
'cluster-details',
Expand All @@ -41,7 +41,7 @@ const wizardStepsOrder: ClusterWizardStepsType[] = [
'storage',
'networking',
'custom-manifests',
'kubeconfig-download',
'credentials-download',
'review',
];

Expand Down Expand Up @@ -247,7 +247,7 @@ const reviewStepValidationsMap: WizardStepValidationMap = {
softValidationIds: [],
};

const kubeconfigValidationMap = buildEmptyValidationsMap();
const credentialsValidationMap = buildEmptyValidationsMap();

const customManifestsValidationsMap = buildEmptyValidationsMap();

Expand All @@ -262,7 +262,7 @@ export const wizardStepsValidationsMap: WizardStepsValidationMap<ClusterWizardSt
storage: storageStepValidationsMap,
networking: networkingStepValidationsMap,
review: reviewStepValidationsMap,
'kubeconfig-download': kubeconfigValidationMap,
'credentials-download': credentialsValidationMap,
};

export const allClusterWizardSoftValidationIds =
Expand Down
Loading