Wizard: add satellite registration, add jwt-decode
The jwt decode dependency helps us to keep track of the token that is present in the Satellite command. jwt-decode is the most popular dependency for the job, and very easy to use.
This commit is contained in:
parent
739c0538fe
commit
a4034e8787
21 changed files with 20892 additions and 6818 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -292,6 +292,62 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
|
||||
/composes/{id}/download:
|
||||
get:
|
||||
operationId: getComposeDownload
|
||||
summary: Download the artifact for a compose.
|
||||
security:
|
||||
- Bearer: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 123e4567-e89b-12d3-a456-426655440000
|
||||
required: true
|
||||
description: ID of compose to download
|
||||
description: |-
|
||||
Download the artifact of a finished compose.
|
||||
responses:
|
||||
'200':
|
||||
description: The metadata for the given compose.
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
'400':
|
||||
description: Invalid compose id
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'401':
|
||||
description: Auth token is invalid
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'403':
|
||||
description: Unauthorized to perform operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'404':
|
||||
description: Unknown compose id
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
'500':
|
||||
description: Unexpected error occurred
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
|
||||
/composes/{id}/clone:
|
||||
post:
|
||||
operationId: postCloneCompose
|
||||
|
|
|
|||
|
|
@ -18,9 +18,6 @@
|
|||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"uploads"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"api.Artifact": {
|
||||
|
|
@ -518,6 +515,14 @@
|
|||
"description": "Number of consecutive failed introspections",
|
||||
"type": "integer"
|
||||
},
|
||||
"failed_snapshot_count": {
|
||||
"description": "Number of consecutive failed snapshots",
|
||||
"type": "integer"
|
||||
},
|
||||
"feature_name": {
|
||||
"description": "The feature name this repo requires",
|
||||
"type": "string"
|
||||
},
|
||||
"gpg_key": {
|
||||
"description": "GPG key for repository",
|
||||
"type": "string"
|
||||
|
|
@ -775,6 +780,14 @@
|
|||
"description": "Number of consecutive failed introspections",
|
||||
"type": "integer"
|
||||
},
|
||||
"failed_snapshot_count": {
|
||||
"description": "Number of consecutive failed snapshots",
|
||||
"type": "integer"
|
||||
},
|
||||
"feature_name": {
|
||||
"description": "The feature name this repo requires",
|
||||
"type": "string"
|
||||
},
|
||||
"gpg_key": {
|
||||
"description": "GPG key for repository",
|
||||
"type": "string"
|
||||
|
|
|
|||
|
|
@ -747,10 +747,10 @@ components:
|
|||
properties:
|
||||
version:
|
||||
type: string
|
||||
build_time:
|
||||
type: string
|
||||
build_commit:
|
||||
type: string
|
||||
build_time:
|
||||
type: string
|
||||
Readiness:
|
||||
type: object
|
||||
required:
|
||||
|
|
@ -1654,6 +1654,8 @@ components:
|
|||
$ref: '#/components/schemas/FIPS'
|
||||
installer:
|
||||
$ref: '#/components/schemas/Installer'
|
||||
cacerts:
|
||||
$ref: '#/components/schemas/CACertsCustomization'
|
||||
Container:
|
||||
type: object
|
||||
required:
|
||||
|
|
@ -1874,6 +1876,17 @@ components:
|
|||
type: string
|
||||
description: |
|
||||
Enable passwordless sudo for users or groups (groups must be prefixed by %)
|
||||
CACertsCustomization:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- pem_certs
|
||||
properties:
|
||||
pem_certs:
|
||||
type: array
|
||||
example: [ '---BEGIN CERTIFICATE---\nMIIC0DCCAbigAwIBAgIUI...\n---END CERTIFICATE---' ]
|
||||
items:
|
||||
type: string
|
||||
Ignition:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
|
|
|||
13677
package-lock.json
generated
13677
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -19,6 +19,7 @@
|
|||
"@sentry/webpack-plugin": "3.2.2",
|
||||
"@unleash/proxy-client-react": "5.0.0",
|
||||
"classnames": "2.5.1",
|
||||
"jwt-decode": "4.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ test.describe.serial('test', () => {
|
|||
await frame.getByRole('heading', {
|
||||
name: 'Register systems using this image',
|
||||
});
|
||||
await page.getByTestId('automatically-register-checkbox').uncheck();
|
||||
await page.getByTestId('register-later-radio').click();
|
||||
await frame.getByRole('button', { name: 'Next', exact: true }).click();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,9 @@ export const ValidatedInputAndTextArea = ({
|
|||
</HelperTextItem>
|
||||
</HelperText>
|
||||
)}
|
||||
{hasError && <ErrorMessage errorMessage={errorMessage} />}
|
||||
{validated === 'error' && hasError && (
|
||||
<ErrorMessage errorMessage={errorMessage} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ import {
|
|||
Checkbox,
|
||||
FormGroup,
|
||||
Popover,
|
||||
Radio,
|
||||
Text,
|
||||
TextContent,
|
||||
} from '@patternfly/react-core';
|
||||
import { ExternalLinkAltIcon, HelpIcon } from '@patternfly/react-icons';
|
||||
import { useFlag } from '@unleash/proxy-client-react';
|
||||
|
||||
import { INSIGHTS_URL, RHC_URL, RHEL_10_BETA } from '../../../../constants';
|
||||
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
|
||||
|
|
@ -106,6 +108,10 @@ const Registration = () => {
|
|||
registrationType === 'register-later'
|
||||
);
|
||||
|
||||
const isSatelliteRegistrationEnabled = useFlag(
|
||||
'image-builder.satellite.enabled'
|
||||
);
|
||||
|
||||
// TO DO: Remove when rhc starts working for RHEL 10 Beta
|
||||
useEffect(() => {
|
||||
if (distribution === RHEL_10_BETA) {
|
||||
|
|
@ -115,9 +121,9 @@ const Registration = () => {
|
|||
|
||||
return (
|
||||
<FormGroup label="Registration method">
|
||||
<Checkbox
|
||||
<Radio
|
||||
label="Automatically register and enable advanced capabilities"
|
||||
data-testid="automatically-register-checkbox"
|
||||
data-testid="automatically-register-radio"
|
||||
isChecked={
|
||||
registrationType === 'register-now' ||
|
||||
registrationType === 'register-now-insights' ||
|
||||
|
|
@ -129,9 +135,6 @@ const Registration = () => {
|
|||
dispatch(changeRegistrationType('register-now-rhc'));
|
||||
} else if (checked && distribution === RHEL_10_BETA) {
|
||||
dispatch(changeRegistrationType('register-now-insights'));
|
||||
} else {
|
||||
dispatch(changeRegistrationType('register-later'));
|
||||
setShowOptions(false);
|
||||
}
|
||||
}}
|
||||
id="register-system-now"
|
||||
|
|
@ -201,6 +204,30 @@ const Registration = () => {
|
|||
)
|
||||
}
|
||||
/>
|
||||
<Radio
|
||||
label="Register later"
|
||||
data-testid="register-later-radio"
|
||||
isChecked={registrationType === 'register-later'}
|
||||
onChange={() => {
|
||||
dispatch(changeRegistrationType('register-later'));
|
||||
setShowOptions(false);
|
||||
}}
|
||||
id="register-later"
|
||||
name="register-later"
|
||||
/>
|
||||
{isSatelliteRegistrationEnabled && (
|
||||
<Radio
|
||||
label="Register with Satellite"
|
||||
data-testid="register-satellite-radio"
|
||||
isChecked={registrationType === 'register-satellite'}
|
||||
onChange={() => {
|
||||
dispatch(changeRegistrationType('register-satellite'));
|
||||
setShowOptions(false);
|
||||
}}
|
||||
id="register-satellite"
|
||||
name="register-satellite"
|
||||
/>
|
||||
)}
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
DropEvent,
|
||||
FileUpload,
|
||||
Form,
|
||||
FormGroup,
|
||||
FormHelperText,
|
||||
HelperText,
|
||||
HelperTextItem,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import SatelliteRegistrationCommand from './components/SatelliteRegistrationCommand';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../../store/hooks';
|
||||
import {
|
||||
changeSatelliteCaCertificate,
|
||||
selectSatelliteCaCertificate,
|
||||
} from '../../../../store/wizardSlice';
|
||||
import { useRegistrationValidation } from '../../utilities/useValidation';
|
||||
|
||||
const SatelliteRegistration = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const caCertificate = useAppSelector(selectSatelliteCaCertificate);
|
||||
const [isRejected, setIsRejected] = React.useState(false);
|
||||
const stepValidation = useRegistrationValidation();
|
||||
const validated =
|
||||
stepValidation.errors['certificate'] === 'default'
|
||||
? 'default'
|
||||
: stepValidation.errors['certificate']
|
||||
? 'error'
|
||||
: 'success';
|
||||
|
||||
const handleClear = () => {
|
||||
dispatch(changeSatelliteCaCertificate(''));
|
||||
};
|
||||
|
||||
const handleTextChange = (
|
||||
_event: React.ChangeEvent<HTMLTextAreaElement>,
|
||||
value: string
|
||||
) => {
|
||||
dispatch(changeSatelliteCaCertificate(value));
|
||||
};
|
||||
|
||||
const handleDataChange = (_: DropEvent, value: string) => {
|
||||
dispatch(changeSatelliteCaCertificate(value));
|
||||
};
|
||||
|
||||
const handleFileRejected = () => {
|
||||
dispatch(changeSatelliteCaCertificate(''));
|
||||
setIsRejected(true);
|
||||
};
|
||||
return (
|
||||
<Form>
|
||||
<SatelliteRegistrationCommand />
|
||||
<FormGroup label="Certificate authority (CA)" isRequired>
|
||||
<FileUpload
|
||||
id="text-file-with-restrictions-example"
|
||||
type="text"
|
||||
value={caCertificate || ''}
|
||||
filename={caCertificate ? 'CA detected' : ''}
|
||||
onDataChange={handleDataChange}
|
||||
onTextChange={handleTextChange}
|
||||
onClearClick={handleClear}
|
||||
isRequired={true}
|
||||
dropzoneProps={{
|
||||
accept: {
|
||||
'application/x-pem-file': ['.pem'],
|
||||
'application/x-x509-ca-cert': ['.cer', '.crt'],
|
||||
'application/pkix-cert': ['.der'],
|
||||
},
|
||||
maxSize: 512000,
|
||||
onDropRejected: handleFileRejected,
|
||||
}}
|
||||
validated={isRejected ? 'error' : 'default'}
|
||||
browseButtonText="Upload"
|
||||
allowEditingUploadedText={true}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem
|
||||
variant={
|
||||
isRejected || validated === 'error' ? 'error' : 'default'
|
||||
}
|
||||
hasIcon
|
||||
>
|
||||
{isRejected
|
||||
? 'Must be a .PEM/.CER/.CRT file no larger than 512 KB'
|
||||
: validated === 'error'
|
||||
? stepValidation.errors['certificate']
|
||||
: 'Drag and drop a file or upload one'}
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default SatelliteRegistration;
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
FormGroup,
|
||||
FormHelperText,
|
||||
HelperText,
|
||||
HelperTextItem,
|
||||
} from '@patternfly/react-core';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
|
||||
import {
|
||||
changeSatelliteRegistrationCommand,
|
||||
selectSatelliteRegistrationCommand,
|
||||
} from '../../../../../store/wizardSlice';
|
||||
import { useRegistrationValidation } from '../../../utilities/useValidation';
|
||||
import { ValidatedInputAndTextArea } from '../../../ValidatedInput';
|
||||
|
||||
const SatelliteRegistrationCommand = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const registrationCommand = useAppSelector(
|
||||
selectSatelliteRegistrationCommand
|
||||
);
|
||||
const stepValidation = useRegistrationValidation();
|
||||
const registrationDocs =
|
||||
'https://docs.redhat.com/en/documentation/red_hat_satellite/6.16/html-single/managing_hosts/index#Customizing_the_Registration_Templates_managing-hosts';
|
||||
|
||||
const handleChange = (e: React.FormEvent, value: string) => {
|
||||
dispatch(changeSatelliteRegistrationCommand(value));
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup label="Registration command from Satellite" isRequired>
|
||||
<ValidatedInputAndTextArea
|
||||
inputType={'textArea'}
|
||||
ariaLabel="registration command"
|
||||
value={registrationCommand || ''}
|
||||
onChange={handleChange}
|
||||
placeholder="Registration command"
|
||||
stepValidation={stepValidation}
|
||||
fieldName="command"
|
||||
/>
|
||||
<FormHelperText>
|
||||
<HelperText>
|
||||
<HelperTextItem>
|
||||
To generate command from Satellite, follow the{' '}
|
||||
<a href={registrationDocs} target="_blank" rel="noreferrer">
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</HelperTextItem>
|
||||
</HelperText>
|
||||
</FormHelperText>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export default SatelliteRegistrationCommand;
|
||||
|
|
@ -5,6 +5,7 @@ import { Text, Form, Title, FormGroup } from '@patternfly/react-core';
|
|||
import ActivationKeyInformation from './ActivationKeyInformation';
|
||||
import ActivationKeysList from './ActivationKeysList';
|
||||
import Registration from './Registration';
|
||||
import SatelliteRegistration from './SatelliteRegistration';
|
||||
|
||||
import { useAppSelector } from '../../../../store/hooks';
|
||||
import {
|
||||
|
|
@ -26,10 +27,13 @@ const RegistrationStep = () => {
|
|||
system during initial boot.
|
||||
</Text>
|
||||
<Registration />
|
||||
{!process.env.IS_ON_PREMISE && <ActivationKeysList />}
|
||||
{registrationType === 'register-satellite' && <SatelliteRegistration />}
|
||||
{!process.env.IS_ON_PREMISE &&
|
||||
registrationType !== 'register-satellite' && <ActivationKeysList />}
|
||||
{!process.env.IS_ON_PREMISE &&
|
||||
activationKey &&
|
||||
registrationType !== 'register-later' && (
|
||||
registrationType !== 'register-later' &&
|
||||
registrationType !== 'register-satellite' && (
|
||||
<FormGroup
|
||||
label={'Selected activation key'}
|
||||
data-testid="selected-activation-key"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
export const isValidPEM = (cert: string): boolean => {
|
||||
return /-----BEGIN CERTIFICATE-----[\s\S]+-----END CERTIFICATE-----/.test(
|
||||
cert.trim()
|
||||
);
|
||||
};
|
||||
|
|
@ -371,10 +371,16 @@ export const mapRequestToState = (request: BlueprintResponse): wizardState => {
|
|||
? request.customizations.subscription.rhc
|
||||
? 'register-now-rhc'
|
||||
: 'register-now-insights'
|
||||
: request.customizations.cacerts
|
||||
? 'register-satellite'
|
||||
: 'register-later',
|
||||
activationKey: isRhel(request.distribution)
|
||||
? request.customizations.subscription?.['activation-key']
|
||||
: undefined,
|
||||
satelliteRegistration: {
|
||||
command: getSatelliteCommand(request.customizations.files),
|
||||
caCert: request.customizations.cacerts?.pem_certs[0],
|
||||
},
|
||||
},
|
||||
...commonRequestToState(request),
|
||||
};
|
||||
|
|
@ -433,6 +439,13 @@ const getImageRequests = (state: RootState): ImageRequest[] => {
|
|||
}));
|
||||
};
|
||||
|
||||
const getSatelliteCommand = (files?: File[]): string => {
|
||||
const satelliteCommandFile = files?.find(
|
||||
(file) => file.path === '/usr/local/sbin/register-satellite-cmd'
|
||||
);
|
||||
return satelliteCommandFile?.data ? atob(satelliteCommandFile.data) : '';
|
||||
};
|
||||
|
||||
const uploadTypeByTargetEnv = (imageType: ImageTypes): UploadTypes => {
|
||||
switch (imageType) {
|
||||
case 'aws':
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { CheckCircleIcon } from '@patternfly/react-icons';
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
|
||||
import { isValidPEM } from './certificates';
|
||||
|
||||
import { UNIQUE_VALIDATION_DELAY } from '../../../constants';
|
||||
import { useLazyGetBlueprintsQuery } from '../../../store/backendApi';
|
||||
|
|
@ -34,6 +37,8 @@ import {
|
|||
selectKeyboard,
|
||||
selectTimezone,
|
||||
selectImageTypes,
|
||||
selectSatelliteCaCertificate,
|
||||
selectSatelliteRegistrationCommand,
|
||||
} from '../../../store/wizardSlice';
|
||||
import { keyboardsList } from '../steps/Locale/keyboardsList';
|
||||
import { languagesList } from '../steps/Locale/languagesList';
|
||||
|
|
@ -114,6 +119,10 @@ type ValidationState = {
|
|||
export function useRegistrationValidation(): StepValidation {
|
||||
const registrationType = useAppSelector(selectRegistrationType);
|
||||
const activationKey = useAppSelector(selectActivationKey);
|
||||
const registrationCommand = useAppSelector(
|
||||
selectSatelliteRegistrationCommand
|
||||
);
|
||||
const caCertificate = useAppSelector(selectSatelliteCaCertificate);
|
||||
|
||||
const {
|
||||
isUninitialized,
|
||||
|
|
@ -150,6 +159,57 @@ export function useRegistrationValidation(): StepValidation {
|
|||
};
|
||||
}
|
||||
|
||||
if (registrationType === 'register-satellite') {
|
||||
const errors = {};
|
||||
if (caCertificate && (caCertificate === '' || !isValidPEM(caCertificate))) {
|
||||
Object.assign(errors, {
|
||||
certificate:
|
||||
'Valid certificate must be present if you are registering Satellite.',
|
||||
});
|
||||
}
|
||||
if (registrationCommand === '' || !registrationCommand) {
|
||||
Object.assign(errors, {
|
||||
command: 'No registration command for Satellite registration',
|
||||
});
|
||||
}
|
||||
try {
|
||||
const match = registrationCommand?.match(
|
||||
/Bearer\s+([\w-]+\.[\w-]+\.[\w-]+)/
|
||||
);
|
||||
if (!match) {
|
||||
Object.assign(errors, { command: 'Invalid or missing token' });
|
||||
} else {
|
||||
const token = match[1];
|
||||
const decoded = jwtDecode(token);
|
||||
if (decoded.exp) {
|
||||
const currentTime = Date.now() / 1000;
|
||||
if (decoded.exp < currentTime) {
|
||||
const expirationDate = new Date(decoded.exp * 1000);
|
||||
Object.assign(errors, {
|
||||
command:
|
||||
'The token is already expired. Expiration date: ' +
|
||||
expirationDate,
|
||||
});
|
||||
return {
|
||||
errors: errors,
|
||||
disabledNext:
|
||||
caCertificate === undefined || !isValidPEM(caCertificate),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Object.assign(errors, { command: 'Invalid or missing token' });
|
||||
}
|
||||
return {
|
||||
errors: errors,
|
||||
disabledNext:
|
||||
Object.keys(errors).length > 0 ||
|
||||
caCertificate === undefined ||
|
||||
!isValidPEM(caCertificate),
|
||||
};
|
||||
}
|
||||
|
||||
return { errors: {}, disabledNext: false };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -278,6 +278,10 @@ export type ApiRepositoryResponse = {
|
|||
distribution_versions?: string[] | undefined;
|
||||
/** Number of consecutive failed introspections */
|
||||
failed_introspections_count?: number | undefined;
|
||||
/** Number of consecutive failed snapshots */
|
||||
failed_snapshot_count?: number | undefined;
|
||||
/** The feature name this repo requires */
|
||||
feature_name?: string | undefined;
|
||||
/** GPG key for repository */
|
||||
gpg_key?: string | undefined;
|
||||
/** Label used to configure the yum repository on clients */
|
||||
|
|
@ -328,6 +332,10 @@ export type ApiRepositoryResponseRead = {
|
|||
distribution_versions?: string[] | undefined;
|
||||
/** Number of consecutive failed introspections */
|
||||
failed_introspections_count?: number | undefined;
|
||||
/** Number of consecutive failed snapshots */
|
||||
failed_snapshot_count?: number | undefined;
|
||||
/** The feature name this repo requires */
|
||||
feature_name?: string | undefined;
|
||||
/** GPG key for repository */
|
||||
gpg_key?: string | undefined;
|
||||
/** Label used to configure the yum repository on clients */
|
||||
|
|
@ -448,6 +456,10 @@ export type ApiRepositoryImportResponse = {
|
|||
distribution_versions?: string[] | undefined;
|
||||
/** Number of consecutive failed introspections */
|
||||
failed_introspections_count?: number | undefined;
|
||||
/** Number of consecutive failed snapshots */
|
||||
failed_snapshot_count?: number | undefined;
|
||||
/** The feature name this repo requires */
|
||||
feature_name?: string | undefined;
|
||||
/** GPG key for repository */
|
||||
gpg_key?: string | undefined;
|
||||
/** Label used to configure the yum repository on clients */
|
||||
|
|
@ -504,6 +516,10 @@ export type ApiRepositoryImportResponseRead = {
|
|||
distribution_versions?: string[] | undefined;
|
||||
/** Number of consecutive failed introspections */
|
||||
failed_introspections_count?: number | undefined;
|
||||
/** Number of consecutive failed snapshots */
|
||||
failed_snapshot_count?: number | undefined;
|
||||
/** The feature name this repo requires */
|
||||
feature_name?: string | undefined;
|
||||
/** GPG key for repository */
|
||||
gpg_key?: string | undefined;
|
||||
/** Label used to configure the yum repository on clients */
|
||||
|
|
|
|||
|
|
@ -702,6 +702,9 @@ export type Installer = {
|
|||
unattended?: boolean | undefined;
|
||||
"sudo-nopasswd"?: string[] | undefined;
|
||||
};
|
||||
export type CaCertsCustomization = {
|
||||
pem_certs: string[];
|
||||
};
|
||||
export type Customizations = {
|
||||
containers?: Container[] | undefined;
|
||||
directories?: Directory[] | undefined;
|
||||
|
|
@ -739,6 +742,7 @@ export type Customizations = {
|
|||
partitioning_mode?: ("raw" | "lvm" | "auto-lvm") | undefined;
|
||||
fips?: Fips | undefined;
|
||||
installer?: Installer | undefined;
|
||||
cacerts?: CaCertsCustomization | undefined;
|
||||
};
|
||||
export type BlueprintMetadata = {
|
||||
parent_id: string | null;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ export type RegistrationType =
|
|||
| 'register-later'
|
||||
| 'register-now'
|
||||
| 'register-now-insights'
|
||||
| 'register-now-rhc';
|
||||
| 'register-now-rhc'
|
||||
| 'register-satellite';
|
||||
|
||||
export type ComplianceType = 'openscap' | 'compliance';
|
||||
|
||||
|
|
@ -108,6 +109,10 @@ export type wizardState = {
|
|||
registration: {
|
||||
registrationType: RegistrationType;
|
||||
activationKey: ActivationKeys['name'];
|
||||
satelliteRegistration: {
|
||||
command: string | undefined;
|
||||
caCert: string | undefined;
|
||||
};
|
||||
};
|
||||
compliance: {
|
||||
complianceType: ComplianceType;
|
||||
|
|
@ -197,6 +202,10 @@ export const initialState: wizardState = {
|
|||
? 'register-later'
|
||||
: 'register-now-rhc',
|
||||
activationKey: undefined,
|
||||
satelliteRegistration: {
|
||||
command: undefined,
|
||||
caCert: undefined,
|
||||
},
|
||||
},
|
||||
compliance: {
|
||||
complianceType: 'openscap',
|
||||
|
|
@ -337,6 +346,14 @@ export const selectActivationKey = (state: RootState) => {
|
|||
return state.wizard.registration.activationKey;
|
||||
};
|
||||
|
||||
export const selectSatelliteRegistrationCommand = (state: RootState) => {
|
||||
return state.wizard.registration.satelliteRegistration.command;
|
||||
};
|
||||
|
||||
export const selectSatelliteCaCertificate = (state: RootState) => {
|
||||
return state.wizard.registration.satelliteRegistration.caCert;
|
||||
};
|
||||
|
||||
export const selectComplianceProfileID = (state: RootState) => {
|
||||
return state.wizard.compliance.profileID;
|
||||
};
|
||||
|
|
@ -579,6 +596,15 @@ export const wizardSlice = createSlice({
|
|||
) => {
|
||||
state.registration.registrationType = action.payload;
|
||||
},
|
||||
changeSatelliteRegistrationCommand: (
|
||||
state,
|
||||
action: PayloadAction<string>
|
||||
) => {
|
||||
state.registration.satelliteRegistration.command = action.payload;
|
||||
},
|
||||
changeSatelliteCaCertificate: (state, action: PayloadAction<string>) => {
|
||||
state.registration.satelliteRegistration.caCert = action.payload;
|
||||
},
|
||||
changeActivationKey: (
|
||||
state,
|
||||
action: PayloadAction<ActivationKeys['name']>
|
||||
|
|
@ -1131,6 +1157,8 @@ export const {
|
|||
addEnabledFirewallService,
|
||||
removeEnabledFirewallService,
|
||||
changeTimezone,
|
||||
changeSatelliteRegistrationCommand,
|
||||
changeSatelliteCaCertificate,
|
||||
addNtpServer,
|
||||
removeNtpServer,
|
||||
changeHostname,
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ describe('Import modal', () => {
|
|||
'Automatically register and enable advanced capabilities'
|
||||
);
|
||||
const registrationCheckbox = await screen.findByTestId(
|
||||
'automatically-register-checkbox'
|
||||
'automatically-register-radio'
|
||||
);
|
||||
expect(registrationCheckbox).toHaveFocus();
|
||||
await screen.findByRole('textbox', {
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ describe('Keyboard accessibility', () => {
|
|||
'Automatically register and enable advanced capabilities'
|
||||
);
|
||||
const registrationCheckbox = await screen.findByTestId(
|
||||
'automatically-register-checkbox'
|
||||
'automatically-register-radio'
|
||||
);
|
||||
expect(registrationCheckbox).toHaveFocus();
|
||||
await screen.findByRole('textbox', {
|
||||
|
|
|
|||
|
|
@ -116,10 +116,10 @@ export const clickRegisterLater = async () => {
|
|||
await screen.findByRole('heading', {
|
||||
name: /Register systems using this image/,
|
||||
});
|
||||
const registrationCheckbox = await screen.findByRole('checkbox', {
|
||||
name: /automatically register and enable advanced capabilities/i,
|
||||
const registerLaterRadio = await screen.findByRole('radio', {
|
||||
name: /register later/i,
|
||||
});
|
||||
await waitFor(() => user.click(registrationCheckbox));
|
||||
await waitFor(() => user.click(registerLaterRadio));
|
||||
};
|
||||
|
||||
export const goToOscapStep = async () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue