diff --git a/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx b/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx index 95555fa7d9..7db2cdc501 100644 --- a/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx +++ b/libs/ui-lib/lib/common/components/clusterDetail/OperatorsProgressItem.tsx @@ -80,7 +80,7 @@ type OperatorsPopoverProps = OperatorListProps & { const OperatorsPopover = ({ operators, children }: OperatorsPopoverProps) => { const { t } = useTranslation(); - const opSpecs = useOperatorSpecs(); + const { byKey: opSpecs } = useOperatorSpecs(); return ( string | undefined; export type GetFeatureSupportLevel = ( diff --git a/libs/ui-lib/lib/common/components/operators/operatorDescriptions.tsx b/libs/ui-lib/lib/common/components/operators/operatorDescriptions.tsx new file mode 100644 index 0000000000..651b946471 --- /dev/null +++ b/libs/ui-lib/lib/common/components/operators/operatorDescriptions.tsx @@ -0,0 +1,58 @@ +export const DESCRIPTION_MTV = + 'This Toolkit (MTV) enables you to migrate virtual machines from VMware vSphere, Red Hat Virtualization, or OpenStack to OpenShift Virtualization running on Red Hat OpenShift.'; + +export const DESCRIPTION_AMD_GPU = + 'Automate the management of AMD software components needed to provision and monitor GPUs.'; + +export const DESCRIPTION_LSO = 'Allows provisioning of persistent storage by using local volumes.'; + +export const DESCRIPTION_AUTHORINO = + 'Lightweight external authorization service for tailor-made Zero Trust API security.'; + +export const DESCRIPTION_CNV = 'Run virtual machines alongside containers on one platform.'; + +export const DESCRIPTION_FENCE_AGENTS_REMEDIATION = + 'Externally fences failed nodes using power controllers.'; + +export const DESCRIPTION_KMM = 'Management of kernel modules.'; + +export const DESCRIPTION_KUBE_DESCHEDULER = + 'Evicts pods to reschedule them onto more suitable nodes.'; + +export const DESCRIPTION_MCE = 'Create, import, and manage multiple clusters from this cluster.'; + +export const DESCRIPTION_NMSTATE = + 'Provides users with functionality to configure various network interface types, DNS, and routing on cluster nodes.'; + +export const DESCRIPTION_NODE_FEATURE_DISCOVERY = + 'Manage the detection of hardware features and configuration by labeling nodes with hardware-specific information.'; + +export const DESCRIPTION_NODE_HEALTHCHECK = 'Identify Unhealthy Nodes.'; + +export const DESCRIPTION_NODE_MAINTENANCE = 'Place nodes in maintenance mode.'; + +export const DESCRIPTION_NVIDIA_GPU = + 'Automate the management of NVIDIA software components needed to provision and monitor GPUs.'; + +export const DESCRIPTION_ODF = 'Persistent software-defined storage for hybrid applications.'; + +export const DESCRIPTION_OPENSHIFT_AI = + 'Train, serve, monitor and manage AI/ML models and applications.'; + +export const DESCRIPTION_OSC = + 'OpenShift sandboxed containers support for OpenShift Container Platform provides users with built-in support for running Kata Containers as an additional optional runtime. It provides an additional virtualization machine(VM) isolation layer for pods.'; + +export const DESCRIPTION_PIPELINES = + 'Cloud-native continuous integration and delivery (CI/CD) solution for building pipelines using Tekton.'; + +export const DESCRIPTION_SELF_NODE_REMEDIATION = + 'Allows nodes to reboot themselves when they become unhealthy.'; + +export const DESCRIPTION_SERVERLESS = + 'Deploy workflow applications based on the CNCF Serverless Workflow specification.'; + +export const DESCRIPTION_SERVICEMESH = + 'Platform that provides behavioral insight and operational control over a service mesh.'; + +export const DESCRIPTION_LVM = + 'Storage virtualization that offers a more flexible approach for disk space management.'; diff --git a/libs/ui-lib/lib/common/components/operators/operatorSpecs.tsx b/libs/ui-lib/lib/common/components/operators/operatorSpecs.tsx index fc7445ba55..3ce7e0eea1 100644 --- a/libs/ui-lib/lib/common/components/operators/operatorSpecs.tsx +++ b/libs/ui-lib/lib/common/components/operators/operatorSpecs.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Cluster } from '@openshift-assisted/types/assisted-installer-service'; +import { Cluster, SupportLevel } from '@openshift-assisted/types/assisted-installer-service'; import { FeatureId } from '../../types'; import { OPERATOR_NAME_AMD_GPU, @@ -53,7 +53,31 @@ import { SERVERLESS_OPERATOR_LINK, } from '../../config'; import { getMajorMinorVersion } from '../../utils'; -import { useNewFeatureSupportLevel } from '../newFeatureSupportLevels'; +import { GetFeatureSupportLevel, useNewFeatureSupportLevel } from '../newFeatureSupportLevels'; +import { + DESCRIPTION_AMD_GPU, + DESCRIPTION_AUTHORINO, + DESCRIPTION_LSO, + DESCRIPTION_MTV, + DESCRIPTION_CNV, + DESCRIPTION_FENCE_AGENTS_REMEDIATION, + DESCRIPTION_KMM, + DESCRIPTION_KUBE_DESCHEDULER, + DESCRIPTION_MCE, + DESCRIPTION_NMSTATE, + DESCRIPTION_NODE_FEATURE_DISCOVERY, + DESCRIPTION_NODE_HEALTHCHECK, + DESCRIPTION_NODE_MAINTENANCE, + DESCRIPTION_NVIDIA_GPU, + DESCRIPTION_ODF, + DESCRIPTION_OPENSHIFT_AI, + DESCRIPTION_OSC, + DESCRIPTION_PIPELINES, + DESCRIPTION_SELF_NODE_REMEDIATION, + DESCRIPTION_SERVERLESS, + DESCRIPTION_SERVICEMESH, + DESCRIPTION_LVM, +} from './operatorDescriptions'; // TODO check if it's unused and it can be deleted in favor of "isMajorMinorVersionEqualOrGreater" export const isOCPVersionEqualsOrMore = ( @@ -69,279 +93,420 @@ export const isOCPVersionEqualsOrMore = ( }; export type OperatorSpec = { + operatorKey: string; title: string; featureId: FeatureId; + descriptionText?: string; Description?: React.ComponentType<{ openshiftVersion?: string }>; notStandalone?: boolean; Requirements?: React.ComponentType<{ cluster: Cluster }>; + category: string; + supportLevel?: SupportLevel | undefined; }; -export const getOperatorSpecs = (useLVMS?: boolean): { [key: string]: OperatorSpec } => { +export const getOperatorSpecs = ( + getFeatureSupportLevel: GetFeatureSupportLevel, + useLVMS?: boolean, +): { [category: string]: OperatorSpec[] } => { return { - [OPERATOR_NAME_MTV]: { - title: 'Migration Toolkit for Virtualization', - featureId: 'MTV', - Description: () => ( - <> - This Toolkit (MTV) enables you to migrate virtual machines from VMware vSphere, Red Hat - Virtualization, or OpenStack to OpenShift Virtualization running on Red Hat OpenShift.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_AMD_GPU]: { - title: 'AMD GPU', - featureId: 'AMD_GPU', - Requirements: () => <>Requires at least one supported AMD GPU, - Description: () => ( - <> - Automate the management of AMD software components needed to provision and monitor GPUs. - - ), - }, - [OPERATOR_NAME_LSO]: { - title: 'Local Storage Operator', - featureId: 'LSO', - Description: ({ openshiftVersion }) => ( - <> - Allows provisioning of persistent storage by using local volumes.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_AUTHORINO]: { - title: 'Authorino', - featureId: 'AUTHORINO', - Description: () => ( - <> - Lightweight external authorization service for tailor-made Zero Trust API security.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_CNV]: { - title: 'OpenShift Virtualization', - featureId: 'CNV', - Requirements: () => ( - <>Enabled CPU virtualization support in BIOS (Intel-VT / AMD-V) on all nodes - ), - Description: () => ( - <> - Run virtual machines alongside containers on one platform.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_FENCE_AGENTS_REMEDIATION]: { - title: 'Fence Agents Remediation', - featureId: 'FENCE_AGENTS_REMEDIATION', - Description: () => ( - <> - Externally fences failed nodes using power controllers.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_KMM]: { - title: 'Kernel Module Management', - featureId: 'KMM', - Description: ({ openshiftVersion }) => ( - <> - Management of kernel modules.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_KUBE_DESCHEDULER]: { - title: 'Kube Descheduler', - featureId: 'KUBE_DESCHEDULER', - Description: ({ openshiftVersion }) => ( - <> - Evicts pods to reschedule them onto more suitable nodes.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_MCE]: { - title: 'Multicluster engine', - featureId: 'MCE', - Description: ({ openshiftVersion }) => ( - <> - Create, import, and manage multiple clusters from this cluster.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_NMSTATE]: { - title: 'NMState', - featureId: 'NMSTATE', - Description: ({ openshiftVersion }) => ( - <> - Provides users with functionality to configure various network interface types, DNS, and - routing on cluster nodes.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_NODE_FEATURE_DISCOVERY]: { - title: 'Node Feature Discovery', - featureId: 'NODE_FEATURE_DISCOVERY', - Description: ({ openshiftVersion }) => ( - <> - Manage the detection of hardware features and configuration by labeling nodes with - hardware-specific information.{' '} - - Learn more + [categories[Category.STORAGE]]: [ + { + operatorKey: OPERATOR_NAME_LSO, + title: 'Local Storage Operator', + featureId: 'LSO', + descriptionText: DESCRIPTION_LSO, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_LSO}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.STORAGE], + supportLevel: getFeatureSupportLevel('LSO'), + }, + { + operatorKey: OPERATOR_NAME_LVM, + title: useLVMS ? 'Logical Volume Manager Storage' : 'Logical Volume Manager', + featureId: 'LVM', + descriptionText: DESCRIPTION_LVM, + Description: ({ openshiftVersion }) => + useLVMS ? ( + <> + {DESCRIPTION_LVM}{' '} + Learn more + + ) : ( + <>{DESCRIPTION_LVM} + ), + category: categories[Category.STORAGE], + supportLevel: getFeatureSupportLevel('LVM'), + }, + { + operatorKey: OPERATOR_NAME_ODF, + title: 'OpenShift Data Foundation', + featureId: 'ODF', + descriptionText: DESCRIPTION_ODF, + Requirements: () => ( + + Learn more about the requirements for OpenShift Data Foundation - - ), - }, - [OPERATOR_NAME_NODE_HEALTHCHECK]: { - title: 'Node Healthcheck', - featureId: 'NODE_HEALTHCHECK', - Description: () => ( - <> - Identify Unhealthy Nodes.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_NODE_MAINTENANCE]: { - title: 'Node Maintenance', - featureId: 'NODE_MAINTENANCE', - Description: () => ( - <> - Place nodes in maintenance mode.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_NVIDIA_GPU]: { - title: 'NVIDIA GPU', - featureId: 'NVIDIA_GPU', - Requirements: () => <>Requires at least one supported NVIDIA GPU, - Description: ({ openshiftVersion }) => ( - <> - Automate the management of NVIDIA software components needed to provision and monitor - GPUs. Learn more - - ), - }, - [OPERATOR_NAME_ODF]: { - title: 'OpenShift Data Foundation', - featureId: 'ODF', - Requirements: () => ( - - Learn more about the requirements for OpenShift Data Foundation - - ), - Description: () => ( - <> - Persistent software-defined storage for hybrid applications.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_OPENSHIFT_AI]: { - title: 'OpenShift AI', - featureId: 'OPENSHIFT_AI', - Requirements: () => ( - Learn more - ), - Description: () => ( - <> - Train, serve, monitor and manage AI/ML models and applications.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_OSC]: { - title: 'OpenShift sandboxed containers', - featureId: 'OSC', - Requirements: () => ( - - Learn more about the requirements for OpenShift sandboxed containers - - ), - Description: () => ( - <> - OpenShift sandboxed containers support for OpenShift Container Platform provides users - with built-in support for running Kata Containers as an additional optional runtime. It - provides an additional virtualization machine(VM) isolation layer for pods.{' '} - Learn more - - ), - }, - [OPERATOR_NAME_PIPELINES]: { - title: 'Pipelines', - featureId: 'PIPELINES', - Description: () => ( - <> - Cloud-native continuous integration and delivery (CI/CD) solution for building pipelines - using Tekton. Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_SELF_NODE_REMEDIATION]: { - title: 'Self Node Remediation', - featureId: 'SELF_NODE_REMEDIATION', - Description: () => ( - <> - Allows nodes to reboot themselves when they become unhealthy.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_SERVERLESS]: { - title: 'Serverless', - featureId: 'SERVERLESS', - Description: () => ( - <> - Deploy workflow applications based on the CNCF Serverless Workflow specification.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_SERVICEMESH]: { - title: 'Service Mesh', - featureId: 'SERVICEMESH', - Description: ({ openshiftVersion }) => ( - <> - Platform that provides behavioral insight and operational control over a service mesh.{' '} - Learn more - - ), - notStandalone: true, - }, - [OPERATOR_NAME_LVM]: { - title: useLVMS ? 'Logical Volume Manager Storage' : 'Logical Volume Manager', - featureId: 'LVM', - Description: ({ openshiftVersion }) => - useLVMS ? ( + ), + Description: () => ( <> - Storage virtualization that offers a more flexible approach for disk space management.{' '} - Learn more + {DESCRIPTION_ODF} Learn more - ) : ( + ), + category: categories[Category.STORAGE], + supportLevel: getFeatureSupportLevel('ODF'), + }, + ], + [categories[Category.VIRT]]: [ + { + operatorKey: OPERATOR_NAME_CNV, + title: 'OpenShift Virtualization', + featureId: 'CNV', + descriptionText: DESCRIPTION_CNV, + Requirements: () => ( + <>Enabled CPU virtualization support in BIOS (Intel-VT / AMD-V) on all nodes + ), + Description: () => ( + <> + {DESCRIPTION_CNV} Learn more + + ), + category: categories[Category.VIRT], + supportLevel: getFeatureSupportLevel('CNV'), + }, + { + operatorKey: OPERATOR_NAME_MTV, + title: 'Migration Toolkit for Virtualization', + featureId: 'MTV', + descriptionText: DESCRIPTION_MTV, + Description: () => ( <> - Storage virtualization that offers a more flexible approach for disk space management. + {DESCRIPTION_MTV} Learn more ), - }, + category: categories[Category.VIRT], + supportLevel: getFeatureSupportLevel('MTV'), + }, + ], + [categories[Category.AI]]: [ + { + operatorKey: OPERATOR_NAME_OPENSHIFT_AI, + title: 'OpenShift AI', + featureId: 'OPENSHIFT_AI', + descriptionText: DESCRIPTION_OPENSHIFT_AI, + Requirements: () => ( + Learn more + ), + Description: () => ( + <> + {DESCRIPTION_OPENSHIFT_AI}{' '} + Learn more + + ), + category: categories[Category.AI], + supportLevel: getFeatureSupportLevel('OPENSHIFT_AI'), + }, + { + operatorKey: OPERATOR_NAME_AMD_GPU, + title: 'AMD GPU', + featureId: 'AMD_GPU', + descriptionText: DESCRIPTION_AMD_GPU, + Requirements: () => <>Requires at least one supported AMD GPU, + Description: () => <>{DESCRIPTION_AMD_GPU}, + category: categories[Category.AI], + supportLevel: getFeatureSupportLevel('AMD_GPU'), + }, + { + operatorKey: OPERATOR_NAME_NVIDIA_GPU, + title: 'NVIDIA GPU', + featureId: 'NVIDIA_GPU', + descriptionText: DESCRIPTION_NVIDIA_GPU, + Requirements: () => <>Requires at least one supported NVIDIA GPU, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_NVIDIA_GPU} + Learn more + + ), + category: categories[Category.AI], + supportLevel: getFeatureSupportLevel('NVIDIA_GPU'), + }, + ], + [categories[Category.NETWORK]]: [ + { + operatorKey: OPERATOR_NAME_NMSTATE, + title: 'NMState', + featureId: 'NMSTATE', + descriptionText: DESCRIPTION_NMSTATE, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_NMSTATE}{' '} + Learn more + + ), + category: categories[Category.NETWORK], + supportLevel: getFeatureSupportLevel('NMSTATE'), + }, + { + operatorKey: OPERATOR_NAME_SERVICEMESH, + title: 'Service Mesh', + featureId: 'SERVICEMESH', + descriptionText: DESCRIPTION_SERVICEMESH, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_SERVICEMESH}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.NETWORK], + supportLevel: getFeatureSupportLevel('SERVICEMESH'), + }, + ], + [categories[Category.REMEDIATION]]: [ + { + operatorKey: OPERATOR_NAME_FENCE_AGENTS_REMEDIATION, + title: 'Fence Agents Remediation', + featureId: 'FENCE_AGENTS_REMEDIATION', + descriptionText: DESCRIPTION_FENCE_AGENTS_REMEDIATION, + Description: () => ( + <> + {DESCRIPTION_FENCE_AGENTS_REMEDIATION}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.REMEDIATION], + supportLevel: getFeatureSupportLevel('FENCE_AGENTS_REMEDIATION'), + }, + { + operatorKey: OPERATOR_NAME_NODE_HEALTHCHECK, + title: 'Node Healthcheck', + featureId: 'NODE_HEALTHCHECK', + descriptionText: DESCRIPTION_NODE_HEALTHCHECK, + Description: () => ( + <> + {DESCRIPTION_NODE_HEALTHCHECK}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.REMEDIATION], + supportLevel: getFeatureSupportLevel('NODE_HEALTHCHECK'), + }, + { + operatorKey: OPERATOR_NAME_SELF_NODE_REMEDIATION, + title: 'Self Node Remediation', + featureId: 'SELF_NODE_REMEDIATION', + descriptionText: DESCRIPTION_SELF_NODE_REMEDIATION, + Description: () => ( + <> + {DESCRIPTION_SELF_NODE_REMEDIATION}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.REMEDIATION], + supportLevel: getFeatureSupportLevel('SELF_NODE_REMEDIATION'), + }, + ], + [categories[Category.OTHER]]: [ + { + operatorKey: OPERATOR_NAME_AUTHORINO, + title: 'Authorino', + featureId: 'AUTHORINO', + descriptionText: DESCRIPTION_AUTHORINO, + Description: () => ( + <> + {DESCRIPTION_AUTHORINO}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('AUTHORINO'), + }, + { + operatorKey: OPERATOR_NAME_NODE_FEATURE_DISCOVERY, + title: 'Node Feature Discovery', + featureId: 'NODE_FEATURE_DISCOVERY', + descriptionText: DESCRIPTION_NODE_FEATURE_DISCOVERY, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_NODE_FEATURE_DISCOVERY}{' '} + + Learn more + + + ), + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('NODE_FEATURE_DISCOVERY'), + }, + { + operatorKey: OPERATOR_NAME_PIPELINES, + title: 'Pipelines', + featureId: 'PIPELINES', + descriptionText: DESCRIPTION_PIPELINES, + Description: () => ( + <> + {DESCRIPTION_PIPELINES}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('PIPELINES'), + }, + { + operatorKey: OPERATOR_NAME_SERVERLESS, + title: 'Serverless', + featureId: 'SERVERLESS', + descriptionText: DESCRIPTION_SERVERLESS, + Description: () => ( + <> + {DESCRIPTION_SERVERLESS}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('SERVERLESS'), + }, + { + operatorKey: OPERATOR_NAME_KMM, + title: 'Kernel Module Management', + featureId: 'KMM', + descriptionText: DESCRIPTION_KMM, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_KMM}{' '} + Learn more + + ), + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('KMM'), + }, + { + operatorKey: OPERATOR_NAME_MCE, + title: 'Multicluster engine', + featureId: 'MCE', + descriptionText: DESCRIPTION_MCE, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_MCE}{' '} + Learn more + + ), + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('MCE'), + }, + { + operatorKey: OPERATOR_NAME_OSC, + title: 'OpenShift sandboxed containers', + featureId: 'OSC', + descriptionText: DESCRIPTION_OSC, + Requirements: () => ( + + Learn more about the requirements for OpenShift sandboxed containers + + ), + Description: () => ( + <> + {DESCRIPTION_OSC} Learn more + + ), + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('OSC'), + }, + { + operatorKey: OPERATOR_NAME_KUBE_DESCHEDULER, + title: 'Kube Descheduler', + featureId: 'KUBE_DESCHEDULER', + descriptionText: DESCRIPTION_KUBE_DESCHEDULER, + Description: ({ openshiftVersion }) => ( + <> + {DESCRIPTION_KUBE_DESCHEDULER}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('KUBE_DESCHEDULER'), + }, + + { + operatorKey: OPERATOR_NAME_NODE_MAINTENANCE, + title: 'Node Maintenance', + featureId: 'NODE_MAINTENANCE', + descriptionText: DESCRIPTION_NODE_MAINTENANCE, + Description: () => ( + <> + {DESCRIPTION_NODE_MAINTENANCE}{' '} + Learn more + + ), + notStandalone: true, + category: categories[Category.OTHER], + supportLevel: getFeatureSupportLevel('NODE_MAINTENANCE'), + }, + ], }; }; export const useOperatorSpecs = () => { const { getFeatureSupportLevel } = useNewFeatureSupportLevel(); const useLVMS = getFeatureSupportLevel('LVM') === 'supported'; - return React.useMemo(() => getOperatorSpecs(useLVMS), [useLVMS]); + + // Grouped by category + const byCategory = React.useMemo( + () => getOperatorSpecs(getFeatureSupportLevel, useLVMS), + [getFeatureSupportLevel, useLVMS], + ); + + // Flat map operatorKey -> spec + const byKey: Record = React.useMemo(() => { + return Object.values(byCategory).reduce((acc, specs) => { + specs.forEach((spec) => { + acc[spec.operatorKey] = spec; + }); + return acc; + }, {} as Record); + }, [byCategory]); + + return { byCategory, byKey }; +}; + +export const getOperatorTitleByFeatureId = (featureId: FeatureId): string | undefined => { + const allSpecs = getOperatorSpecs(() => undefined); + + for (const categorySpecs of Object.values(allSpecs)) { + const spec = categorySpecs.find((s) => s.featureId === featureId); + if (spec) { + return spec.title; + } + } + + return undefined; +}; + +enum Category { + STORAGE, + VIRT, + AI, + NETWORK, + REMEDIATION, + OTHER, +} + +export const categories: { [key in Category]: string } = { + [Category.STORAGE]: 'Storage', + [Category.VIRT]: 'Virtualization', + [Category.AI]: 'AI', + [Category.NETWORK]: 'Network', + [Category.REMEDIATION]: 'Remediation', + [Category.OTHER]: 'Other', }; diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx index 6b258dd3af..3339efa9df 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/OcmOperatorProgressItem.tsx @@ -80,7 +80,7 @@ type OperatorsPopoverProps = OperatorListProps & { const OperatorsPopover = ({ operators, children }: OperatorsPopoverProps) => { const { t } = useTranslation(); - const opSpecs = useOperatorSpecs(); + const { byKey: opSpecs } = useOperatorSpecs(); return ( { const { getFeatureSupportLevel, getFeatureDisabledReason } = useNewFeatureSupportLevel(); - const opSpecs = useOperatorSpecs(); + const { byKey: opSpecs } = useOperatorSpecs(); const { isViewerMode } = useSelector(selectCurrentClusterPermissionsState); const { values, setFieldValue } = useFormikContext(); diff --git a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx index 59e28118f8..fff088611d 100644 --- a/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterConfiguration/review/ReviewOperatorsTable.tsx @@ -6,7 +6,8 @@ import { TableSummaryExpandable } from './TableSummaryExpandable'; import { useOperatorSpecs } from '../../../../common/components/operators/operatorSpecs'; export const ReviewOperatorsTable = ({ cluster }: { cluster: Cluster }) => { - const opSpecs = useOperatorSpecs(); + const { byKey: opSpecs } = useOperatorSpecs(); + const rows = React.useMemo( () => cluster.monitoredOperators diff --git a/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx b/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx index 207c765cd6..0878f67b86 100644 --- a/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterDetail/ProgressBarAlerts.tsx @@ -66,7 +66,7 @@ export const HostInstallationWarning = ({ message, }: InstallationProgressWarningProps) => { const { addAlert, clearAlerts } = useAlerts(); - const opSpecs = useOperatorSpecs(); + const { byKey: opSpecs } = useOperatorSpecs(); return ( <> diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.tsx index e2b33b917c..a41396f0d9 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsBundle.tsx @@ -24,22 +24,18 @@ import NewFeatureSupportLevelBadge, { } from '../../../common/components/newFeatureSupportLevels/NewFeatureSupportLevelBadge'; import { ExternalLink, OperatorsValues, PopoverIcon, singleClusterBundles } from '../../../common'; import { useFormikContext } from 'formik'; -import { - GetFeatureSupportLevel, - useNewFeatureSupportLevel, -} from '../../../common/components/newFeatureSupportLevels'; +import { useNewFeatureSupportLevel } from '../../../common/components/newFeatureSupportLevels'; import { useFeature } from '../../hooks/use-feature'; import { useSelector } from 'react-redux'; import { selectIsCurrentClusterSNO } from '../../store/slices/current-cluster/selectors'; import { getNewBundleOperators } from '../clusterConfiguration/operators/utils'; import { bundleSpecs } from '../clusterConfiguration/operators/bundleSpecs'; -import { useOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; - -import './OperatorsBundle.css'; +import { OperatorSpec, useOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; import { useClusterWizardContext } from './ClusterWizardContext'; +import './OperatorsBundle.css'; const BundleLabel = ({ bundle }: { bundle: Bundle }) => { - const opSpecs = useOperatorSpecs(); + const { byKey: opSpecs } = useOperatorSpecs(); const bundleSpec = bundleSpecs[bundle.id || '']; return ( @@ -81,19 +77,17 @@ const BundleLabel = ({ bundle }: { bundle: Bundle }) => { const getBundleSupportLevel = ( bundle: Bundle, - opSpecs: ReturnType, - getFeatureSupportLevel: GetFeatureSupportLevel, + opSpecsByKey: Record, ): NewSupportLevelBadgeProps['supportLevel'] => { let supportLevel: NewSupportLevelBadgeProps['supportLevel'] = undefined; if (bundle.operators) { for (const op of bundle.operators) { - const operatorSpec = opSpecs[op]; + const operatorSpec = opSpecsByKey[op]; if (operatorSpec) { - const opSupportLevel = getFeatureSupportLevel(operatorSpec.featureId); - if (opSupportLevel === 'dev-preview') { + if (operatorSpec.supportLevel === 'dev-preview') { supportLevel = 'dev-preview'; break; - } else if (opSupportLevel === 'tech-preview') { + } else if (operatorSpec.supportLevel === 'tech-preview') { supportLevel = 'tech-preview'; } } @@ -113,11 +107,11 @@ const BundleCard = ({ }) => { const { values, setFieldValue } = useFormikContext(); const isSNO = useSelector(selectIsCurrentClusterSNO); - const { isFeatureSupported, getFeatureSupportLevel } = useNewFeatureSupportLevel(); - const opSpecs = useOperatorSpecs(); + const { isFeatureSupported } = useNewFeatureSupportLevel(); + const { byKey: opSpecs } = useOperatorSpecs(); const { uiSettings } = useClusterWizardContext(); - const supportLevel = getBundleSupportLevel(bundle, opSpecs, getFeatureSupportLevel); + const supportLevel = getBundleSupportLevel(bundle, opSpecs); const hasUnsupportedOperators = !!bundle.operators?.some((op) => { const operatorSpec = opSpecs[op]; diff --git a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsSelect.tsx b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsSelect.tsx index a647b7133d..86d68955dc 100644 --- a/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsSelect.tsx +++ b/libs/ui-lib/lib/ocm/components/clusterWizard/OperatorsSelect.tsx @@ -53,22 +53,16 @@ const OperatorsSelect = ({ void fetchSupportedOperators(); }, [addAlert, setSupportedOperators, setIsLoading]); - const opSpecs = useOperatorSpecs(); + const { byCategory, byKey: opSpecs } = useOperatorSpecs(); const operators = React.useMemo(() => { - return supportedOperators - .sort((a, b) => { - const aTitle = opSpecs[a]?.title || a; - const bTitle = opSpecs[b]?.title || b; - return aTitle.localeCompare(bTitle); - }) - .filter((op) => { - if (!isSingleClusterFeatureEnabled) { - return true; - } - return singleClusterOperators.includes(op); - }); - }, [isSingleClusterFeatureEnabled, supportedOperators, opSpecs]); + return supportedOperators.filter((op) => { + if (!isSingleClusterFeatureEnabled) { + return true; + } + return singleClusterOperators.includes(op); + }); + }, [isSingleClusterFeatureEnabled, supportedOperators]); if (isLoading) { return ; @@ -86,22 +80,30 @@ const OperatorsSelect = ({ data-testid="single-operators-section" > - {operators.map((operatorKey) => { - if (!opSpecs[operatorKey]) { + {Object.entries(byCategory).map(([categoryName, specs]) => { + const categoryOperators = specs.filter((spec) => operators.includes(spec.operatorKey)); + if (categoryOperators.length === 0) { return null; } return ( - - - + + + {categoryName} + + {categoryOperators.map((spec) => ( + + + + ))} + ); })} diff --git a/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts b/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts index 411265f74b..856273153d 100644 --- a/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts +++ b/libs/ui-lib/lib/ocm/components/featureSupportLevels/featureStateUtils.ts @@ -4,11 +4,6 @@ import { CpuArchitecture, FeatureId, isSNO, - OPERATOR_NAME_CNV, - OPERATOR_NAME_LVM, - OPERATOR_NAME_ODF, - OPERATOR_NAME_OPENSHIFT_AI, - OPERATOR_NAME_OSC, SupportedCpuArchitecture, } from '../../../common'; import { @@ -17,7 +12,7 @@ import { SupportLevel, } from '@openshift-assisted/types/assisted-installer-service'; import { ExternalPlatformLabels } from '../clusterConfiguration/platformIntegration/constants'; -import { getOperatorSpecs } from '../../../common/components/operators/operatorSpecs'; +import { getOperatorTitleByFeatureId } from '../../../common/components/operators/operatorSpecs'; export const clusterExistsReason = 'This option is not editable after the draft cluster is created'; @@ -46,10 +41,7 @@ const getOdfDisabledReason = ( return undefined; } - const opSpecs = getOperatorSpecs(); - - const operatorTitle = opSpecs[OPERATOR_NAME_ODF]?.title || ''; - + const operatorTitle = getOperatorTitleByFeatureId('ODF') || ''; const isArm = activeFeatureConfiguration?.underlyingCpuArchitecture === CpuArchitecture.ARM; if (isArm && isSNO(cluster)) { return `${operatorTitle} is not available when using Single Node OpenShift or ARM CPU architecture.`; @@ -75,8 +67,8 @@ const getCnvDisabledReason = ( return undefined; } - const opSpecs = getOperatorSpecs(); - const operatorTitle = opSpecs[OPERATOR_NAME_CNV]?.title || ''; + const operatorTitle = getOperatorTitleByFeatureId('CNV') || ''; + if (platformType === 'nutanix') { return `${operatorTitle} is not available when Nutanix platform type is selected.`; } @@ -105,8 +97,7 @@ const getLvmDisabledReason = ( return undefined; } - const opSpecs = getOperatorSpecs(); - const operatorTitle = opSpecs[OPERATOR_NAME_LVM]?.title; + const operatorTitle = getOperatorTitleByFeatureId('LVM') || ''; if (platformType === 'nutanix') { return `${operatorTitle} is not supported when Nutanix platform type is selected.`; } @@ -125,8 +116,7 @@ const getOscDisabledReason = ( return undefined; } - const opSpecs = getOperatorSpecs(); - const operatorTitle = opSpecs[OPERATOR_NAME_OSC]?.title || ''; + const operatorTitle = getOperatorTitleByFeatureId('OSC') || ''; if (!isSupported) { return `${operatorTitle} is not supported in this OpenShift version.`; } @@ -299,8 +289,7 @@ const getOpenShiftAIDisabledReason = ( return undefined; } - const opSpecs = getOperatorSpecs(); - const operatorTitle = opSpecs[OPERATOR_NAME_OPENSHIFT_AI]?.title || ''; + const operatorTitle = getOperatorTitleByFeatureId('OPENSHIFT_AI') || ''; const isArm = activeFeatureConfiguration?.underlyingCpuArchitecture === CpuArchitecture.ARM; if (isArm) { return `${operatorTitle} is not available when ARM CPU architecture is selected.`;