Merge c5de94250a into d7f844b8b6
This commit is contained in:
commit
e57578cadc
14 changed files with 241 additions and 47 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { execSync } from 'child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
import { expect, type Page } from '@playwright/test';
|
||||
import { expect, FrameLocator, type Page } from '@playwright/test';
|
||||
|
||||
export const togglePreview = async (page: Page) => {
|
||||
const toggleSwitch = page.locator('#preview-toggle');
|
||||
|
|
@ -85,3 +85,15 @@ export const getHostDistroName = (): string => {
|
|||
export const getHostArch = (): string => {
|
||||
return execSync('uname -m').toString('utf-8').replace(/\s/g, '');
|
||||
};
|
||||
|
||||
export const isRhel = async (frame: Page | FrameLocator) => {
|
||||
if (isHosted()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const registerHeading = frame.getByRole('heading', {
|
||||
name: 'Register systems using this image',
|
||||
});
|
||||
|
||||
return registerHeading.isVisible();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { expect, FrameLocator, type Page, test } from '@playwright/test';
|
||||
|
||||
import { closePopupsIfExist, isHosted } from './helpers';
|
||||
import { closePopupsIfExist, isHosted, isRhel } from './helpers';
|
||||
import { ibFrame, navigateToLandingPage } from './navHelpers';
|
||||
|
||||
/**
|
||||
|
|
@ -45,11 +45,12 @@ export const fillInDetails = async (
|
|||
|
||||
/**
|
||||
* Select "Register later" option in the wizard
|
||||
* This function executes only on the hosted service
|
||||
* This function executes only on if the registration
|
||||
* step is visible (it won't be visible for Fedora)
|
||||
* @param page - the page object
|
||||
*/
|
||||
export const registerLater = async (page: Page | FrameLocator) => {
|
||||
if (isHosted()) {
|
||||
if (await isRhel(page)) {
|
||||
await page.getByRole('button', { name: 'Register' }).click();
|
||||
await page.getByRole('radio', { name: 'Register later' }).click();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import TOML from '@ltd/j-toml';
|
|||
import { expect, test } from '@playwright/test';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { closePopupsIfExist, isHosted } from './helpers/helpers';
|
||||
import { closePopupsIfExist, isHosted, isRhel } from './helpers/helpers';
|
||||
import { ensureAuthenticated } from './helpers/login';
|
||||
import { ibFrame, navigateToLandingPage } from './helpers/navHelpers';
|
||||
|
||||
|
|
@ -27,11 +27,8 @@ test.describe.serial('test', () => {
|
|||
.click();
|
||||
await frame.getByRole('button', { name: 'Next', exact: true }).click();
|
||||
|
||||
if (isHosted()) {
|
||||
frame.getByRole('heading', {
|
||||
name: 'Register systems using this image',
|
||||
});
|
||||
await page.getByRole('radio', { name: /Register later/i }).click();
|
||||
if (await isRhel(frame)) {
|
||||
await frame.getByRole('radio', { name: /Register later/i }).click();
|
||||
await frame.getByRole('button', { name: 'Next', exact: true }).click();
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +278,12 @@ test.describe.serial('test', () => {
|
|||
// the first card should be the AWS card
|
||||
await frame.locator('.pf-v6-c-card').first().click();
|
||||
await frame.getByRole('button', { name: 'Next', exact: true }).click();
|
||||
// Just use the default region
|
||||
await frame.getByRole('button', { name: 'Next', exact: true }).click();
|
||||
if (await isRhel(frame)) {
|
||||
await frame.getByRole('radio', { name: /Register later/i }).click();
|
||||
await frame.getByRole('button', { name: 'Next', exact: true }).click();
|
||||
}
|
||||
await frame.getByRole('button', { name: 'Review and finish' }).click();
|
||||
await frame.getByRole('button', { name: 'Back', exact: true }).click();
|
||||
|
||||
|
|
|
|||
|
|
@ -318,7 +318,7 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
|
|||
'parentId' in step && step.parentId === 'step-optional-steps';
|
||||
|
||||
useEffect(() => {
|
||||
if (process.env.IS_ON_PREMISE) {
|
||||
if (!isRhel(distribution)) {
|
||||
if (step.id === 'step-oscap' && step.isVisited) {
|
||||
setWasRegisterVisited(true);
|
||||
}
|
||||
|
|
@ -460,7 +460,7 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => {
|
|||
name='Register'
|
||||
id='step-register'
|
||||
key='step-register'
|
||||
isHidden={!!process.env.IS_ON_PREMISE || !isRhel(distribution)}
|
||||
isHidden={!isRhel(distribution)}
|
||||
navItem={CustomStatusNavItem}
|
||||
status={
|
||||
wasRegisterVisited
|
||||
|
|
|
|||
|
|
@ -26,10 +26,14 @@ const ActivationKeyInformation = (): JSX.Element => {
|
|||
} = useShowActivationKeyQuery(
|
||||
{ name: activationKey! },
|
||||
{
|
||||
skip: !activationKey,
|
||||
skip: !activationKey || !!process.env.IS_ON_PREMISE,
|
||||
},
|
||||
);
|
||||
|
||||
if (process.env.IS_ON_PREMISE) {
|
||||
return <Content component={ContentVariants.dd}>{activationKey}</Content>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isFetchingActivationKeyInfo && <Spinner size='lg' />}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
import React, { MutableRefObject, useEffect, useRef } from 'react';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Content,
|
||||
ContentVariants,
|
||||
FormGroup,
|
||||
FormGroupLabelHelp,
|
||||
Popover,
|
||||
} from '@patternfly/react-core';
|
||||
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||
|
||||
import { ACTIVATION_KEYS_URL, CDN_PROD_URL } from '../../../../../constants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
changeActivationKey,
|
||||
changeBaseUrl,
|
||||
changeOrgId,
|
||||
changeServerUrl,
|
||||
selectActivationKey,
|
||||
selectOrgId,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
import { ValidatedInput } from '../../../ValidatedInput';
|
||||
|
||||
const ManualRegistrationPopover = ({
|
||||
ref,
|
||||
}: {
|
||||
ref: MutableRefObject<null>;
|
||||
}) => {
|
||||
return (
|
||||
<Popover
|
||||
triggerRef={ref}
|
||||
headerContent='About Activation Keys & Organization ID'
|
||||
position='right'
|
||||
minWidth='30rem'
|
||||
bodyContent={
|
||||
<Content>
|
||||
<Content component={ContentVariants.p}>
|
||||
Activation keys assist you in registering and configuring systems.
|
||||
Metadata such as role, system purpose, and usage can be
|
||||
automatically attached to systems via an activation key, and
|
||||
monitored with Subscription Watch.
|
||||
</Content>
|
||||
<Content component={ContentVariants.p}>
|
||||
The Organization ID is the numeric identifier for your organization
|
||||
and is separate from your account number. Your organization's
|
||||
activation keys and organization ID are displayed on the Activation
|
||||
Keys page in the Hybrid Cloud Console.
|
||||
</Content>
|
||||
<Button
|
||||
component='a'
|
||||
target='_blank'
|
||||
variant='link'
|
||||
icon={<ExternalLinkAltIcon />}
|
||||
iconPosition='right'
|
||||
isInline
|
||||
href={ACTIVATION_KEYS_URL}
|
||||
>
|
||||
View activation keys in Hybrid Cloud Console
|
||||
</Button>
|
||||
</Content>
|
||||
}
|
||||
>
|
||||
<FormGroupLabelHelp
|
||||
ref={ref}
|
||||
aria-label='About Activation Keys & Organization ID'
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export const ManualActivationKey = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const orgId = useAppSelector(selectOrgId);
|
||||
const activationKey = useAppSelector(selectActivationKey);
|
||||
const orgIdRef = useRef(null);
|
||||
const activationKeyRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(changeServerUrl('subscription.rhsm.redhat.com'));
|
||||
dispatch(changeBaseUrl(CDN_PROD_URL));
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormGroup
|
||||
label={'Activation key'}
|
||||
labelHelp={<ManualRegistrationPopover ref={activationKeyRef} />}
|
||||
>
|
||||
<ValidatedInput
|
||||
placeholder='Activation key'
|
||||
ariaLabel='Activation key'
|
||||
value={activationKey || ''}
|
||||
onChange={(_, value) => {
|
||||
dispatch(changeActivationKey(value.trim()));
|
||||
}}
|
||||
validator={(v) => v !== undefined && v !== ''}
|
||||
helperText='The activation key cannot be empty'
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={'Organization ID'}
|
||||
labelHelp={<ManualRegistrationPopover ref={orgIdRef} />}
|
||||
>
|
||||
<ValidatedInput
|
||||
placeholder='Organization ID'
|
||||
ariaLabel='Organization ID'
|
||||
value={orgId || ''}
|
||||
onChange={(_, value) => {
|
||||
dispatch(changeOrgId(value.trim()));
|
||||
}}
|
||||
validator={(v) => {
|
||||
if (v === undefined || v === '') {
|
||||
return false;
|
||||
}
|
||||
return /^\d+$/.test(v.trim());
|
||||
}}
|
||||
helperText='Please enter a valid Organization ID'
|
||||
/>
|
||||
</FormGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -13,6 +13,7 @@ import useChrome from '@redhat-cloud-services/frontend-components/useChrome';
|
|||
|
||||
import ActivationKeyInformation from './components/ActivationKeyInformation';
|
||||
import ActivationKeysList from './components/ActivationKeysList';
|
||||
import { ManualActivationKey } from './components/ManualActivationKey';
|
||||
import Registration from './components/Registration';
|
||||
import SatelliteRegistration from './components/SatelliteRegistration';
|
||||
|
||||
|
|
@ -44,23 +45,25 @@ const RegistrationStep = () => {
|
|||
<Title headingLevel='h1' size='xl'>
|
||||
Register systems using this image
|
||||
</Title>
|
||||
<FormGroup label='Organization ID'>
|
||||
<ClipboardCopy
|
||||
hoverTip='Copy to clipboard'
|
||||
clickTip='Successfully copied to clipboard!'
|
||||
variant='inline-compact'
|
||||
>
|
||||
{orgId || ''}
|
||||
</ClipboardCopy>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem>
|
||||
If using an activation key with command line registration, you
|
||||
must provide your organization's ID
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
{!process.env.IS_ON_PREMISE && (
|
||||
<FormGroup label='Organization ID'>
|
||||
<ClipboardCopy
|
||||
hoverTip='Copy to clipboard'
|
||||
clickTip='Successfully copied to clipboard!'
|
||||
variant='inline-compact'
|
||||
>
|
||||
{orgId || ''}
|
||||
</ClipboardCopy>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem>
|
||||
If using an activation key with command line registration, you
|
||||
must provide your organization's ID
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
)}
|
||||
<Registration />
|
||||
{registrationType === 'register-satellite' && <SatelliteRegistration />}
|
||||
{!process.env.IS_ON_PREMISE &&
|
||||
|
|
@ -76,6 +79,9 @@ const RegistrationStep = () => {
|
|||
<ActivationKeyInformation />
|
||||
</FormGroup>
|
||||
)}
|
||||
{process.env.IS_ON_PREMISE && registrationType !== 'register-later' && (
|
||||
<ManualActivationKey />
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import {
|
|||
useCreateBPWithNotification as useCreateBlueprintMutation,
|
||||
useUpdateBPWithNotification as useUpdateBlueprintMutation,
|
||||
} from '../../../../../Hooks';
|
||||
import { useAppSelector } from '../../../../../store/hooks';
|
||||
import { selectOrgId } from '../../../../../store/wizardSlice';
|
||||
import { resolveRelPath } from '../../../../../Utilities/path';
|
||||
import { mapRequestFromState } from '../../../utilities/requestMapper';
|
||||
import { useIsBlueprintValid } from '../../../utilities/useValidation';
|
||||
|
|
@ -41,6 +43,7 @@ const ReviewWizardFooter = () => {
|
|||
};
|
||||
const navigate = useNavigate();
|
||||
const isValid = useIsBlueprintValid();
|
||||
const orgId = useAppSelector(selectOrgId);
|
||||
|
||||
useEffect(() => {
|
||||
if (isUpdateSuccess || isCreateSuccess) {
|
||||
|
|
@ -58,9 +61,7 @@ const ReviewWizardFooter = () => {
|
|||
return requestBody;
|
||||
}
|
||||
|
||||
// NOTE: This should be fine on-prem, we should
|
||||
// be able to ignore the `org-id`
|
||||
return mapRequestFromState(store, '');
|
||||
return mapRequestFromState(store, orgId ?? '');
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ import {
|
|||
selectKeyboard,
|
||||
selectLanguages,
|
||||
selectNtpServers,
|
||||
selectOrgId,
|
||||
selectPackages,
|
||||
selectPartitions,
|
||||
selectRecommendedRepositories,
|
||||
|
|
@ -704,6 +705,7 @@ export const RegisterAapList = () => {
|
|||
};
|
||||
|
||||
export const RegisterNowList = () => {
|
||||
const orgId = useAppSelector(selectOrgId);
|
||||
const activationKey = useAppSelector(selectActivationKey);
|
||||
const registrationType = useAppSelector(selectRegistrationType);
|
||||
|
||||
|
|
@ -711,7 +713,7 @@ export const RegisterNowList = () => {
|
|||
// @ts-ignore type of 'activationKey' might not be strictly compatible with the expected type for 'name'.
|
||||
{ name: activationKey },
|
||||
{
|
||||
skip: !activationKey,
|
||||
skip: !activationKey || process.env.IS_ON_PREMISE,
|
||||
},
|
||||
);
|
||||
return (
|
||||
|
|
@ -753,6 +755,12 @@ export const RegisterNowList = () => {
|
|||
<Content component={ContentVariants.dd}>
|
||||
<ActivationKeyInformation />
|
||||
</Content>
|
||||
{process.env.IS_ON_PREMISE && (
|
||||
<>
|
||||
<Content component={ContentVariants.dt}>Organization ID</Content>
|
||||
<Content component={ContentVariants.dd}>{orgId}</Content>
|
||||
</>
|
||||
)}
|
||||
</Content>
|
||||
</Content>
|
||||
{isError && (
|
||||
|
|
|
|||
|
|
@ -399,6 +399,9 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => {
|
|||
activationKey: isRhel(request.distribution)
|
||||
? request.customizations.subscription?.['activation-key']
|
||||
: undefined,
|
||||
orgId: isRhel(request.distribution)
|
||||
? request.customizations.subscription?.['organization']?.toString()
|
||||
: undefined,
|
||||
satelliteRegistration: {
|
||||
command: getSatelliteCommand(request.customizations.files),
|
||||
caCert: request.customizations.cacerts?.pem_certs[0],
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import {
|
|||
selectKeyboard,
|
||||
selectLanguages,
|
||||
selectNtpServers,
|
||||
selectOrgId,
|
||||
selectPartitions,
|
||||
selectRegistrationType,
|
||||
selectSatelliteCaCertificate,
|
||||
|
|
@ -123,6 +124,7 @@ type ValidationState = {
|
|||
export function useRegistrationValidation(): StepValidation {
|
||||
const registrationType = useAppSelector(selectRegistrationType);
|
||||
const activationKey = useAppSelector(selectActivationKey);
|
||||
const orgId = useAppSelector(selectOrgId);
|
||||
const registrationCommand = useAppSelector(
|
||||
selectSatelliteRegistrationCommand,
|
||||
);
|
||||
|
|
@ -132,15 +134,37 @@ export function useRegistrationValidation(): StepValidation {
|
|||
useShowActivationKeyQuery(
|
||||
{ name: activationKey! },
|
||||
{
|
||||
skip: !activationKey,
|
||||
skip: !activationKey || !!process.env.IS_ON_PREMISE,
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
registrationType !== 'register-later' &&
|
||||
registrationType !== 'register-satellite' &&
|
||||
!activationKey
|
||||
) {
|
||||
if (registrationType === 'register-later') {
|
||||
return { errors: {}, disabledNext: false };
|
||||
}
|
||||
|
||||
if (process.env.IS_ON_PREMISE) {
|
||||
const errors: Record<string, string> = {};
|
||||
let disabledNext = false;
|
||||
|
||||
if (!activationKey?.trim()) {
|
||||
errors.activationKey = 'Activation Key not set';
|
||||
disabledNext = true;
|
||||
}
|
||||
|
||||
if (!orgId?.trim()) {
|
||||
errors.orgId = 'Organization ID not set';
|
||||
disabledNext = true;
|
||||
} else if (!/^\d+$/.test(orgId.trim())) {
|
||||
errors.orgId = 'Organization ID should be a numeric value';
|
||||
disabledNext = true;
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
return { errors, disabledNext };
|
||||
}
|
||||
}
|
||||
|
||||
if (registrationType !== 'register-satellite' && !activationKey) {
|
||||
return {
|
||||
errors: { activationKey: 'No activation key selected' },
|
||||
disabledNext: true,
|
||||
|
|
@ -148,10 +172,10 @@ export function useRegistrationValidation(): StepValidation {
|
|||
}
|
||||
|
||||
if (
|
||||
registrationType !== 'register-later' &&
|
||||
registrationType !== 'register-satellite' &&
|
||||
activationKey &&
|
||||
(isFetchingKeyInfo || isErrorKeyInfo)
|
||||
(isFetchingKeyInfo || isErrorKeyInfo) &&
|
||||
!process.env.IS_ON_PREMISE
|
||||
) {
|
||||
return {
|
||||
errors: { activationKey: 'Invalid activation key' },
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ export const RELEASE_LIFECYCLE_URL =
|
|||
export const AZURE_AUTH_URL =
|
||||
'https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow';
|
||||
export const COMPLIANCE_URL = '/insights/compliance/scappolicies';
|
||||
export const ACTIVATION_KEYS_URL = '/insights/connector/activation-keys';
|
||||
export const ACTIVATION_KEYS_URL = !process.env.IS_ON_PREMISE
|
||||
? '/insights/connector/activation-keys'
|
||||
: 'https://console.redhat.com/settings/connector/activation-keys';
|
||||
export const COMPLIANCE_AND_VULN_SCANNING_URL =
|
||||
'https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/security_guide/chap-compliance_and_vulnerability_scanning';
|
||||
export const CREATING_IMAGES_WITH_IB_URL =
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ export type wizardState = {
|
|||
registration: {
|
||||
registrationType: RegistrationType;
|
||||
activationKey: ActivationKeys['name'];
|
||||
orgId: string | undefined;
|
||||
satelliteRegistration: {
|
||||
command: string | undefined;
|
||||
caCert: string | undefined;
|
||||
|
|
@ -226,6 +227,7 @@ export const initialState: wizardState = {
|
|||
? 'register-later'
|
||||
: 'register-now-rhc',
|
||||
activationKey: undefined,
|
||||
orgId: undefined,
|
||||
satelliteRegistration: {
|
||||
command: undefined,
|
||||
caCert: undefined,
|
||||
|
|
@ -381,6 +383,10 @@ export const selectActivationKey = (state: RootState) => {
|
|||
return state.wizard.registration.activationKey;
|
||||
};
|
||||
|
||||
export const selectOrgId = (state: RootState) => {
|
||||
return state.wizard.registration.orgId;
|
||||
};
|
||||
|
||||
export const selectSatelliteRegistrationCommand = (state: RootState) => {
|
||||
return state.wizard.registration.satelliteRegistration?.command;
|
||||
};
|
||||
|
|
@ -682,6 +688,9 @@ export const wizardSlice = createSlice({
|
|||
) => {
|
||||
state.registration.activationKey = action.payload;
|
||||
},
|
||||
changeOrgId: (state, action: PayloadAction<string>) => {
|
||||
state.registration.orgId = action.payload;
|
||||
},
|
||||
changeComplianceType: (state, action: PayloadAction<ComplianceType>) => {
|
||||
state.compliance.complianceType = action.payload;
|
||||
},
|
||||
|
|
@ -1223,6 +1232,7 @@ export const {
|
|||
reinitializeGcp,
|
||||
changeRegistrationType,
|
||||
changeActivationKey,
|
||||
changeOrgId,
|
||||
changeCompliance,
|
||||
changeComplianceType,
|
||||
changeFileSystemConfiguration,
|
||||
|
|
|
|||
|
|
@ -30,10 +30,8 @@ const goToHostnameStep = async () => {
|
|||
});
|
||||
await waitFor(() => user.click(guestImageCheckBox));
|
||||
|
||||
if (!process.env.IS_ON_PREMISE) {
|
||||
await clickNext(); // Registration
|
||||
await clickRegisterLater();
|
||||
}
|
||||
await clickNext(); // Registration
|
||||
await clickRegisterLater();
|
||||
await goToStep(/Hostname/);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue