diff --git a/cockpit/webpack.config.ts b/cockpit/webpack.config.ts index 910bc9c56..00a170fdb 100644 --- a/cockpit/webpack.config.ts +++ b/cockpit/webpack.config.ts @@ -27,6 +27,9 @@ module.exports = { mode, devtool, plugins, + devServer: { + historyApiFallback: true, // Ensures all routes are served with `index.html` + }, resolve: { fallback: { path: require.resolve('path-browserify'), diff --git a/fec.config.js b/fec.config.js index 86ddb935d..8bfa0a782 100644 --- a/fec.config.js +++ b/fec.config.js @@ -112,6 +112,7 @@ module.exports = { // to false cockpit: false, 'cockpit/fsinfo': false, + 'os-release': false, }, }, routes: { diff --git a/src/AppCockpit.tsx b/src/AppCockpit.tsx index 76da43a0e..6384a8fc2 100644 --- a/src/AppCockpit.tsx +++ b/src/AppCockpit.tsx @@ -6,7 +6,7 @@ import React from 'react'; import NotificationsPortal from '@redhat-cloud-services/frontend-components-notifications/NotificationPortal'; import { createRoot } from 'react-dom/client'; import { Provider } from 'react-redux'; -import { BrowserRouter } from 'react-router-dom'; +import { HashRouter } from 'react-router-dom'; import { Router } from './Router'; import { onPremStore as store } from './store'; @@ -15,9 +15,9 @@ const Application = () => { return ( - + - + ); }; diff --git a/src/Components/Blueprints/BlueprintDiffModal.tsx b/src/Components/Blueprints/BlueprintDiffModal.tsx index 8501fa7b2..bddf26d59 100644 --- a/src/Components/Blueprints/BlueprintDiffModal.tsx +++ b/src/Components/Blueprints/BlueprintDiffModal.tsx @@ -5,9 +5,9 @@ import { Button, Modal, ModalVariant } from '@patternfly/react-core'; import { BuildImagesButton } from './BuildImagesButton'; +import { useGetBlueprintQuery } from '../../store/backendApi'; import { selectSelectedBlueprintId } from '../../store/BlueprintSlice'; import { useAppSelector } from '../../store/hooks'; -import { useGetBlueprintQuery } from '../../store/imageBuilderApi'; type blueprintDiffProps = { // baseVersion is the version of the blueprint to compare the latest version against diff --git a/src/Components/Blueprints/BuildImagesButton.tsx b/src/Components/Blueprints/BuildImagesButton.tsx index 25e364ff9..c486322a8 100644 --- a/src/Components/Blueprints/BuildImagesButton.tsx +++ b/src/Components/Blueprints/BuildImagesButton.tsx @@ -19,12 +19,12 @@ import { addNotification } from '@redhat-cloud-services/frontend-components-noti import { skipToken } from '@reduxjs/toolkit/query'; import { targetOptions } from '../../constants'; +import { useGetBlueprintQuery } from '../../store/backendApi'; import { selectSelectedBlueprintId } from '../../store/BlueprintSlice'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { ImageTypes, useComposeBlueprintMutation, - useGetBlueprintQuery, } from '../../store/imageBuilderApi'; type BuildImagesButtonPropTypes = { diff --git a/src/Components/CreateImageWizard/CreateImageWizard.tsx b/src/Components/CreateImageWizard/CreateImageWizard.tsx index 6a6dd2677..ceb80b977 100644 --- a/src/Components/CreateImageWizard/CreateImageWizard.tsx +++ b/src/Components/CreateImageWizard/CreateImageWizard.tsx @@ -34,6 +34,7 @@ import Azure from './steps/TargetEnvironment/Azure'; import Gcp from './steps/TargetEnvironment/Gcp'; import TimezoneStep from './steps/Timezone'; import UsersStep from './steps/Users'; +import { getHostArch, getHostDistro } from './utilities/getHostInfo'; import { useFilesystemValidation, useSnapshotValidation, @@ -192,6 +193,24 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { if (searchParams.get('target') === 'qcow2') { dispatch(addImageType('guest-image')); } + + const initializeHostDistro = async () => { + const distro = await getHostDistro(); + dispatch(changeDistribution(distro)); + }; + + const initializeHostArch = async () => { + const arch = await getHostArch(); + dispatch(changeArchitecture(arch)); + }; + + if (process.env.IS_ON_PREMISE) { + if (!searchParams.get('release')) { + initializeHostDistro(); + } + + initializeHostArch(); + } // This useEffect hook should run *only* on mount and therefore has an empty // dependency array. eslint's exhaustive-deps rule does not support this use. // eslint-disable-next-line react-hooks/exhaustive-deps @@ -391,7 +410,9 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { name={complianceEnabled ? 'Compliance' : 'OpenSCAP'} id="step-oscap" key="step-oscap" - isHidden={distribution === RHEL_10_BETA} + isHidden={ + distribution === RHEL_10_BETA || !!process.env.IS_ON_PREMISE + } footer={ } @@ -442,7 +463,9 @@ const CreateImageWizard = ({ isEdit }: CreateImageWizardProps) => { name="Custom repositories" id="wizard-custom-repositories" key="wizard-custom-repositories" - isHidden={distribution === RHEL_10_BETA} + isHidden={ + distribution === RHEL_10_BETA || !!process.env.IS_ON_PREMISE + } isDisabled={snapshotValidation.disabledNext} footer={ diff --git a/src/Components/CreateImageWizard/EditImageWizard.tsx b/src/Components/CreateImageWizard/EditImageWizard.tsx index 31721de5f..cf30de0b6 100644 --- a/src/Components/CreateImageWizard/EditImageWizard.tsx +++ b/src/Components/CreateImageWizard/EditImageWizard.tsx @@ -5,8 +5,8 @@ import { useNavigate } from 'react-router-dom'; import CreateImageWizard from './CreateImageWizard'; import { mapRequestToState } from './utilities/requestMapper'; +import { useGetBlueprintQuery } from '../../store/backendApi'; import { useAppDispatch } from '../../store/hooks'; -import { useGetBlueprintQuery } from '../../store/imageBuilderApi'; import { loadWizardState } from '../../store/wizardSlice'; import { resolveRelPath } from '../../Utilities/path'; diff --git a/src/Components/CreateImageWizard/steps/ImageOutput/ArchSelect.tsx b/src/Components/CreateImageWizard/steps/ImageOutput/ArchSelect.tsx index 3e8669eb6..32fe90c72 100644 --- a/src/Components/CreateImageWizard/steps/ImageOutput/ArchSelect.tsx +++ b/src/Components/CreateImageWizard/steps/ImageOutput/ArchSelect.tsx @@ -7,7 +7,7 @@ import { SelectVariant, } from '@patternfly/react-core/deprecated'; -import { ARCHS } from '../../../../constants'; +import { ARCHES } from '../../../../constants'; import { useAppDispatch, useAppSelector } from '../../../../store/hooks'; import { ImageRequest } from '../../../../store/imageBuilderApi'; import { @@ -30,7 +30,15 @@ const ArchSelect = () => { const setSelectOptions = () => { const options: ReactElement[] = []; - ARCHS.forEach((arch) => { + const arches = ARCHES.filter((a) => { + // we don't want to support cross-arch + // builds for on-prem for now + if (process.env.IS_ON_PREMISE) { + return a === arch; + } + return true; + }); + arches.forEach((arch) => { options.push( {arch} diff --git a/src/Components/CreateImageWizard/steps/ImageOutput/ReleaseSelect.tsx b/src/Components/CreateImageWizard/steps/ImageOutput/ReleaseSelect.tsx index 8c0f73ae0..d6ef70262 100644 --- a/src/Components/CreateImageWizard/steps/ImageOutput/ReleaseSelect.tsx +++ b/src/Components/CreateImageWizard/steps/ImageOutput/ReleaseSelect.tsx @@ -17,6 +17,7 @@ import { RHEL_9_FULL_SUPPORT, RHEL_9_MAINTENANCE_SUPPORT, RHEL_10_BETA, + ON_PREM_RELEASES, } from '../../../../constants'; import { useAppDispatch, useAppSelector } from '../../../../store/hooks'; import { Distributions } from '../../../../store/imageBuilderApi'; @@ -40,6 +41,8 @@ const ReleaseSelect = () => { const isRHEL9BetaEnabled = useFlag('image-builder.rhel9.beta.enabled'); const isRHEL10BetaEnabled = useFlag('image-builder.rhel10.beta.enabled'); + const releases = process.env.IS_ON_PREMISE ? ON_PREM_RELEASES : RELEASES; + const handleSelect = (_event: React.MouseEvent, selection: Distributions) => { dispatch(changeDistribution(selection)); setIsOpen(false); @@ -75,7 +78,11 @@ const ReleaseSelect = () => { const setSelectOptions = () => { const options: ReactElement[] = []; const filteredRhel = new Map( - [...RELEASES].filter(([key]) => { + [...releases].filter(([key]) => { + if (process.env.IS_ON_PREMISE) { + return key === distribution; + } + if (key === RHEL_9_BETA) { return isRHEL9BetaEnabled; } @@ -99,7 +106,7 @@ const ReleaseSelect = () => { value={key} description={setDescription(key as Distributions)} > - {RELEASES.get(key)} + {releases.get(key)} ); }); @@ -114,14 +121,17 @@ const ReleaseSelect = () => { variant={SelectVariant.single} onToggle={() => setIsOpen(!isOpen)} onSelect={handleSelect} - selections={RELEASES.get(distribution)} + selections={releases.get(distribution)} isOpen={isOpen} - {...(!showDevelopmentOptions && { - loadingVariant: { - text: 'Show options for further development of RHEL', - onClick: handleExpand, - }, - })} + {...(!showDevelopmentOptions && + // Hide this for on-prem since the host + // could be centos or fedora + !process.env.IS_ON_PREMISE && { + loadingVariant: { + text: 'Show options for further development of RHEL', + onClick: handleExpand, + }, + })} > {setSelectOptions()} diff --git a/src/Components/CreateImageWizard/steps/ImageOutput/TargetEnvironment.tsx b/src/Components/CreateImageWizard/steps/ImageOutput/TargetEnvironment.tsx index dacd1f021..481159637 100644 --- a/src/Components/CreateImageWizard/steps/ImageOutput/TargetEnvironment.tsx +++ b/src/Components/CreateImageWizard/steps/ImageOutput/TargetEnvironment.tsx @@ -12,13 +12,10 @@ import { Tile, } from '@patternfly/react-core'; import { HelpIcon } from '@patternfly/react-icons'; -import { useFlag } from '@unleash/proxy-client-react'; +import { useGetArchitecturesQuery } from '../../../../store/backendApi'; import { useAppSelector, useAppDispatch } from '../../../../store/hooks'; -import { - ImageTypes, - useGetArchitecturesQuery, -} from '../../../../store/imageBuilderApi'; +import { ImageTypes } from '../../../../store/imageBuilderApi'; import { provisioningApi } from '../../../../store/provisioningApi'; import { rhsmApi } from '../../../../store/rhsmApi'; import { @@ -31,6 +28,7 @@ import { selectDistribution, selectImageTypes, } from '../../../../store/wizardSlice'; +import { useFlag } from '../../../../Utilities/useGetEnvironment'; const TargetEnvironment = () => { const arch = useAppSelector(selectArchitecture); diff --git a/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx b/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx index d883a0219..00f0044b9 100644 --- a/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx +++ b/src/Components/CreateImageWizard/steps/Oscap/Oscap.tsx @@ -24,6 +24,7 @@ import { RHEL_10_BETA, RHEL_10, } from '../../../../constants'; +import { useGetOscapProfilesQuery } from '../../../../store/backendApi'; import { usePoliciesQuery, PolicyRead } from '../../../../store/complianceApi'; import { useAppDispatch, useAppSelector } from '../../../../store/hooks'; import { @@ -32,7 +33,6 @@ import { Filesystem, OpenScapProfile, useGetOscapCustomizationsQuery, - useGetOscapProfilesQuery, useLazyGetOscapCustomizationsQuery, Services, } from '../../../../store/imageBuilderApi'; diff --git a/src/Components/CreateImageWizard/steps/Oscap/index.tsx b/src/Components/CreateImageWizard/steps/Oscap/index.tsx index ae2926562..192f94255 100644 --- a/src/Components/CreateImageWizard/steps/Oscap/index.tsx +++ b/src/Components/CreateImageWizard/steps/Oscap/index.tsx @@ -9,7 +9,6 @@ import { Title, } from '@patternfly/react-core'; import { ExternalLinkAltIcon } from '@patternfly/react-icons'; -import { useFlag } from '@unleash/proxy-client-react'; import { Oscap, removeBetaFromRelease } from './Oscap'; @@ -18,7 +17,7 @@ import { COMPLIANCE_PROD_URL, COMPLIANCE_STAGE_URL, } from '../../../../constants'; -import { imageBuilderApi } from '../../../../store/enhancedImageBuilderApi'; +import { useBackendPrefetch } from '../../../../store/backendApi'; import { useAppDispatch, useAppSelector } from '../../../../store/hooks'; import { useGetOscapCustomizationsQuery } from '../../../../store/imageBuilderApi'; import { @@ -35,17 +34,17 @@ import { selectComplianceType, clearKernelAppend, } from '../../../../store/wizardSlice'; -import { useGetEnvironment } from '../../../../Utilities/useGetEnvironment'; +import { + useFlag, + useGetEnvironment, +} from '../../../../Utilities/useGetEnvironment'; const OscapStep = () => { const dispatch = useAppDispatch(); const complianceEnabled = useFlag('image-builder.compliance.enabled'); const complianceType = useAppSelector(selectComplianceType); const profileID = useAppSelector(selectComplianceProfileID); - const prefetchOscapProfile = imageBuilderApi.usePrefetch( - 'getOscapProfiles', - {} - ); + const prefetchOscapProfile = useBackendPrefetch('getOscapProfiles', {}); const { isProd } = useGetEnvironment(); const release = removeBetaFromRelease(useAppSelector(selectDistribution)); const { data: currentProfileData } = useGetOscapCustomizationsQuery( diff --git a/src/Components/CreateImageWizard/steps/Packages/Packages.tsx b/src/Components/CreateImageWizard/steps/Packages/Packages.tsx index c662e45cf..52eeefb98 100644 --- a/src/Components/CreateImageWizard/steps/Packages/Packages.tsx +++ b/src/Components/CreateImageWizard/steps/Packages/Packages.tsx @@ -49,6 +49,7 @@ import { EPEL_9_REPO_DEFINITION, RH_ICON_SIZE, } from '../../../../constants'; +import { useGetArchitecturesQuery } from '../../../../store/backendApi'; import { ApiRepositoryResponseRead, useCreateRepositoryMutation, @@ -57,10 +58,7 @@ import { useSearchPackageGroupMutation, } from '../../../../store/contentSourcesApi'; import { useAppSelector } from '../../../../store/hooks'; -import { - Package, - useGetArchitecturesQuery, -} from '../../../../store/imageBuilderApi'; +import { Package } from '../../../../store/imageBuilderApi'; import { selectArchitecture, selectPackages, diff --git a/src/Components/CreateImageWizard/steps/Packages/index.tsx b/src/Components/CreateImageWizard/steps/Packages/index.tsx index ed8702308..35501ddd6 100644 --- a/src/Components/CreateImageWizard/steps/Packages/index.tsx +++ b/src/Components/CreateImageWizard/steps/Packages/index.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Alert, Text, Form, Title } from '@patternfly/react-core'; -import { useFlag } from '@unleash/proxy-client-react'; import PackageRecommendations from './PackageRecommendations'; import Packages from './Packages'; @@ -9,6 +8,7 @@ import Packages from './Packages'; import { RHEL_8, RHEL_9 } from '../../../../constants'; import { useAppSelector } from '../../../../store/hooks'; import { selectDistribution } from '../../../../store/wizardSlice'; +import { useFlag } from '../../../../Utilities/useGetEnvironment'; const PackagesStep = () => { const packageRecommendationsFlag = useFlag('image-builder.pkgrecs.enabled'); diff --git a/src/Components/CreateImageWizard/steps/Registration/index.tsx b/src/Components/CreateImageWizard/steps/Registration/index.tsx index 5a1900156..0acaa5589 100644 --- a/src/Components/CreateImageWizard/steps/Registration/index.tsx +++ b/src/Components/CreateImageWizard/steps/Registration/index.tsx @@ -26,15 +26,17 @@ const RegistrationStep = () => { system during initial boot. - - {activationKey && registrationType !== 'register-later' && ( - - - - )} + {!process.env.IS_ON_PREMISE && } + {!process.env.IS_ON_PREMISE && + activationKey && + registrationType !== 'register-later' && ( + + + + )} ); }; diff --git a/src/Components/CreateImageWizard/steps/Review/ReviewStepTextLists.tsx b/src/Components/CreateImageWizard/steps/Review/ReviewStepTextLists.tsx index e6d0c6c46..55744870b 100644 --- a/src/Components/CreateImageWizard/steps/Review/ReviewStepTextLists.tsx +++ b/src/Components/CreateImageWizard/steps/Review/ReviewStepTextLists.tsx @@ -24,6 +24,7 @@ import { import { FSReviewTable } from './ReviewStepTables'; import { + ON_PREM_RELEASES, RELEASES, RHEL_8, RHEL_8_FULL_SUPPORT, @@ -93,6 +94,7 @@ const ExpirationWarning = () => { export const ImageOutputList = () => { const distribution = useAppSelector(selectDistribution); const arch = useAppSelector(selectArchitecture); + const releases = process.env.IS_ON_PREMISE ? ON_PREM_RELEASES : RELEASES; return ( {distribution === RHEL_8 && ( @@ -118,7 +120,7 @@ export const ImageOutputList = () => { Release - {RELEASES.get(distribution)} + {releases.get(distribution)} Architecture diff --git a/src/Components/CreateImageWizard/utilities/getHostInfo.ts b/src/Components/CreateImageWizard/utilities/getHostInfo.ts new file mode 100644 index 000000000..beff5a858 --- /dev/null +++ b/src/Components/CreateImageWizard/utilities/getHostInfo.ts @@ -0,0 +1,18 @@ +import cockpit from 'cockpit'; +import { read_os_release } from 'os-release'; + +import { Distributions } from '../../../store/imageBuilderApi'; + +export const getHostDistro = async () => { + const osRel = await read_os_release(); + return `${osRel.ID}-${osRel.VERSION_ID}` as Distributions; +}; + +type Architecture = 'x86_64' | 'aarch64'; +export const getHostArch = async () => { + const hostArch = await cockpit.spawn(['uname', '-m'], { + superuser: 'try', + }); + + return (hostArch as string).trim() as Architecture; +}; diff --git a/src/Components/CreateImageWizard/utilities/useValidation.tsx b/src/Components/CreateImageWizard/utilities/useValidation.tsx index 25aba3631..433039eeb 100644 --- a/src/Components/CreateImageWizard/utilities/useValidation.tsx +++ b/src/Components/CreateImageWizard/utilities/useValidation.tsx @@ -1,11 +1,9 @@ import { useEffect, useState } from 'react'; import { UNIQUE_VALIDATION_DELAY } from '../../../constants'; +import { useLazyGetBlueprintsQuery } from '../../../store/backendApi'; import { useAppSelector } from '../../../store/hooks'; -import { - BlueprintsResponse, - useLazyGetBlueprintsQuery, -} from '../../../store/imageBuilderApi'; +import { BlueprintsResponse } from '../../../store/imageBuilderApi'; import { useShowActivationKeyQuery } from '../../../store/rhsmApi'; import { selectBlueprintId, diff --git a/src/Components/edge/ImageDetails.tsx b/src/Components/edge/ImageDetails.tsx index 1b15829c6..89d45586a 100644 --- a/src/Components/edge/ImageDetails.tsx +++ b/src/Components/edge/ImageDetails.tsx @@ -3,7 +3,6 @@ import React from 'react'; import AsyncComponent from '@redhat-cloud-services/frontend-components/AsyncComponent'; import ErrorState from '@redhat-cloud-services/frontend-components/ErrorState'; import Unavailable from '@redhat-cloud-services/frontend-components/Unavailable'; -import { useFlag } from '@unleash/proxy-client-react'; import { useDispatch } from 'react-redux'; import { useNavigate, useLocation, useParams } from 'react-router-dom'; @@ -12,6 +11,7 @@ import { manageEdgeImagesUrlName, } from '../../Utilities/edge'; import { resolveRelPath } from '../../Utilities/path'; +import { useFlag } from '../../Utilities/useGetEnvironment'; const ImageDetail = () => { const dispatch = useDispatch(); diff --git a/src/Components/edge/ImagesTable.tsx b/src/Components/edge/ImagesTable.tsx index b8c9a17ea..d8c54de69 100644 --- a/src/Components/edge/ImagesTable.tsx +++ b/src/Components/edge/ImagesTable.tsx @@ -3,7 +3,6 @@ import React from 'react'; import AsyncComponent from '@redhat-cloud-services/frontend-components/AsyncComponent'; import ErrorState from '@redhat-cloud-services/frontend-components/ErrorState'; import Unavailable from '@redhat-cloud-services/frontend-components/Unavailable'; -import { useFlag } from '@unleash/proxy-client-react'; import { useDispatch } from 'react-redux'; import { useNavigate, useLocation } from 'react-router-dom'; @@ -13,6 +12,7 @@ import { manageEdgeImagesUrlName, } from '../../Utilities/edge'; import { resolveRelPath } from '../../Utilities/path'; +import { useFlag } from '../../Utilities/useGetEnvironment'; const ImagesTable = () => { const dispatch = useDispatch(); diff --git a/src/Components/sharedComponents/ImageBuilderHeader.tsx b/src/Components/sharedComponents/ImageBuilderHeader.tsx index ce2a71276..b911c6317 100644 --- a/src/Components/sharedComponents/ImageBuilderHeader.tsx +++ b/src/Components/sharedComponents/ImageBuilderHeader.tsx @@ -24,8 +24,8 @@ import { CREATING_IMAGES_WITH_IB_SERVICE_URL, OSBUILD_SERVICE_ARCHITECTURE_URL, } from '../../constants'; +import { useBackendPrefetch } from '../../store/backendApi'; import { useAppSelector } from '../../store/hooks'; -import { imageBuilderApi } from '../../store/imageBuilderApi'; import { selectDistribution } from '../../store/wizardSlice'; import { resolveRelPath } from '../../Utilities/path'; import './ImageBuilderHeader.scss'; @@ -99,7 +99,7 @@ export const ImageBuilderHeader = ({ const navigate = useNavigate(); const distribution = useAppSelector(selectDistribution); - const prefetchTargets = imageBuilderApi.usePrefetch('getArchitectures'); + const prefetchTargets = useBackendPrefetch('getArchitectures'); const importExportFlag = useFlagWithEphemDefault( 'image-builder.import.enabled' diff --git a/src/Utilities/path.ts b/src/Utilities/path.ts index 15cafc6e1..16d9d52c5 100644 --- a/src/Utilities/path.ts +++ b/src/Utilities/path.ts @@ -1,4 +1,9 @@ function resolveRelPath(path = '') { + if (process.env.IS_ON_PREMISE) { + // We're using the hash router + // so we can just return the path + return path.length > 0 ? path : '/'; + } return `/insights/image-builder${path.length > 0 ? `/${path}` : ''}`; } diff --git a/src/Utilities/useGetEnvironment.ts b/src/Utilities/useGetEnvironment.ts index 3ec3d1bcd..eaeced88c 100644 --- a/src/Utilities/useGetEnvironment.ts +++ b/src/Utilities/useGetEnvironment.ts @@ -1,14 +1,16 @@ import { useChrome } from '@redhat-cloud-services/frontend-components/useChrome'; import { useFlag as useUnleashFlag } from '@unleash/proxy-client-react'; -export const useGetEnvironment = () => { - const { isBeta, isProd, getEnvironment } = useChrome(); - // Expose beta features in the ephemeral environment - if (isBeta() || getEnvironment() === 'qa') { - return { isBeta: () => true, isProd: isProd }; - } - return { isBeta: () => false, isProd: isProd }; -}; +export const useGetEnvironment = process.env.IS_ON_PREMISE + ? () => ({ isBeta: () => false, isProd: () => true }) + : () => { + const { isBeta, isProd, getEnvironment } = useChrome(); + // Expose beta features in the ephemeral environment + if (isBeta() || getEnvironment() === 'qa') { + return { isBeta: () => true, isProd: isProd }; + } + return { isBeta: () => false, isProd: isProd }; + }; /** * A hook that returns the value of a flag with a default value for ephemeral environment. diff --git a/src/constants.ts b/src/constants.ts index 8e49932c2..af41711c4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -56,6 +56,9 @@ export const RHEL_9_BETA = 'rhel-9-beta'; export const RHEL_10 = 'rhel-10'; export const RHEL_10_BETA = 'rhel-10-beta'; export const CENTOS_9 = 'centos-9'; +export const CENTOS_10 = 'centos-10'; +export const FEDORA_40 = 'fedora-40'; +export const FEDORA_41 = 'fedora-41'; export const X86_64 = 'x86_64'; export const AARCH64 = 'aarch64'; @@ -90,12 +93,20 @@ export const RELEASES = new Map([ [CENTOS_9, 'CentOS Stream 9'], ]); +export const ON_PREM_RELEASES = new Map([ + [CENTOS_10, 'CentOS Stream 10'], + [FEDORA_40, 'Fedora Linux 40'], + [FEDORA_41, 'Fedora Linux 41'], + [RHEL_10_BETA, 'Red Hat Enterprise Linux (RHEL) 10 Beta'], + [RHEL_10, 'Red Hat Enterprise Linux (RHEL) 10'], +]); + export const RHEL_9_FULL_SUPPORT = ['2022-05-18', '2027-05-31']; export const RHEL_8_FULL_SUPPORT = ['2019-05-07', '2024-05-31']; export const RHEL_9_MAINTENANCE_SUPPORT = ['2027-05-31', '2032-05-31']; export const RHEL_8_MAINTENANCE_SUPPORT = ['2024-05-31', '2029-05-31']; -export const ARCHS = [X86_64, AARCH64]; +export const ARCHES = [X86_64, AARCH64]; export const DEFAULT_AWS_REGION = 'us-east-1'; diff --git a/src/store/backendApi.ts b/src/store/backendApi.ts index 1a1564158..250d8ed71 100644 --- a/src/store/backendApi.ts +++ b/src/store/backendApi.ts @@ -1,21 +1,35 @@ -import { - useGetBlueprintsQuery as useCockpitGetBlueprintsQuery, - useDeleteBlueprintMutation as useCockpitDeleteMutation, -} from './cockpitApi'; +import * as cockpitQueries from './cockpitApi'; import { cockpitApi } from './enhancedCockpitApi'; import { imageBuilderApi } from './enhancedImageBuilderApi'; -import { - useGetBlueprintsQuery as useImageBuilderGetBlueprintsQuery, - useDeleteBlueprintMutation as useImageBuilderDeleteMutation, -} from './imageBuilderApi'; +import * as imageBuilderQueries from './imageBuilderApi'; + +export const useGetArchitecturesQuery = process.env.IS_ON_PREMISE + ? cockpitQueries.useGetArchitecturesQuery + : imageBuilderQueries.useGetArchitecturesQuery; + +export const useGetBlueprintQuery = process.env.IS_ON_PREMISE + ? cockpitQueries.useGetBlueprintQuery + : imageBuilderQueries.useGetBlueprintQuery; export const useGetBlueprintsQuery = process.env.IS_ON_PREMISE - ? useCockpitGetBlueprintsQuery - : useImageBuilderGetBlueprintsQuery; + ? cockpitQueries.useGetBlueprintsQuery + : imageBuilderQueries.useGetBlueprintsQuery; + +export const useLazyGetBlueprintsQuery = process.env.IS_ON_PREMISE + ? cockpitQueries.useLazyGetBlueprintsQuery + : imageBuilderQueries.useLazyGetBlueprintsQuery; export const useDeleteBlueprintMutation = process.env.IS_ON_PREMISE - ? useCockpitDeleteMutation - : useImageBuilderDeleteMutation; + ? cockpitQueries.useDeleteBlueprintMutation + : imageBuilderQueries.useDeleteBlueprintMutation; + +export const useGetOscapProfilesQuery = process.env.IS_ON_PREMISE + ? cockpitQueries.useGetOscapProfilesQuery + : imageBuilderQueries.useGetOscapProfilesQuery; + +export const useBackendPrefetch = process.env.IS_ON_PREMISE + ? cockpitApi.usePrefetch + : imageBuilderApi.usePrefetch; export const backendApi = process.env.IS_ON_PREMISE ? cockpitApi diff --git a/src/store/cockpitApi.ts b/src/store/cockpitApi.ts index 9e5f6d5dd..cdd1718e9 100644 --- a/src/store/cockpitApi.ts +++ b/src/store/cockpitApi.ts @@ -19,6 +19,10 @@ import { DeleteBlueprintApiResponse, DeleteBlueprintApiArg, BlueprintItem, + GetOscapProfilesApiArg, + GetOscapProfilesApiResponse, + GetBlueprintApiResponse, + GetBlueprintApiArg, } from './imageBuilderApi'; import { mapOnPremToHosted } from '../Components/Blueprints/helpers/onPremToHostedBlueprintMapper'; @@ -39,9 +43,62 @@ export const cockpitApi = emptyCockpitApi.injectEndpoints({ GetArchitecturesApiResponse, GetArchitecturesApiArg >({ - query: (queryArg) => ({ - url: `/architectures/${queryArg.distribution}`, - }), + queryFn: () => { + // TODO: this is hardcoded for now, but we may need to query + // the cloudapi endpoint on the composer socket to get the + // available information + return { + data: [ + { + arch: 'aarch64', + image_types: ['aws', 'guest-image', 'image-installer'], + repositories: [], + }, + { + arch: 'x86_64', + image_types: [ + 'aws', + 'gcp', + 'azure', + 'rhel-edge-commit', + 'rhel-edge-installer', + 'edge-commit', + 'edge-installer', + 'guest-image', + 'image-installer', + 'vsphere', + ], + repositories: [], + }, + ], + }; + }, + }), + getBlueprint: builder.query({ + queryFn: async ({ id, version }) => { + try { + const blueprintsDir = await getBlueprintsPath(); + const file = cockpit.file(path.join(blueprintsDir, id)); + + const contents = await file.read(); + const parsed = toml.parse(contents); + file.close(); + + const blueprint = mapOnPremToHosted(parsed); + + return { + data: { + ...blueprint, + id, + version, + last_modified_at: Date.now().toString(), + image_requests: [], + }, + }; + } catch (error) { + return { error }; + } + }, }), getBlueprints: builder.query< GetBlueprintsApiResponse, @@ -143,12 +200,28 @@ export const cockpitApi = emptyCockpitApi.injectEndpoints({ } }, }), + getOscapProfiles: builder.query< + GetOscapProfilesApiResponse, + GetOscapProfilesApiArg + >({ + queryFn: async () => { + // TODO: make a call to get the openscap profiles + // For now, just return an empty list so we can + // step through the wizard. + return { + data: [], + }; + }, + }), }; }, }); export const { + useGetArchitecturesQuery, + useGetBlueprintQuery, useGetBlueprintsQuery, + useLazyGetBlueprintsQuery, useDeleteBlueprintMutation, - useGetArchitecturesQuery, + useGetOscapProfilesQuery, } = cockpitApi; diff --git a/src/store/index.ts b/src/store/index.ts index 9a8c06781..46557ae6e 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -33,6 +33,10 @@ export const serviceReducer = combineReducers({ }); export const onPremReducer = combineReducers({ + [contentSourcesApi.reducerPath]: contentSourcesApi.reducer, + [rhsmApi.reducerPath]: rhsmApi.reducer, + [provisioningApi.reducerPath]: provisioningApi.reducer, + [complianceApi.reducerPath]: complianceApi.reducer, [cockpitApi.reducerPath]: cockpitApi.reducer, // TODO: add other endpoints so we can remove this. // It's still needed to get things to work. @@ -125,6 +129,10 @@ export const onPremMiddleware = (getDefaultMiddleware: Function) => promiseMiddleware, // TODO: add other endpoints so we can remove this. // It's still needed to get things to work. + contentSourcesApi.middleware, + rhsmApi.middleware, + provisioningApi.middleware, + complianceApi.middleware, imageBuilderApi.middleware, cockpitApi.middleware ); diff --git a/src/test/mocks/os-release/index.ts b/src/test/mocks/os-release/index.ts new file mode 100644 index 000000000..81fab5061 --- /dev/null +++ b/src/test/mocks/os-release/index.ts @@ -0,0 +1,13 @@ +type osRelease = { + ID: string; + VERSION_ID: string; +}; + +export const read_os_release = (): Promise => { + return new Promise((resolve) => { + resolve({ + ID: '', + VERSION_ID: '', + }); + }); +}; diff --git a/vitest.config.ts b/vitest.config.ts index ea646e570..0d67068c8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -34,6 +34,7 @@ const config = { __dirname, 'src/test/mocks/cockpit/fsinfo' ), + 'os-release': path.resolve(__dirname, 'src/test/mocks/os-release'), }, }, esbuild: {