Drop Edge Management federated module (HMS-8637)

This commit is contained in:
Simon Steinbeiss 2025-06-03 18:34:01 +02:00 committed by Klara Simickova
parent 33b374b3a0
commit a92d087014
9 changed files with 11 additions and 334 deletions

View file

@ -1,13 +1,6 @@
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import {
Button,
Popover,
Tabs,
Tab,
TabTitleText,
Content,
TabAction,
PageSection,
Sidebar,
SidebarContent,
@ -16,45 +9,18 @@ import {
Toolbar,
ToolbarContent,
} from '@patternfly/react-core';
import { ExternalLinkAltIcon, HelpIcon } from '@patternfly/react-icons';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { Outlet } from 'react-router-dom';
import './LandingPage.scss';
import { NewAlert } from './NewAlert';
import { MANAGING_WITH_DNF_URL, OSTREE_URL } from '../../constants';
import { manageEdgeImagesUrlName } from '../../Hooks/Edge/useGetNotificationProp';
import { resolveRelPath } from '../../Utilities/path';
import { useFlag } from '../../Utilities/useGetEnvironment';
import BlueprintsSidebar from '../Blueprints/BlueprintsSideBar';
import EdgeImagesTable from '../edge/ImagesTable';
import ImagesTable from '../ImagesTable/ImagesTable';
import { ImageBuilderHeader } from '../sharedComponents/ImageBuilderHeader';
export const LandingPage = () => {
const [showAlert, setShowAlert] = useState(true);
const { pathname } = useLocation();
const navigate = useNavigate();
const tabsPath = [
resolveRelPath(''),
resolveRelPath(manageEdgeImagesUrlName),
];
const initialActiveTabKey =
tabsPath.indexOf(pathname) >= 0 ? tabsPath.indexOf(pathname) : 0;
const [activeTabKey, setActiveTabKey] = useState(initialActiveTabKey);
useEffect(() => {
setActiveTabKey(initialActiveTabKey);
}, [initialActiveTabKey]);
const handleTabClick = (_event: React.MouseEvent, tabIndex: number) => {
const tabPath = tabsPath[tabIndex];
if (tabPath !== '') {
navigate(tabPath);
}
setActiveTabKey(tabIndex);
};
const edgeParityFlag = useFlag('edgeParity.image-list');
const imageList = (
<>
@ -85,115 +51,9 @@ export const LandingPage = () => {
return (
<>
<ImageBuilderHeader activeTab={activeTabKey} />
<PageSection hasBodyWrapper={false}>
{edgeParityFlag ? (
<Tabs
className="pf-c-tabs pf-c-page-header pf-c-table"
activeKey={activeTabKey}
onSelect={handleTabClick}
>
<Tab
eventKey={0}
title={<TabTitleText> Conventional (RPM-DNF){''} </TabTitleText>}
actions={
<HelpPopover
header={'Conventional (RPM-DNF)'}
body={
<div>
<Content>
<Content>
With RPM-DNF, you can manage the system software by
using the DNF package manager and updated RPM
packages. This is a simple and adaptive method of
managing and modifying the system over its lifecycle.
</Content>
<Content>
<Button
component="a"
target="_blank"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
isInline
href={MANAGING_WITH_DNF_URL}
>
Learn more about managing images with DNF
</Button>
</Content>
</Content>
</div>
}
/>
}
>
{imageList}
</Tab>
<Tab
eventKey={1}
title={<TabTitleText>Immutable (OSTree) </TabTitleText>}
actions={
<HelpPopover
header={'Immutable (OSTree)'}
body={
<Content>
<Content>
With OSTree, you can manage the system software by
referencing a central image repository. OSTree images
contain a complete operating system ready to be remotely
installed at scale. You can track updates to images
through commits and enable secure updates that only
address changes and keep the operating system unchanged.
The updates are quick, and the rollbacks are easy.
</Content>
<Content>
<Button
component="a"
target="_blank"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
isInline
href={OSTREE_URL}
>
Learn more about OSTree
</Button>
</Content>
</Content>
}
/>
}
>
<EdgeImagesTable />
</Tab>
</Tabs>
) : (
imageList
)}
<Outlet />
</PageSection>
</>
);
};
type HelpPopoverPropTypes = {
header: string;
body: React.ReactNode;
};
const HelpPopover = ({ header, body }: HelpPopoverPropTypes) => {
const ref = React.createRef<HTMLElement>();
return (
<>
<TabAction ref={ref}>
<HelpIcon />
</TabAction>
<Popover
minWidth="35rem"
headerContent={header}
bodyContent={body}
triggerRef={ref}
/>
<ImageBuilderHeader />
{imageList}
<Outlet />
</>
);
};

View file

@ -1,44 +0,0 @@
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 { useNavigate, useLocation, useParams } from 'react-router-dom';
import {
useGetNotificationProp,
manageEdgeImagesUrlName,
} from '../../Hooks/Edge/useGetNotificationProp';
import { resolveRelPath } from '../../Utilities/path';
import { useFlag } from '../../Utilities/useGetEnvironment';
const ImageDetail = () => {
const notificationProp = useGetNotificationProp();
// Feature flag for the federated modules
const edgeParityFlag = useFlag('edgeParity.image-list');
// Feature flag to access the 'local' images table list
const edgeLocalImageTable = useFlag('image-builder.edge.local-image-table');
if (edgeLocalImageTable) {
return <div />;
}
if (edgeParityFlag) {
return (
<AsyncComponent
scope="edge"
module="./ImagesDetail"
ErrorComponent={<ErrorState />}
navigateProp={useNavigate}
locationProp={useLocation}
notificationProp={notificationProp}
pathPrefix={resolveRelPath('')}
urlName={manageEdgeImagesUrlName}
paramsProp={useParams}
/>
);
} else {
return <Unavailable />;
}
};
export default ImageDetail;

View file

@ -1,45 +0,0 @@
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 { useNavigate, useLocation } from 'react-router-dom';
import { CREATING_IMAGES_WITH_IB_URL } from '../../constants';
import {
useGetNotificationProp,
manageEdgeImagesUrlName,
} from '../../Hooks/Edge/useGetNotificationProp';
import { resolveRelPath } from '../../Utilities/path';
import { useFlag } from '../../Utilities/useGetEnvironment';
const ImagesTable = () => {
const notificationProp = useGetNotificationProp();
// Feature flag for the federated modules
const edgeParityFlag = useFlag('edgeParity.image-list');
// Feature flag to access the 'local' images table list
const edgeLocalImageTable = useFlag('image-builder.edge.local-image-table');
if (edgeLocalImageTable) {
return <div />;
}
if (edgeParityFlag) {
return (
<AsyncComponent
scope="edge"
module="./Images"
ErrorComponent={<ErrorState />}
navigateProp={useNavigate}
locationProp={useLocation}
showHeaderProp={false}
docLinkProp={CREATING_IMAGES_WITH_IB_URL}
notificationProp={notificationProp}
pathPrefix={resolveRelPath('')}
urlName={manageEdgeImagesUrlName}
/>
);
}
return <Unavailable />;
};
export default ImagesTable;

View file

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { Button, Popover, Content, Flex, Alert } from '@patternfly/react-core';
import { Button, Popover, Content, Flex } from '@patternfly/react-core';
import { ExternalLinkAltIcon, HelpIcon } from '@patternfly/react-icons';
// eslint-disable-next-line rulesdir/disallow-fec-relative-imports
import {
@ -11,10 +11,8 @@ import {
import { useNavigate } from 'react-router-dom';
import {
CREATE_RHEL_IMAGES_WITH_AUTOMATED_MANAGEMENT_URL,
CREATING_IMAGES_WITH_IB_SERVICE_URL,
OSBUILD_SERVICE_ARCHITECTURE_URL,
RHEM_DOCUMENTATION_URL,
} from '../../constants';
import { useBackendPrefetch } from '../../store/backendApi';
import { useAppSelector } from '../../store/hooks';
@ -24,6 +22,10 @@ import './ImageBuilderHeader.scss';
import { useFlagWithEphemDefault } from '../../Utilities/useGetEnvironment';
import { ImportBlueprintModal } from '../Blueprints/ImportBlueprintModal';
type ImageBuilderHeaderPropTypes = {
inWizard?: boolean;
};
const AboutImageBuilderPopover = () => {
return (
<Popover
@ -52,19 +54,6 @@ const AboutImageBuilderPopover = () => {
Image builder for RPM-DNF documentation
</Button>
</Content>
<Content>
<Button
component="a"
target="_blank"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
isInline
href={CREATE_RHEL_IMAGES_WITH_AUTOMATED_MANAGEMENT_URL}
>
Image builder for OSTree documentation
</Button>
</Content>
</Content>
}
>
@ -78,13 +67,7 @@ const AboutImageBuilderPopover = () => {
);
};
type ImageBuilderHeaderPropTypes = {
activeTab?: number;
inWizard?: boolean;
};
export const ImageBuilderHeader = ({
activeTab,
inWizard,
}: ImageBuilderHeaderPropTypes) => {
const navigate = useNavigate();
@ -96,7 +79,6 @@ export const ImageBuilderHeader = ({
'image-builder.import.enabled'
);
const [showImportModal, setShowImportModal] = useState(false);
const isOnBlueprintsTab = activeTab === 0;
return (
<>
{importExportFlag && (
@ -124,7 +106,6 @@ export const ImageBuilderHeader = ({
variant="primary"
data-testid="blueprints-create-button"
onClick={() => navigate(resolveRelPath('imagewizard'))}
isDisabled={!isOnBlueprintsTab}
onMouseEnter={() =>
prefetchTargets({
distribution: distribution,
@ -138,7 +119,6 @@ export const ImageBuilderHeader = ({
data-testid="import-blueprint-button"
variant="secondary"
onClick={() => setShowImportModal(true)}
isDisabled={!isOnBlueprintsTab}
>
Import
</Button>
@ -148,37 +128,6 @@ export const ImageBuilderHeader = ({
</>
}
/>
{!isOnBlueprintsTab && !inWizard && !process.env.IS_ON_PREMISE && (
<Alert
variant="info"
isInline
title={<>Upcoming decommission of hosted Edge Management service</>}
className="pf-v6-u-mt-sm pf-v6-u-mb-sm"
>
<Content>
<Content>
As of July 31, 2025, the hosted edge management service will no
longer be supported. This means that pushing image updates to
Immutable (OSTree) systems using the Hybrid Cloud Console will
be discontinued. For an alternative way to manage edge systems,
customers are encouraged to explore Red Hat Edge Manager (RHEM).
</Content>
<Content>
<Button
component="a"
target="_blank"
variant="link"
icon={<ExternalLinkAltIcon />}
iconPosition="right"
isInline
href={RHEM_DOCUMENTATION_URL}
>
Red Hat Edge Manager (RHEM) documentation
</Button>
</Content>
</Content>
</Alert>
)}
</PageHeader>
</>
);

View file

@ -2,13 +2,8 @@ import React, { lazy, Suspense } from 'react';
import { Route, Routes } from 'react-router-dom';
import EdgeImageDetail from './Components/edge/ImageDetails';
import ShareImageModal from './Components/ShareImageModal/ShareImageModal';
import { manageEdgeImagesUrlName } from './Hooks/Edge/useGetNotificationProp';
import {
useFlag,
useFlagWithEphemDefault,
} from './Utilities/useGetEnvironment';
import { useFlagWithEphemDefault } from './Utilities/useGetEnvironment';
const LandingPage = lazy(() => import('./Components/LandingPage/LandingPage'));
const ImportImageWizard = lazy(
@ -17,7 +12,6 @@ const ImportImageWizard = lazy(
const CreateImageWizard = lazy(() => import('./Components/CreateImageWizard'));
export const Router = () => {
const edgeParityFlag = useFlag('edgeParity.image-list');
const importExportFlag = useFlagWithEphemDefault(
'image-builder.import.enabled'
);
@ -52,18 +46,6 @@ export const Router = () => {
</Suspense>
}
/>
{edgeParityFlag && (
<Route
path={`/${manageEdgeImagesUrlName}/:imageId`}
element={<EdgeImageDetail />}
>
<Route path="*" element={<EdgeImageDetail />} />
<Route
path={`versions/:imageVersionId/*`}
element={<EdgeImageDetail />}
/>
</Route>
)}
</Routes>
);
};

View file

@ -35,13 +35,8 @@ export const MANAGING_WITH_DNF_URL =
'https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/managing_software_with_the_dnf_tool/index';
export const CREATING_IMAGES_WITH_IB_SERVICE_URL =
'https://docs.redhat.com/en/documentation/red_hat_insights/1-latest/html/deploying_and_managing_rhel_systems_in_hybrid_clouds/index';
export const RHEM_DOCUMENTATION_URL =
'https://docs.redhat.com/en/documentation/red_hat_ansible_automation_platform/2.5/html/managing_device_fleets_with_the_red_hat_edge_manager/index';
export const OSTREE_URL = 'https://ostreedev.github.io/ostree/';
export const DOCUMENTATION_URL =
'https://docs.redhat.com/en/documentation/red_hat_insights/1-latest/html/deploying_and_managing_rhel_systems_in_hybrid_clouds/index';
export const CREATE_RHEL_IMAGES_WITH_AUTOMATED_MANAGEMENT_URL =
'https://docs.redhat.com/en/documentation/edge_management/2022/html/create_rhel_for_edge_images_and_configure_automated_management/index';
export const OSBUILD_SERVICE_ARCHITECTURE_URL =
'https://osbuild.org/docs/service/architecture/';
export const GENERATING_SSH_KEY_PAIRS_URL =

View file

@ -1,8 +0,0 @@
import { useFlag } from '@unleash/proxy-client-react';
describe('mocking unleash calls', () => {
test('the ege local image table is set to true', () => {
const edgeLocalImageTable = useFlag('image-builder.edge.local-image-table');
expect(edgeLocalImageTable).toBe(true);
});
});

View file

@ -1,8 +0,0 @@
import { useFlag } from '../../../Utilities/useGetEnvironment';
describe('mocking unleash calls', () => {
test('the ege local image table is set to true', () => {
const edgeLocalImageTable = useFlag('image-builder.edge.local-image-table');
expect(edgeLocalImageTable).toBe(true);
});
});

View file

@ -57,10 +57,6 @@ vi.mock('@unleash/proxy-client-react', () => ({
switch (flag) {
case 'image-builder.import.enabled':
return true;
case 'edgeParity.image-list':
return true;
case 'image-builder.edge.local-image-table':
return true;
case 'image-builder.satellite.enabled':
return true;
case 'image-builder.templates.enabled':