From b6cdfdb10267bfdd8dac4286efd30ecc87b804b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anna=20V=C3=ADtov=C3=A1?= Date: Wed, 12 Mar 2025 14:12:59 +0100 Subject: [PATCH] Wizard: Add Satellite service and update mapper --- .../SatelliteRegistrationCommand.tsx | 8 +++ .../utilities/requestMapper.ts | 63 +++++++++++++------ .../utilities/useValidation.tsx | 7 ++- src/constants.ts | 22 +++++++ src/store/wizardSlice.ts | 2 +- 5 files changed, 80 insertions(+), 22 deletions(-) diff --git a/src/Components/CreateImageWizard/steps/Registration/components/SatelliteRegistrationCommand.tsx b/src/Components/CreateImageWizard/steps/Registration/components/SatelliteRegistrationCommand.tsx index 3a97e83d..173fbced 100644 --- a/src/Components/CreateImageWizard/steps/Registration/components/SatelliteRegistrationCommand.tsx +++ b/src/Components/CreateImageWizard/steps/Registration/components/SatelliteRegistrationCommand.tsx @@ -7,9 +7,12 @@ import { HelperTextItem, } from '@patternfly/react-core'; +import { SATELLITE_SERVICE } from '../../../../../constants'; import { useAppDispatch, useAppSelector } from '../../../../../store/hooks'; import { + addEnabledService, changeSatelliteRegistrationCommand, + removeEnabledService, selectSatelliteRegistrationCommand, } from '../../../../../store/wizardSlice'; import { useRegistrationValidation } from '../../../utilities/useValidation'; @@ -25,6 +28,11 @@ const SatelliteRegistrationCommand = () => { '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) => { + if (!registrationCommand && !!value) { + dispatch(addEnabledService(SATELLITE_SERVICE)); + } else if (!!registrationCommand && !value) { + dispatch(removeEnabledService(SATELLITE_SERVICE)); + } dispatch(changeSatelliteRegistrationCommand(value)); }; diff --git a/src/Components/CreateImageWizard/utilities/requestMapper.ts b/src/Components/CreateImageWizard/utilities/requestMapper.ts index 2d85f48e..0f0b0a1a 100644 --- a/src/Components/CreateImageWizard/utilities/requestMapper.ts +++ b/src/Components/CreateImageWizard/utilities/requestMapper.ts @@ -6,6 +6,7 @@ import { parseSizeUnit } from './parseSizeUnit'; import { CENTOS_9, FIRST_BOOT_SERVICE_DATA, + SATELLITE_SERVICE_DATA, RHEL_8, RHEL_9, RHEL_9_BETA, @@ -84,6 +85,8 @@ import { selectUsers, selectMetadata, selectFirewall, + selectSatelliteCaCertificate, + selectSatelliteRegistrationCommand, } from '../../../store/wizardSlice'; import isRhel from '../../../Utilities/isRhel'; import { FileSystemConfigurationType } from '../steps/FileSystem'; @@ -443,7 +446,9 @@ const getSatelliteCommand = (files?: File[]): string => { const satelliteCommandFile = files?.find( (file) => file.path === '/usr/local/sbin/register-satellite-cmd' ); - return satelliteCommandFile?.data ? atob(satelliteCommandFile.data) : ''; + return satelliteCommandFile?.data + ? decodeURIComponent(atob(satelliteCommandFile.data)) + : ''; }; const uploadTypeByTargetEnv = (imageType: ImageTypes): UploadTypes => { @@ -528,26 +533,42 @@ const getImageOptions = ( }; const getCustomizations = (state: RootState, orgID: string): Customizations => { + const satCert = selectSatelliteCaCertificate(state); + const files: File[] = []; + if (selectFirstBootScript(state)) { + files.push({ + path: '/etc/systemd/system/custom-first-boot.service', + data: FIRST_BOOT_SERVICE_DATA, + data_encoding: 'base64', + ensure_parents: true, + }); + files.push({ + path: '/usr/local/sbin/custom-first-boot', + data: btoa(selectFirstBootScript(state)), + data_encoding: 'base64', + mode: '0774', + ensure_parents: true, + }); + } + const satCmd = selectSatelliteRegistrationCommand(state); + if (satCmd && selectRegistrationType(state) === 'register-satellite') { + files.push({ + path: '/etc/systemd/system/register-satellite.service', + data: SATELLITE_SERVICE_DATA, + data_encoding: 'base64', + ensure_parents: true, + }); + files.push({ + path: '/usr/local/sbin/register-satellite', + data: btoa(encodeURIComponent(satCmd)), + data_encoding: 'base64', + ensure_parents: true, + }); + } return { containers: undefined, directories: undefined, - files: selectFirstBootScript(state) - ? [ - { - path: '/etc/systemd/system/custom-first-boot.service', - data: FIRST_BOOT_SERVICE_DATA, - data_encoding: 'base64', - ensure_parents: true, - }, - { - path: '/usr/local/sbin/custom-first-boot', - data: btoa(selectFirstBootScript(state)), - data_encoding: 'base64', - mode: '0774', - ensure_parents: true, - }, - ] - : undefined, + files: files.length > 0 ? files : undefined, subscription: getSubscription(state, orgID), packages: getPackages(state), payload_repositories: getPayloadRepositories(state), @@ -567,6 +588,12 @@ const getCustomizations = (state: RootState, orgID: string): Customizations => { ignition: undefined, partitioning_mode: undefined, fips: undefined, + cacerts: + satCert && selectRegistrationType(state) === 'register-satellite' + ? { + pem_certs: [satCert], + } + : undefined, }; }; diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx index 5478fe85..7128d2f9 100644 --- a/src/Components/CreateImageWizard/utilities/useValidation.tsx +++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx @@ -174,12 +174,13 @@ export function useRegistrationValidation(): StepValidation { const token = match[1]; const decoded = jwtDecode(token); if (decoded.exp) { - const currentTime = Date.now() / 1000; - if (decoded.exp < currentTime) { + const currentTimeSeconds = Date.now() / 1000; + const dayInSeconds = 86400; + if (decoded.exp < currentTimeSeconds + dayInSeconds) { const expirationDate = new Date(decoded.exp * 1000); Object.assign(errors, { command: - 'The token is already expired. Expiration date: ' + + 'The token is already expired or will expire by next day. Expiration date: ' + expirationDate, }); return { diff --git a/src/constants.ts b/src/constants.ts index 05db7612..3b418d41 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -261,6 +261,28 @@ WantedBy=graphical.target export const FIRST_BOOT_SERVICE = 'custom-first-boot'; +export const SATELLITE_SERVICE_DATA = btoa(`[Unit] + Description=Custom first boot script + ConditionFileIsExecutable=/usr/local/sbin/register-satellite + ConditionPathExists=!/var/local/.register-satellite-done + Wants=network-online.target + After=network-online.target + After=osbuild-first-boot.service + + [Service] + Type=oneshot + ExecStart=/usr/local/sbin/register-satellite + ExecStartPost=/usr/bin/touch /var/local/.register-satellite-done + RemainAfterExit=yes + + [Install] + WantedBy=basic.target + WantedBy=multi-user.target + WantedBy=graphical.target +`); + +export const SATELLITE_SERVICE = 'register-satellite'; + // For use when calling content API (now required) export enum ContentOrigin { 'REDHAT' = 'red_hat', diff --git a/src/store/wizardSlice.ts b/src/store/wizardSlice.ts index 63acdd0c..98247923 100644 --- a/src/store/wizardSlice.ts +++ b/src/store/wizardSlice.ts @@ -348,7 +348,7 @@ export const selectActivationKey = (state: RootState) => { }; export const selectSatelliteRegistrationCommand = (state: RootState) => { - return state.wizard.registration.satelliteRegistration.command; + return state.wizard.registration.satelliteRegistration?.command; }; export const selectSatelliteCaCertificate = (state: RootState) => {