From 23960370c509e4fad1784df8fe16a334b2ad1b00 Mon Sep 17 00:00:00 2001 From: SanjalKatiyar Date: Mon, 13 Nov 2023 15:31:09 +0530 Subject: [PATCH] refactoring (support ODF in any Namespace) Adds a new package "immer". Adds a new extension "console.redux-reducer", for redux state. Adds a new hook "useOprInstallNamespaceSelector", for detecting ODF install namespace. Removes dependency on "openshift-storage" namespace. --- package.json | 2 + .../dashboards/cards/activity-card.tsx | 7 +- packages/ibm/system-connection-details.tsx | 2 +- .../create-dr-policy/create-dr-policy.tsx | 1 + .../mco/components/mco-dashboard/queries.ts | 2 + .../parsers/application-set-parser.spec.tsx | 3 + .../ocs/block-pool/BlockPoolDetailsPage.tsx | 31 +- packages/ocs/block-pool/BlockPoolListPage.tsx | 76 +++-- packages/ocs/block-pool/CreateBlockPool.tsx | 30 +- packages/ocs/block-pool/body.tsx | 14 +- packages/ocs/block-pool/footer.tsx | 12 +- packages/ocs/constants/common.ts | 14 +- .../status-card/status-card.tsx | 15 +- .../activity-card/activity-card.tsx | 3 +- .../capacity-breakdown-card.spec.tsx | 8 + .../capacity-breakdown-card.tsx | 7 +- .../data-consumption-card.tsx | 12 +- .../details-card/details-card.tsx | 20 +- .../status-card/status-card.tsx | 3 +- .../ocs/dashboards/odf-system-dashboard.tsx | 9 +- .../persistent-external/breakdown-card.tsx | 4 + .../persistent-external/details-card.tsx | 24 +- .../activity-card/activity-card.tsx | 7 +- .../capacity-breakdown-card.tsx | 4 + .../persistent-internal/details-card.tsx | 24 +- .../persistent-internal/inventory-card.tsx | 7 +- .../status-card/status-card.tsx | 8 +- .../block-pool/create-block-pool-modal.tsx | 28 +- .../ocs/modals/block-pool/modal-footer.tsx | 8 +- packages/ocs/storage-class/mutators.ts | 24 +- packages/ocs/storage-class/sc-form.tsx | 127 ++++---- .../ocs/storage-class/thick-provisioner.tsx | 46 ++- packages/ocs/utils/block-pool.tsx | 2 +- packages/ocs/utils/common.ts | 17 +- .../external-storage/external-storage.ts | 1 + .../components/alerts/alert-action-path.tsx | 2 + .../bucket-class/backingstore-table.tsx | 20 +- .../odf/components/bucket-class/create-bc.tsx | 65 ++-- packages/odf/components/bucket-class/state.ts | 5 - .../wizard-pages/general-page.tsx | 59 ++-- .../HealthOverview.tsx | 9 +- .../create-bs/backing-store-dropdown.tsx | 14 +- .../odf/components/create-bs/create-bs.tsx | 218 +++++++------- .../backing-storage-step.tsx | 15 +- .../capacity-and-nodes-step.tsx | 17 +- .../create-local-volume-set-step.tsx | 13 +- .../create-storage-class-step.tsx | 14 +- .../review-and-create-step.tsx | 28 +- .../security-and-network-step/configure.tsx | 12 +- .../security-and-network-step/encryption.tsx | 4 - .../security-and-network-step.tsx | 6 +- .../system-connection-details.tsx | 13 +- .../create-storage-system/footer.tsx | 54 +++- .../create-storage-system/payloads.ts | 27 +- .../select-nodes-table/select-nodes-table.tsx | 16 +- .../odf/components/kms-config/kms-config.tsx | 5 +- packages/odf/components/kms-config/utils.tsx | 111 ++++--- .../components/kms-config/vault-config.tsx | 67 ++--- .../mcg-endpoints/pvc-endpoint-type.tsx | 11 +- .../mcg/CreateObjectBucketClaim.tsx | 121 ++++---- .../create-namespace-store.spec.tsx | 21 +- .../create-namespace-store.tsx | 10 +- .../namespace-store-dropdown.tsx | 10 +- .../namespace-store-form.spec.tsx | 30 +- .../namespace-store/namespace-store-form.tsx | 281 +++++++++--------- .../namespace-store/namespace-store-modal.tsx | 6 +- .../namespace-store/namespace-store-table.tsx | 20 +- .../object-service.tsx | 3 + .../components/odf-dashboard/dashboard.tsx | 7 +- .../performance-card/performance-card.tsx | 12 +- .../odf/components/odf-dashboard/queries.ts | 1 + .../odf-dashboard/status-card/status-card.tsx | 24 +- .../system-capacity-card/capacity-card.tsx | 24 +- .../BackingStoreDetailsPage.tsx | 24 +- .../resource-pages/BucketClassDetailsPage.tsx | 24 +- .../NamespaceStoreDetailsPage.tsx | 20 +- .../components/resource-pages/list-page.tsx | 29 +- .../system-list/odf-system-list.tsx | 14 +- packages/odf/components/topology/Topology.tsx | 46 +-- .../sidebar/TopologySideBarContent.tsx | 9 +- .../storage-cluster/StorageClusterDetails.tsx | 31 +- .../storage-cluster/StorageClusterObserve.tsx | 24 +- .../StorageClusterResources.tsx | 39 +-- packages/odf/components/utils/common.ts | 5 +- packages/odf/components/utils/safety-box.tsx | 62 ++++ packages/odf/constants/common.ts | 5 +- packages/odf/constants/mcg.ts | 4 +- packages/odf/constants/providerSchema.ts | 2 +- packages/odf/features.ts | 164 ++-------- packages/odf/hooks/index.ts | 4 + packages/odf/hooks/useSafeK8sGet.ts | 29 ++ packages/odf/hooks/useSafeK8sList.ts | 25 ++ packages/odf/hooks/useSafeK8sWatchResource.ts | 34 +++ .../odf/hooks/useSafeK8sWatchResources.ts | 32 ++ .../add-capacity/add-capacity-modal.tsx | 166 ++++++----- .../advanced-vault-modal.tsx | 16 +- packages/odf/redux/actions/index.ts | 1 + packages/odf/redux/actions/odf-namespace.ts | 19 ++ packages/odf/redux/combineReducers.ts | 7 + packages/odf/redux/dispatchers/index.ts | 1 + .../odf/redux/dispatchers/odf-namespace.ts | 16 + packages/odf/redux/index.ts | 3 + packages/odf/redux/provider-hooks/index.ts | 1 + .../redux/provider-hooks/useODFNamespace.ts | 69 +++++ packages/odf/redux/reducers/index.ts | 1 + packages/odf/redux/reducers/odf-namespace.ts | 25 ++ packages/odf/redux/selectors/index.ts | 1 + packages/odf/redux/selectors/odf-namespace.ts | 29 ++ packages/odf/resources.ts | 64 ++-- packages/odf/types/common.ts | 3 + packages/odf/utils/mcg.ts | 12 +- packages/odf/utils/ocs.ts | 5 +- .../breakdown-card/breakdown-body.tsx | 3 + .../breakdown-card/breakdown-chart.tsx | 15 +- .../custom-prometheus-poll-hook.ts | 2 +- .../src/hooks/custom-prometheus-poll/utils.ts | 8 +- packages/shared/src/hooks/k8s-get-hook.ts | 3 +- packages/shared/src/hooks/use-fetch-csv.tsx | 64 ++-- packages/shared/src/hooks/useK8sList.ts | 3 +- packages/shared/src/queries/pod.ts | 10 +- .../src/topology/sidebar/common/PodList.tsx | 2 +- .../sidebar/deployments/DeploymentObserve.tsx | 5 +- .../deployments/DeploymentResources.tsx | 39 +-- .../src/topology/sidebar/node/NodeObserve.tsx | 1 + .../topology/sidebar/node/NodeResources.tsx | 11 +- packages/shared/src/utils/index.ts | 1 + .../shared/src/utils/valid-k8s-resources.ts | 49 +++ plugins/odf/console-extensions.json | 70 +---- plugins/odf/console-plugin.json | 3 +- yarn.lock | 5 + 130 files changed, 1926 insertions(+), 1410 deletions(-) create mode 100644 packages/odf/components/utils/safety-box.tsx create mode 100644 packages/odf/hooks/index.ts create mode 100644 packages/odf/hooks/useSafeK8sGet.ts create mode 100644 packages/odf/hooks/useSafeK8sList.ts create mode 100644 packages/odf/hooks/useSafeK8sWatchResource.ts create mode 100644 packages/odf/hooks/useSafeK8sWatchResources.ts create mode 100644 packages/odf/redux/actions/index.ts create mode 100644 packages/odf/redux/actions/odf-namespace.ts create mode 100644 packages/odf/redux/combineReducers.ts create mode 100644 packages/odf/redux/dispatchers/index.ts create mode 100644 packages/odf/redux/dispatchers/odf-namespace.ts create mode 100644 packages/odf/redux/index.ts create mode 100644 packages/odf/redux/provider-hooks/index.ts create mode 100644 packages/odf/redux/provider-hooks/useODFNamespace.ts create mode 100644 packages/odf/redux/reducers/index.ts create mode 100644 packages/odf/redux/reducers/odf-namespace.ts create mode 100644 packages/odf/redux/selectors/index.ts create mode 100644 packages/odf/redux/selectors/odf-namespace.ts create mode 100644 packages/shared/src/utils/valid-k8s-resources.ts diff --git a/package.json b/package.json index b5b4db912..7279e574b 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "history": "^4.9.0", "i18next": "^20.2.1", "i18next-conv": "^10.1.0", + "immer": "^10.0.3", "js-base64": "^2.1.9", "js-yaml": "^3.13.1", "lodash-es": "^4.17.21", @@ -108,6 +109,7 @@ "ts-loader": "^9.4.1", "ts-node": "5.0.1", "tsm": "^2.3.0", + "typesafe-actions": "^4.2.1", "typescript": "^4.8", "victory-core": "^35.4.4", "webpack": "5.74.0", diff --git a/packages/client/src/components/dashboards/cards/activity-card.tsx b/packages/client/src/components/dashboards/cards/activity-card.tsx index 1d8566be6..c485f3ea6 100644 --- a/packages/client/src/components/dashboards/cards/activity-card.tsx +++ b/packages/client/src/components/dashboards/cards/activity-card.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { PVC_PROVISIONER_ANNOTATION } from '@odf/ocs/constants'; import { eventsResource, @@ -48,6 +49,8 @@ const getClientOperatorSubscription = ( ) as SubscriptionKind; const RecentEvent: React.FC = () => { + const { odfNamespace } = useODFNamespaceSelector(); + const [pvcs, pvcLoaded, pvcLoadError] = useK8sWatchResource(pvcResource); const [events, eventsLoaded, eventsLoadError] = @@ -61,8 +64,8 @@ const RecentEvent: React.FC = () => { const memoizedPVCNames = useDeepCompareMemoize(validPVC, true); const clientEventsFilter = React.useCallback( - () => isPersistentStorageEvent(memoizedPVCNames), - [memoizedPVCNames] + () => isPersistentStorageEvent(memoizedPVCNames, odfNamespace), + [memoizedPVCNames, odfNamespace] ); const eventObject = { diff --git a/packages/ibm/system-connection-details.tsx b/packages/ibm/system-connection-details.tsx index 76c623b5a..1be1650a0 100644 --- a/packages/ibm/system-connection-details.tsx +++ b/packages/ibm/system-connection-details.tsx @@ -166,9 +166,9 @@ export const createFlashSystemPayload: CreatePayload = ({ systemName, state, model, + namespace, storageClassName, }) => { - const namespace = 'openshift-storage'; const defaultFilesystem = 'ext4'; const defaultVolumeMode = 'thick'; const defaultVolumePrefix = 'odf'; diff --git a/packages/mco/components/create-dr-policy/create-dr-policy.tsx b/packages/mco/components/create-dr-policy/create-dr-policy.tsx index d515bf36c..36a6f0a81 100644 --- a/packages/mco/components/create-dr-policy/create-dr-policy.tsx +++ b/packages/mco/components/create-dr-policy/create-dr-policy.tsx @@ -167,6 +167,7 @@ export const CreateDRPolicy: React.FC = ({ clusterName: cluster?.name, storageClusterRef: { name: cluster.storageClusterName, + // ToDo (epic 4422): Need to update this as per ConfigMap/ClusterClaim (whichever us decided) JSON output namespace: CEPH_STORAGE_NAMESPACE, }, })), diff --git a/packages/mco/components/mco-dashboard/queries.ts b/packages/mco/components/mco-dashboard/queries.ts index a638a42a7..a19f5072b 100644 --- a/packages/mco/components/mco-dashboard/queries.ts +++ b/packages/mco/components/mco-dashboard/queries.ts @@ -26,6 +26,7 @@ export const LAST_SYNC_TIME_QUERY = 'ramen_sync_duration_seconds'; export const getLastSyncPerClusterQuery = () => `${LAST_SYNC_TIME_QUERY}{${DRPC_OBJECT_TYPE}, ${RAMEN_HUB_OPERATOR_METRICS_SERVICE}}`; +// ToDo (epic 4422): Need to update as per updates in the metrics export const CAPACITY_QUERIES = { [StorageDashboard.TOTAL_CAPACITY_FILE_BLOCK]: `(label_replace(odf_system_map{target_namespace="openshift-storage"} , "managedBy", "$1", "target_name", "(.*)")) * on (namespace, managedBy, cluster) group_right(storage_system, target_kind) ${TOTAL_CAPACITY_FILE_BLOCK_METRIC}`, [StorageDashboard.USED_CAPACITY_FILE_BLOCK]: `(label_replace(odf_system_map{target_namespace="openshift-storage"} , "managedBy", "$1", "target_name", "(.*)")) * on (namespace, managedBy, cluster) group_right(storage_system, target_kind) ${USED_CAPACITY_FILE_BLOCK_METRIC}`, @@ -43,6 +44,7 @@ export const getRBDSnapshotUtilizationQuery = ( return queries[queryName]; }; +// ToDo (epic 4422): Need to update as per updates in the metrics export const STATUS_QUERIES = { [StorageDashboard.SYSTEM_HEALTH]: `(label_replace(odf_system_map{target_namespace="openshift-storage"} , "managedBy", "$1", "target_name", "(.*)")) * on (namespace, managedBy, cluster) group_right(storage_system, target_kind) ${SYSTEM_HEALTH_METRIC}`, [StorageDashboard.HEALTH]: SYSTEM_HEALTH_METRIC, diff --git a/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx b/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx index 70209f0f2..de55ab9f9 100644 --- a/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx +++ b/packages/mco/components/modals/app-manage-policies/parsers/application-set-parser.spec.tsx @@ -273,6 +273,7 @@ const appResources2: ArgoApplicationSetResourceKind = { }; const onClose = jest.fn(); + jest.mock('@odf/mco/hooks/disaster-recovery', () => ({ __esModule: true, useDisasterRecoveryResourceWatch: jest.fn(() => { @@ -283,6 +284,7 @@ jest.mock('@odf/mco/hooks/disaster-recovery', () => ({ } }), })); + jest.mock('@odf/mco/hooks/argo-application-set', () => ({ __esModule: true, useArgoApplicationSetResourceWatch: jest.fn(() => { @@ -293,6 +295,7 @@ jest.mock('@odf/mco/hooks/argo-application-set', () => ({ } }), })); + jest.mock('../utils/k8s-utils', () => ({ unAssignPromises: jest.fn(() => [Promise.resolve({ data: {} })]), })); diff --git a/packages/ocs/block-pool/BlockPoolDetailsPage.tsx b/packages/ocs/block-pool/BlockPoolDetailsPage.tsx index 834958f67..fad036e7e 100644 --- a/packages/ocs/block-pool/BlockPoolDetailsPage.tsx +++ b/packages/ocs/block-pool/BlockPoolDetailsPage.tsx @@ -1,14 +1,13 @@ import * as React from 'react'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import DetailsPage from '@odf/shared/details-page/DetailsPage'; -import { LoadingBox } from '@odf/shared/generic/status-box'; import { Kebab } from '@odf/shared/kebab/kebab'; import { ModalKeys } from '@odf/shared/modals/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { referenceForModel } from '@odf/shared/utils'; import { EventStreamWrapped, YAMLEditorWrapped } from '@odf/shared/utils/Tabs'; -import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; import { RouteComponentProps, useLocation } from 'react-router-dom'; -import { CEPH_NS } from '../constants'; import { BlockPoolDashboard } from '../dashboards/block-pool/block-pool-dashboard'; import { CephBlockPoolModel, CephClusterModel } from '../models'; import { StoragePoolKind } from '../types'; @@ -33,12 +32,16 @@ export const BlockPoolDetailsPage: React.FC = ({ const location = useLocation(); const kind = referenceForModel(CephBlockPoolModel); - const [resource, loaded] = useK8sWatchResource({ - kind, - name: poolName, - namespace: CEPH_NS, - isList: false, - }); + const { odfNamespace, isODFNsLoaded, odfNsLoadError } = + useODFNamespaceSelector(); + + const [resource, loaded, loadError] = + useSafeK8sWatchResource((ns: string) => ({ + kind, + name: poolName, + namespace: ns, + isList: false, + })); const breadcrumbs = [ { @@ -62,7 +65,7 @@ export const BlockPoolDetailsPage: React.FC = ({ extraProps={{ resource, resourceModel: CephBlockPoolModel, - namespace: CEPH_NS, + namespace: odfNamespace, }} customKebabItems={[ { @@ -82,12 +85,12 @@ export const BlockPoolDetailsPage: React.FC = ({ ]} /> ); - }, [resource, t]); + }, [resource, odfNamespace, t]); - return !loaded ? ( - - ) : ( + return ( ({ ceph: { kind: referenceForModel(CephClusterModel), namespaced: true, - namespace: CEPH_NS, + namespace: ns, isList: true, }, sc: { @@ -386,7 +391,7 @@ const resources = { kind: referenceForModel(CephBlockPoolModel), isList: true, }, -}; +}); type WatchType = { sc: StorageClassResourceKind[]; @@ -397,10 +402,15 @@ type WatchType = { export const BlockPoolListPage: React.FC = ({}) => { const { t } = useCustomTranslation(); + const { odfNamespace, isODFNsLoaded, odfNsLoadError, isNsSafe } = + useODFNamespaceSelector(); + const location = useLocation(); const listPagePath: string = location.pathname; - const response = useK8sWatchResources(resources); + const response = useSafeK8sWatchResources( + resources + ) as WatchK8sResults; const cephClusters = response.ceph.data; const cephLoaded = response.ceph.loaded; @@ -423,27 +433,37 @@ export const BlockPoolListPage: React.FC = ({}) => { // Metrics const [poolRawCapacityMetrics, rawCapLoadError, rawCapLoading] = - useCustomPrometheusPoll({ - endpoint: 'api/v1/query' as any, - query: getPoolQuery( - memoizedPoolNames, - StorageDashboardQuery.POOL_RAW_CAPACITY_USED - ), - namespace: CEPH_NS, - basePath: usePrometheusBasePath(), - }); + useCustomPrometheusPoll( + getValidPrometheusPollObj( + { + endpoint: 'api/v1/query' as any, + query: getPoolQuery( + memoizedPoolNames, + StorageDashboardQuery.POOL_RAW_CAPACITY_USED + ), + namespace: odfNamespace, + basePath: usePrometheusBasePath(), + }, + isNsSafe + ) + ); // compression queries const [compressionSavings, compressionLoadError, compressionLoading] = - useCustomPrometheusPoll({ - endpoint: 'api/v1/query' as any, - query: getPoolQuery( - poolNames, - StorageDashboardQuery.POOL_COMPRESSION_SAVINGS - ), - namespace: CEPH_NS, - basePath: usePrometheusBasePath(), - }); + useCustomPrometheusPoll( + getValidPrometheusPollObj( + { + endpoint: 'api/v1/query' as any, + query: getPoolQuery( + poolNames, + StorageDashboardQuery.POOL_COMPRESSION_SAVINGS + ), + namespace: odfNamespace, + basePath: usePrometheusBasePath(), + }, + isNsSafe + ) + ); const customData = React.useMemo(() => { const poolRawCapacity: PoolMetrics = getPerPoolMetrics( @@ -498,15 +518,15 @@ export const BlockPoolListPage: React.FC = ({}) => { diff --git a/packages/ocs/block-pool/CreateBlockPool.tsx b/packages/ocs/block-pool/CreateBlockPool.tsx index bdc8aa6d9..820d8ec2f 100644 --- a/packages/ocs/block-pool/CreateBlockPool.tsx +++ b/packages/ocs/block-pool/CreateBlockPool.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { StatusBox } from '@odf/shared/generic/status-box'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; import { CephClusterKind } from '@odf/shared/types'; @@ -7,13 +9,11 @@ import { referenceForModel } from '@odf/shared/utils'; import { getAPIVersionForModel, k8sCreate, - useK8sWatchResource, } from '@openshift-console/dynamic-plugin-sdk'; import { match, useHistory } from 'react-router'; import { Button, Modal } from '@patternfly/react-core'; import { CEPH_EXTERNAL_CR_NAME, - CEPH_NS, COMPRESSION_ON, POOL_STATE, } from '../constants'; @@ -30,12 +30,15 @@ import { } from './reducer'; import './create-block-pool.scss'; -export const getPoolKindObj = (state: BlockPoolState): StoragePoolKind => ({ +export const getPoolKindObj = ( + state: BlockPoolState, + ns: string +): StoragePoolKind => ({ apiVersion: getAPIVersionForModel(CephBlockPoolModel), kind: CephBlockPoolModel.kind, metadata: { name: state.poolName, - namespace: CEPH_NS, + namespace: ns, }, spec: { compressionMode: state.isCompressed ? COMPRESSION_ON : 'none', @@ -50,11 +53,11 @@ export const getPoolKindObj = (state: BlockPoolState): StoragePoolKind => ({ }, }); -export const cephClusterResource = { +export const cephClusterResource = (ns: string) => ({ kind: referenceForModel(CephClusterModel), - namespace: CEPH_NS, + namespace: ns, isList: true, -}; +}); const CreateBlockPool: React.FC = ({ match: blockPoolMatch, @@ -69,13 +72,16 @@ const CreateBlockPool: React.FC = ({ blockPoolInitialState ); const [cephClusters, isLoaded, loadError] = - useK8sWatchResource(cephClusterResource); + useSafeK8sWatchResource(cephClusterResource); const cephCluster: CephClusterKind = useDeepCompareMemoize( cephClusters[0], true ); + const { odfNamespace, isODFNsLoaded, odfNsLoadError } = + useODFNamespaceSelector(); + // OCS create pool page url ends with ~new, ODF create pool page ends with /create/~new const blockPoolPageUrl = params?.appName ? url.replace('/~new', '') @@ -88,7 +94,7 @@ const CreateBlockPool: React.FC = ({ // Create new pool const createPool = () => { if (cephCluster?.status?.phase === POOL_STATE.READY) { - const poolObj: StoragePoolKind = getPoolKindObj(state); + const poolObj: StoragePoolKind = getPoolKindObj(state, odfNamespace); dispatch({ type: BlockPoolActionType.SET_INPROGRESS, payload: true }); k8sCreate({ model: CephBlockPoolModel, data: poolObj }) @@ -148,7 +154,7 @@ const CreateBlockPool: React.FC = ({

- {isLoaded && !loadError ? ( + {isLoaded && isODFNsLoaded && !loadError && !odfNsLoadError ? ( <> = ({ ) : ( )} diff --git a/packages/ocs/block-pool/body.tsx b/packages/ocs/block-pool/body.tsx index 2e150e3e3..76be1d64f 100644 --- a/packages/ocs/block-pool/body.tsx +++ b/packages/ocs/block-pool/body.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; +import { useSafeK8sGet, useSafeK8sList } from '@odf/core/hooks'; import { checkArbiterCluster } from '@odf/core/utils'; import { fieldRequirementsTranslations, formSettings, } from '@odf/shared/constants'; -import { useK8sGet } from '@odf/shared/hooks/k8s-get-hook'; -import { useK8sList } from '@odf/shared/hooks/useK8sList'; import { TextInputWithFieldRequirements } from '@odf/shared/input-with-requirements'; import { getName } from '@odf/shared/selectors'; import { @@ -29,12 +28,7 @@ import { } from '@patternfly/react-core'; import { CaretDownIcon } from '@patternfly/react-icons'; import validationRegEx from '../../odf/utils/validation'; -import { - CEPH_NS, - OCS_DEVICE_REPLICA, - POOL_PROGRESS, - POOL_STATE, -} from '../constants'; +import { OCS_DEVICE_REPLICA, POOL_PROGRESS, POOL_STATE } from '../constants'; import { StorageClusterModel, CephBlockPoolModel } from '../models'; import { getErrorMessage, @@ -79,11 +73,11 @@ export const BlockPoolBody = (props: BlockPoolBodyPros) => { const { t } = useCustomTranslation(); const [storageCluster, storageClusterLoaded, storageClusterLoadError] = - useK8sGet>(StorageClusterModel, null, CEPH_NS); + useSafeK8sGet>(StorageClusterModel, null); const [isReplicaOpen, setReplicaOpen] = React.useState(false); - const [data, loaded, loadError] = useK8sList(CephBlockPoolModel, CEPH_NS); + const [data, loaded, loadError] = useSafeK8sList(CephBlockPoolModel); const { schema, fieldRequirements } = React.useMemo(() => { const existingNames = diff --git a/packages/ocs/block-pool/footer.tsx b/packages/ocs/block-pool/footer.tsx index e44da1e67..03720b982 100644 --- a/packages/ocs/block-pool/footer.tsx +++ b/packages/ocs/block-pool/footer.tsx @@ -1,24 +1,19 @@ import * as React from 'react'; import { ButtonBar } from '@odf/shared/generic/ButtonBar'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { ActionGroup, Button } from '@patternfly/react-core'; -import { OCS_POOL_MANAGEMENT } from '../constants'; import { BlockPoolState } from './reducer'; import './create-block-pool.scss'; export const checkRequiredValues = ( poolName: string, replicaSize: string, - volumeType: string, - isPoolManagementSupported: boolean -): boolean => - !poolName || !replicaSize || (isPoolManagementSupported && !volumeType); + volumeType: string +): boolean => !poolName || !replicaSize || !volumeType; export const BlockPoolFooter = (props: BlockPoolFooterProps) => { const { state, cancel, onConfirm } = props; const { t } = useCustomTranslation(); - const isPoolManagementSupported = useFlag(OCS_POOL_MANAGEMENT); return ( @@ -31,8 +26,7 @@ export const BlockPoolFooter = (props: BlockPoolFooterProps) => { isDisabled={checkRequiredValues( state.poolName, state.replicaSize, - state.volumeType, - isPoolManagementSupported + state.volumeType )} > {t('Create')} diff --git a/packages/ocs/constants/common.ts b/packages/ocs/constants/common.ts index 85e8824d2..b0ecce9c7 100644 --- a/packages/ocs/constants/common.ts +++ b/packages/ocs/constants/common.ts @@ -1,11 +1,6 @@ export const PVC_PROVISIONER_ANNOTATION = 'volume.beta.kubernetes.io/storage-provisioner'; export const OCS_OPERATOR = 'ocs-operator'; - -export const CEPH_NS = 'openshift-storage'; - -export const cephStorageLabel = 'cluster.ocs.openshift.io/openshift-storage'; - export const COMPRESSION_ON = 'aggressive'; export const CEPH_EXTERNAL_CR_NAME = 'ocs-external-storagecluster-cephcluster'; @@ -34,11 +29,16 @@ export const OCS_DEVICE_REPLICA = Object.freeze({ '4': '4-way', }); -export const OCS_POOL_MANAGEMENT = 'OCS_POOL_MANAGEMENT'; - export enum CLUSTER_STATUS { READY = 'Ready', PROGRESSING = 'Progressing', } export const CEPH_INTERNAL_CR_NAME = 'ocs-storagecluster-cephcluster'; + +export const CLUSTER_ID = 'clusterID'; +export const PROV_SECRET_NS = 'csi.storage.k8s.io/provisioner-secret-namespace'; +export const NODE_SECRET_NS = 'csi.storage.k8s.io/node-stage-secret-namespace'; +export const CONTROLLER_SECRET_NS = + 'csi.storage.k8s.io/controller-expand-secret-namespace'; +export const CEPH_NS_SESSION_STORAGE = 'odfConsole_scForm_cephNs'; diff --git a/packages/ocs/dashboards/network-file-system/status-card/status-card.tsx b/packages/ocs/dashboards/network-file-system/status-card/status-card.tsx index f67542154..94842a86b 100644 --- a/packages/ocs/dashboards/network-file-system/status-card/status-card.tsx +++ b/packages/ocs/dashboards/network-file-system/status-card/status-card.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { K8sResourceObj } from '@odf/core/types'; import HealthItem from '@odf/shared/dashboards/status-card/HealthItem'; import { PodModel } from '@odf/shared/models'; import { PodKind } from '@odf/shared/types'; @@ -9,28 +10,24 @@ import { getPodStatus, getPodHealthState, } from '@odf/shared/utils'; -import { - useK8sWatchResource, - WatchK8sResource, -} from '@openshift-console/dynamic-plugin-sdk'; import { Card, CardBody, CardHeader, CardTitle } from '@patternfly/react-core'; -const nfsPodResource: WatchK8sResource = { +const nfsPodResource: K8sResourceObj = (ns) => ({ isList: true, kind: referenceForModel(PodModel), - namespace: CEPH_STORAGE_NAMESPACE, + namespace: ns, selector: { matchLabels: { app: 'rook-ceph-nfs', ceph_nfs: 'ocs-storagecluster-cephnfs', }, }, -}; +}); export const StatusCard: React.FC = () => { const { t } = useCustomTranslation(); const [nfsPods, nfsPodsLoaded, nfsPodsLoadError] = - useK8sWatchResource(nfsPodResource); + useSafeK8sWatchResource(nfsPodResource); const serverHealthState = getPodHealthState( getPodStatus(nfsPods?.[0]), diff --git a/packages/ocs/dashboards/object-service/activity-card/activity-card.tsx b/packages/ocs/dashboards/object-service/activity-card/activity-card.tsx index d200c7c7e..78ce50305 100644 --- a/packages/ocs/dashboards/object-service/activity-card/activity-card.tsx +++ b/packages/ocs/dashboards/object-service/activity-card/activity-card.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { RGW_FLAG } from '@odf/core/features'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; import { secretResource } from '@odf/core/resources'; import { useCustomPrometheusPoll, @@ -42,7 +43,7 @@ const RecentEvent: React.FC = () => { const OngoingActivity: React.FC = () => { const [data, loaded, loadError] = - useK8sWatchResource(secretResource); + useSafeK8sWatchResource(secretResource); const isRGWSupported = useFlag(RGW_FLAG); const rgwPrefix = React.useMemo( diff --git a/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.spec.tsx b/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.spec.tsx index a0eef9dfa..8f112f9eb 100644 --- a/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.spec.tsx +++ b/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.spec.tsx @@ -23,6 +23,14 @@ jest.mock( }) ); +jest.mock('@odf/core/hooks', () => ({ + useSafeK8sWatchResource: () => [true, false, false], +})); + +jest.mock('@odf/core/redux', () => ({ + useODFNamespaceSelector: () => ({ odfNamespace: '' }), +})); + describe('Capacity Breakdown Card', () => { it('renders the Capacity Breakdown Card', () => { render(); diff --git a/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.tsx b/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.tsx index 99009b141..d7bfed35c 100644 --- a/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.tsx +++ b/packages/ocs/dashboards/object-service/capacity-breakdown/capacity-breakdown-card.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; import { RGW_FLAG } from '@odf/core/features'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { secretResource } from '@odf/core/resources'; import { BreakdownCardBody } from '@odf/shared/dashboards/breakdown-card/breakdown-body'; import { LabelPadding } from '@odf/shared/dashboards/breakdown-card/breakdown-chart'; @@ -98,6 +100,8 @@ const BreakdownCardBody_: React.FC = ({ }) => { const { t } = useCustomTranslation(); + const { odfNamespace } = useODFNamespaceSelector(); + // For charts whose datapoints are composed of multiple queries const flattenedResponse = response.reduce( (acc, curr, ind) => (ind < response?.length - 1 ? [...acc, ...curr] : acc), @@ -130,6 +134,7 @@ const BreakdownCardBody_: React.FC = ({ humanize={humanizeBinaryBytes} ocsVersion={ocsVersion} labelPadding={labelPadding} + odfNamespace={odfNamespace} /> ); }; @@ -304,7 +309,7 @@ const BreakdownCard: React.FC = () => { }, [isRGWSupported]); const [secretData, secretLoaded, secretLoadError] = - useK8sWatchResource(secretResource); + useSafeK8sWatchResource(secretResource); const rgwPrefix = React.useMemo( () => isRGWSupported && secretLoaded && !secretLoadError diff --git a/packages/ocs/dashboards/object-service/data-consumption-card/data-consumption-card.tsx b/packages/ocs/dashboards/object-service/data-consumption-card/data-consumption-card.tsx index e41d01cd9..131f7d3e6 100644 --- a/packages/ocs/dashboards/object-service/data-consumption-card/data-consumption-card.tsx +++ b/packages/ocs/dashboards/object-service/data-consumption-card/data-consumption-card.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; import { RGW_FLAG } from '@odf/core/features'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { K8sResourceObj } from '@odf/core/types'; import { OCS_OPERATOR, Breakdown, @@ -8,7 +10,6 @@ import { } from '@odf/ocs/constants'; import { DATA_CONSUMPTION_QUERIES } from '@odf/ocs/queries'; import { getRangeVectorStats } from '@odf/shared/charts'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { FieldLevelHelp } from '@odf/shared/generic/FieldLevelHelp'; import { useCustomPrometheusPoll, @@ -21,7 +22,6 @@ import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { DataPoint } from '@odf/shared/utils'; import { referenceForModel } from '@odf/shared/utils'; import { - useK8sWatchResource, PrometheusResponse, useFlag, } from '@openshift-console/dynamic-plugin-sdk'; @@ -37,11 +37,11 @@ const timeSpan = { [ServiceType.MCG]: null, }; -const csvResource = { +const csvResource: K8sResourceObj = (ns) => ({ isList: true, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: ns, kind: referenceForModel(ClusterServiceVersionModel), -}; +}); type ServiceTypeProps = { queries: string[]; @@ -242,7 +242,7 @@ const DataConsumptionCard: React.FC = () => { const [serviceType, setServiceType] = React.useState(ServiceType.MCG); const RGW = useFlag(RGW_FLAG); const [csvList, csvLoaded, csvLoadError] = - useK8sWatchResource(csvResource); + useSafeK8sWatchResource(csvResource); const isOCS45 = csvLoaded && !csvLoadError && diff --git a/packages/ocs/dashboards/object-service/details-card/details-card.tsx b/packages/ocs/dashboards/object-service/details-card/details-card.tsx index 62deb5912..22c40bddf 100644 --- a/packages/ocs/dashboards/object-service/details-card/details-card.tsx +++ b/packages/ocs/dashboards/object-service/details-card/details-card.tsx @@ -2,8 +2,9 @@ import * as React from 'react'; import { OCS_OPERATOR } from '@odf/core/constants'; import { ODF_MODEL_FLAG } from '@odf/core/features'; import { RGW_FLAG } from '@odf/core/features'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { getOperatorVersion } from '@odf/core/utils'; -import { CEPH_STORAGE_NAMESPACE, ODF_OPERATOR } from '@odf/shared/constants'; +import { ODF_OPERATOR } from '@odf/shared/constants'; import { useCustomPrometheusPoll, usePrometheusBasePath, @@ -34,6 +35,8 @@ export const ObjectServiceDetailsCard: React.FC<{}> = () => { const [infrastructure, infrastructureLoaded, infrastructureError] = useK8sGet(InfrastructureModel, 'cluster'); + const { odfNamespace, isNsSafe } = useODFNamespaceSelector(); + const [systemResult, systemLoadError] = useCustomPrometheusPoll({ query: NOOBAA_SYSTEM_NAME_QUERY, endpoint: 'api/v1/query' as any, @@ -57,7 +60,8 @@ export const ObjectServiceDetailsCard: React.FC<{}> = () => { const [csv, csvLoaded, csvLoadError] = useFetchCsv({ specName: !isODF ? OCS_OPERATOR : ODF_OPERATOR, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, + startPollingInstantly: isNsSafe, }); const serviceVersion = getOperatorVersion(csv); @@ -70,7 +74,7 @@ export const ObjectServiceDetailsCard: React.FC<{}> = () => { const servicePath = `${resourcePathFromModel( ClusterServiceVersionModel, getName(csv), - CEPH_STORAGE_NAMESPACE + odfNamespace )}`; return ( @@ -80,7 +84,7 @@ export const ObjectServiceDetailsCard: React.FC<{}> = () => { - {csvLoaded && !csvLoadError ? ( + {isNsSafe && csvLoaded && !csvLoadError ? ( {serviceName} ) : ( serviceName @@ -89,13 +93,13 @@ export const ObjectServiceDetailsCard: React.FC<{}> = () => {

{t('Multicloud Object Gateway')} diff --git a/packages/ocs/dashboards/object-service/status-card/status-card.tsx b/packages/ocs/dashboards/object-service/status-card/status-card.tsx index 053db2a7b..d7bfd94bd 100644 --- a/packages/ocs/dashboards/object-service/status-card/status-card.tsx +++ b/packages/ocs/dashboards/object-service/status-card/status-card.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { RGW_FLAG } from '@odf/core/features'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; import { CephObjectStoreModel } from '@odf/core/models'; import { NooBaaSystemModel } from '@odf/core/models'; import { secretResource } from '@odf/core/resources'; @@ -82,7 +83,7 @@ const StatusCard: React.FC<{}> = () => { const { t } = useCustomTranslation(); const [secretData, secretLoaded, secretLoadError] = - useK8sWatchResource(secretResource); + useSafeK8sWatchResource(secretResource); const [noobaa, noobaaLoaded, noobaaLoadError] = useK8sWatchResource(noobaaResource); const [rgw, rgwLoaded, rgwLoadError] = useK8sWatchResource( diff --git a/packages/ocs/dashboards/odf-system-dashboard.tsx b/packages/ocs/dashboards/odf-system-dashboard.tsx index c67bbdaa1..c59b27b5c 100644 --- a/packages/ocs/dashboards/odf-system-dashboard.tsx +++ b/packages/ocs/dashboards/odf-system-dashboard.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { LoadingBox } from '@odf/shared/generic/status-box'; +import NamespaceSafetyBox from '@odf/core/components/utils/safety-box'; import PageHeading from '@odf/shared/heading/page-heading'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { referenceForModel } from '@odf/shared/utils'; @@ -63,15 +63,14 @@ const ODFSystemDashboard: React.FC = ({ }, [isExternal, isCephAvailable, pages, setPages, t]); const title = match.params.systemName; + const arePagesLoaded = pages.length > 0; return ( <> - {pages.length > 0 ? ( + - ) : ( - - )} + ); }; diff --git a/packages/ocs/dashboards/persistent-external/breakdown-card.tsx b/packages/ocs/dashboards/persistent-external/breakdown-card.tsx index bd127ff27..c2c4fe1b5 100644 --- a/packages/ocs/dashboards/persistent-external/breakdown-card.tsx +++ b/packages/ocs/dashboards/persistent-external/breakdown-card.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { BreakdownCardBody } from '@odf/shared/dashboards/breakdown-card/breakdown-body'; import { getSelectOptions } from '@odf/shared/dashboards/breakdown-card/breakdown-dropdown'; import { @@ -40,6 +41,8 @@ export const BreakdownCard: React.FC = () => { const [isOpenBreakdownSelect, setBreakdownSelect] = React.useState(false); const [pvcNamespace, setPVCNamespace] = React.useState(''); + const { odfNamespace } = useODFNamespaceSelector(); + const { queries, model, metric } = getBreakdownMetricsQuery( metricType, pvcNamespace, @@ -127,6 +130,7 @@ export const BreakdownCard: React.FC = () => { top5MetricsStats={top5MetricsStats} metricModel={model} humanize={humanize} + odfNamespace={odfNamespace} /> {metricType === BreakdownCardFieldsWithParams.PVCS && !queriesLoadError && diff --git a/packages/ocs/dashboards/persistent-external/details-card.tsx b/packages/ocs/dashboards/persistent-external/details-card.tsx index e1b12427b..4ee8de349 100644 --- a/packages/ocs/dashboards/persistent-external/details-card.tsx +++ b/packages/ocs/dashboards/persistent-external/details-card.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import { CEPH_BRAND_NAME, OCS_OPERATOR } from '@odf/core/constants'; import { ODF_MODEL_FLAG } from '@odf/core/features'; +import { useSafeK8sWatchResources } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { getOperatorVersion } from '@odf/core/utils'; -import { CEPH_STORAGE_NAMESPACE, ODF_OPERATOR } from '@odf/shared/constants'; +import { ODF_OPERATOR } from '@odf/shared/constants'; import { useFetchCsv } from '@odf/shared/hooks/use-fetch-csv'; import { SecretModel } from '@odf/shared/models'; import { getName } from '@odf/shared/selectors'; @@ -10,10 +12,7 @@ import { SecretKind, K8sResourceKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { referenceForModel } from '@odf/shared/utils'; import { ExternalLink } from '@odf/shared/utils/link'; -import { - useFlag, - useK8sWatchResources, -} from '@openshift-console/dynamic-plugin-sdk'; +import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { DetailsBody } from '@openshift-console/dynamic-plugin-sdk-internal'; import { OverviewDetailItem as DetailItem } from '@openshift-console/plugin-shared'; import { Base64 } from 'js-base64'; @@ -34,25 +33,27 @@ type ResourcesObject = { }; }; -const k8sResources = { +const k8sResources = (ns: string) => ({ ocs: { kind: referenceForModel(StorageClusterModel), namespaced: true, isList: true, - namespace: 'openshift-storage', + namespace: ns, }, secret: { kind: SecretModel.kind, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: ns, name: 'rook-ceph-dashboard-link', }, -}; +}); export const DetailsCard: React.FC = () => { const { t } = useCustomTranslation(); const isODF = useFlag(ODF_MODEL_FLAG); - const resourcesObj: ResourcesObject = useK8sWatchResources(k8sResources); + const { odfNamespace, isNsSafe } = useODFNamespaceSelector(); + + const resourcesObj: ResourcesObject = useSafeK8sWatchResources(k8sResources); const inTransitEncryptionStatus = getNetworkEncryption( resourcesObj['ocs'].data?.[0] ) @@ -63,7 +64,8 @@ export const DetailsCard: React.FC = () => { const [csv, csvLoaded, csvError] = useFetchCsv({ specName: !isODF ? OCS_OPERATOR : ODF_OPERATOR, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, + startPollingInstantly: isNsSafe, }); const subscriptionVersion = getOperatorVersion(csv); diff --git a/packages/ocs/dashboards/persistent-internal/activity-card/activity-card.tsx b/packages/ocs/dashboards/persistent-internal/activity-card/activity-card.tsx index e8604748c..b1f0829e4 100644 --- a/packages/ocs/dashboards/persistent-internal/activity-card/activity-card.tsx +++ b/packages/ocs/dashboards/persistent-internal/activity-card/activity-card.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { useCustomPrometheusPoll, usePrometheusBasePath, @@ -65,6 +66,8 @@ export const eventsResource = { }; const RecentEvent: React.FC = () => { + const { odfNamespace } = useODFNamespaceSelector(); + const [pvcs, pvcLoaded] = useK8sWatchResource(pvcResource); const [events, eventsLoaded] = @@ -78,8 +81,8 @@ const RecentEvent: React.FC = () => { const memoizedPVCNames = useDeepCompareMemoize(validPVC, true); const ocsEventsFilter = React.useCallback( - () => isPersistentStorageEvent(memoizedPVCNames), - [memoizedPVCNames] + () => isPersistentStorageEvent(memoizedPVCNames, odfNamespace), + [memoizedPVCNames, odfNamespace] ); const eventObject = { diff --git a/packages/ocs/dashboards/persistent-internal/capacity-breakdown-card/capacity-breakdown-card.tsx b/packages/ocs/dashboards/persistent-internal/capacity-breakdown-card/capacity-breakdown-card.tsx index 9016d6a89..28652125a 100644 --- a/packages/ocs/dashboards/persistent-internal/capacity-breakdown-card/capacity-breakdown-card.tsx +++ b/packages/ocs/dashboards/persistent-internal/capacity-breakdown-card/capacity-breakdown-card.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { namespaceResource } from '@odf/core/resources'; import { BreakdownCardBody } from '@odf/shared/dashboards/breakdown-card/breakdown-body'; import { getSelectOptions } from '@odf/shared/dashboards/breakdown-card/breakdown-dropdown'; @@ -134,6 +135,8 @@ const BreakdownCard: React.FC = () => { pvcNamespace ); + const { odfNamespace } = useODFNamespaceSelector(); + const [modelByUsed, modelUsedError, modelUsedLoading] = useCustomPrometheusPoll({ query: queries[modelByUsedQueryMap[metricType]], @@ -224,6 +227,7 @@ const BreakdownCard: React.FC = () => { metricModel={model} humanize={humanize} isPersistentInternal={true} + odfNamespace={odfNamespace} /> {metricType === BreakdownCardFieldsWithParams.PVCS && !queriesLoadError && diff --git a/packages/ocs/dashboards/persistent-internal/details-card.tsx b/packages/ocs/dashboards/persistent-internal/details-card.tsx index e634b49df..e257287ad 100644 --- a/packages/ocs/dashboards/persistent-internal/details-card.tsx +++ b/packages/ocs/dashboards/persistent-internal/details-card.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; +import { useSafeK8sList } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { getOperatorVersion } from '@odf/core/utils'; -import { CEPH_STORAGE_NAMESPACE, ODF_OPERATOR } from '@odf/shared/constants'; +import { ODF_OPERATOR } from '@odf/shared/constants'; import { useK8sGet } from '@odf/shared/hooks/k8s-get-hook'; import { useFetchCsv } from '@odf/shared/hooks/use-fetch-csv'; -import { useK8sList } from '@odf/shared/hooks/useK8sList'; import { ClusterServiceVersionModel, InfrastructureModel, @@ -19,21 +20,24 @@ import { DetailsBody } from '@openshift-console/dynamic-plugin-sdk-internal'; import { OverviewDetailItem as DetailItem } from '@openshift-console/plugin-shared'; import { Link } from 'react-router-dom'; import { Card, CardBody, CardHeader, CardTitle } from '@patternfly/react-core'; -import { CEPH_NS } from '../../constants'; import { StorageClusterModel } from '../../models'; import { getNetworkEncryption } from '../../utils'; const DetailsCard: React.FC = () => { const { t } = useCustomTranslation(); + + const { odfNamespace, isNsSafe } = useODFNamespaceSelector(); + const [infrastructure, infrastructureLoaded, infrastructureError] = useK8sGet(InfrastructureModel, 'cluster'); - const [ocsData, ocsLoaded, ocsError] = useK8sList( + const [ocsData, ocsLoaded, ocsError] = useSafeK8sList( StorageClusterModel, - CEPH_NS + odfNamespace ); const [csv, csvLoaded, csvError] = useFetchCsv({ specName: ODF_OPERATOR, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, + startPollingInstantly: isNsSafe, }); const infrastructurePlatform = getInfrastructurePlatform(infrastructure); const cluster: StorageClusterKind = ocsData?.find( @@ -48,7 +52,7 @@ const DetailsCard: React.FC = () => { const servicePath = `${resourcePathFromModel( ClusterServiceVersionModel, getName(csv), - CEPH_NS + odfNamespace )}`; const serviceName = t('Data Foundation'); return ( @@ -59,7 +63,7 @@ const DetailsCard: React.FC = () => { - {csvLoaded && !csvError ? ( + {isNsSafe && csvLoaded && !csvError ? ( {serviceName} @@ -70,7 +74,7 @@ const DetailsCard: React.FC = () => { {ocsName} @@ -96,7 +100,7 @@ const DetailsCard: React.FC = () => { key="inTransitEncryption" title={t('In-transit encryption')} isLoading={!ocsLoaded} - error={ocsError} + error={ocsError as any} > {inTransitEncryptionStatus} diff --git a/packages/ocs/dashboards/persistent-internal/inventory-card.tsx b/packages/ocs/dashboards/persistent-internal/inventory-card.tsx index 067ad5e9e..aa8d8882e 100644 --- a/packages/ocs/dashboards/persistent-internal/inventory-card.tsx +++ b/packages/ocs/dashboards/persistent-internal/inventory-card.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import { cephStorageLabel } from '@odf/core/constants'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { NodeModel, PersistentVolumeClaimModel, @@ -17,7 +19,6 @@ import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { useK8sWatchResources } from '@openshift-console/dynamic-plugin-sdk'; import { ResourceInventoryItem } from '@openshift-console/dynamic-plugin-sdk-internal'; import { Card, CardBody, CardHeader, CardTitle } from '@patternfly/react-core'; -import { cephStorageLabel } from '../../constants'; import { getCephNodes, getCephPVCs, @@ -46,6 +47,8 @@ const watchResources = { const InventoryCard: React.FC = () => { const { t } = useCustomTranslation(); + const { odfNamespace } = useODFNamespaceSelector(); + const resources = useK8sWatchResources(watchResources); const nodesLoaded = resources?.nodes?.loaded; @@ -76,7 +79,7 @@ const InventoryCard: React.FC = () => { isLoading={!nodesLoaded} error={!!nodesLoadError} kind={NodeModel as any} - resources={getCephNodes(nodesData)} + resources={getCephNodes(nodesData, odfNamespace)} mapper={getNodeStatusGroups} basePath={ocsNodesHref} /> diff --git a/packages/ocs/dashboards/persistent-internal/status-card/status-card.tsx b/packages/ocs/dashboards/persistent-internal/status-card/status-card.tsx index 8d8ecf1e2..b066f8c32 100644 --- a/packages/ocs/dashboards/persistent-internal/status-card/status-card.tsx +++ b/packages/ocs/dashboards/persistent-internal/status-card/status-card.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { ODF_MANAGED_FLAG } from '@odf/core/features'; import { healthStateMapping } from '@odf/shared/dashboards/status-card/states'; import { useCustomPrometheusPoll, @@ -14,7 +13,6 @@ import { Alert, HealthState, useK8sWatchResource, - useFlag, } from '@openshift-console/dynamic-plugin-sdk'; import { AlertItem, @@ -63,7 +61,6 @@ export const CephAlerts: React.FC = () => { const [alerts, loaded, error] = useAlerts(); const filteredAlerts = loaded && !error && !_.isEmpty(alerts) ? filterCephAlerts(alerts) : []; - const isOdfManaged = useFlag(ODF_MANAGED_FLAG); return ( @@ -73,10 +70,7 @@ export const CephAlerts: React.FC = () => { ))} diff --git a/packages/ocs/modals/block-pool/create-block-pool-modal.tsx b/packages/ocs/modals/block-pool/create-block-pool-modal.tsx index 808cf86c7..627623274 100644 --- a/packages/ocs/modals/block-pool/create-block-pool-modal.tsx +++ b/packages/ocs/modals/block-pool/create-block-pool-modal.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE, ONE_SECOND } from '@odf/shared/constants'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; +import { K8sResourceObj } from '@odf/core/types'; +import { ONE_SECOND } from '@odf/shared/constants'; import { ModalTitle, ModalFooter } from '@odf/shared/generic/ModalTitle'; import { HandlePromiseProps, @@ -9,11 +12,7 @@ import { ModalBody } from '@odf/shared/modals/Modal'; import { CephClusterKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { referenceForModel } from '@odf/shared/utils'; -import { - k8sCreate, - useK8sWatchResource, - WatchK8sResource, -} from '@openshift-console/dynamic-plugin-sdk'; +import { k8sCreate } from '@openshift-console/dynamic-plugin-sdk'; import { ModalComponent } from '@openshift-console/dynamic-plugin-sdk/lib/app/modal-support/ModalProvider'; import { Modal } from '@patternfly/react-core'; import { BlockPoolStatus, BlockPoolBody } from '../../block-pool/body'; @@ -34,6 +33,8 @@ export const CreateBlockPoolModal = withHandlePromise( const { cephClusters, onPoolCreation, handlePromise, errorMessage } = props; const { t } = useCustomTranslation(); + const { odfNamespace } = useODFNamespaceSelector(); + const [state, dispatch] = React.useReducer( blockPoolReducer, blockPoolInitialState @@ -47,18 +48,19 @@ export const CreateBlockPoolModal = withHandlePromise( const MODAL_TITLE = t('Create BlockPool'); // Watch newly created pool after submit - const poolResource: WatchK8sResource = React.useMemo(() => { - return { + const poolResource: K8sResourceObj = React.useCallback( + (ns) => ({ kind: referenceForModel(CephBlockPoolModel), namespaced: true, isList: false, name: state.poolName, - namespace: CEPH_STORAGE_NAMESPACE, - }; - }, [state.poolName]); + namespace: ns, + }), + [state.poolName] + ); const [newPool, newPoolLoaded, newPoolLoadError] = - useK8sWatchResource(poolResource); + useSafeK8sWatchResource(poolResource); React.useEffect(() => { if (isSubmit) { @@ -115,7 +117,7 @@ export const CreateBlockPoolModal = withHandlePromise( type: BlockPoolActionType.SET_POOL_STATUS, payload: POOL_PROGRESS.PROGRESS, }); - const poolObj: StoragePoolKind = getPoolKindObj(state); + const poolObj: StoragePoolKind = getPoolKindObj(state, odfNamespace); handlePromise( k8sCreate({ model: CephBlockPoolModel, data: poolObj }), diff --git a/packages/ocs/modals/block-pool/modal-footer.tsx b/packages/ocs/modals/block-pool/modal-footer.tsx index d64997826..ca745c2c9 100644 --- a/packages/ocs/modals/block-pool/modal-footer.tsx +++ b/packages/ocs/modals/block-pool/modal-footer.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { TFunction } from 'i18next'; import { useHistory } from 'react-router'; import { @@ -15,14 +14,12 @@ import { BlockPoolActionType, BlockPoolState, } from '../../block-pool/reducer'; -import { OCS_POOL_MANAGEMENT, POOL_PROGRESS } from '../../constants'; +import { POOL_PROGRESS } from '../../constants'; export const BlockPoolModalFooter = (props: BlockPoolModalFooterProps) => { const { state, dispatch, onSubmit, primaryAction, cancel, close } = props; const { t } = useCustomTranslation(); - const isPoolManagementSupported = useFlag(OCS_POOL_MANAGEMENT); - const handleFinishButton = (e: React.FormEvent) => { e.preventDefault(); close(); @@ -148,8 +145,7 @@ export const BlockPoolModalFooter = (props: BlockPoolModalFooterProps) => { checkRequiredValues( state.poolName, state.replicaSize, - state.volumeType, - isPoolManagementSupported + state.volumeType ), }, ], diff --git a/packages/ocs/storage-class/mutators.ts b/packages/ocs/storage-class/mutators.ts index 2fc5848e6..cf4691c35 100644 --- a/packages/ocs/storage-class/mutators.ts +++ b/packages/ocs/storage-class/mutators.ts @@ -1,5 +1,26 @@ +import { + CLUSTER_ID, + PROV_SECRET_NS, + NODE_SECRET_NS, + CONTROLLER_SECRET_NS, + CEPH_NS_SESSION_STORAGE, +} from '@odf/ocs/constants'; import { StorageClass } from '@odf/shared/types'; +export const addClusterParams = (sc: StorageClass): StorageClass => { + const cephClusterNamespace = sessionStorage.getItem(CEPH_NS_SESSION_STORAGE); + sessionStorage.removeItem(CEPH_NS_SESSION_STORAGE); + sc.parameters = { + ...sc.parameters, + [CLUSTER_ID]: cephClusterNamespace, + [PROV_SECRET_NS]: cephClusterNamespace, + [NODE_SECRET_NS]: cephClusterNamespace, + [CONTROLLER_SECRET_NS]: cephClusterNamespace, + }; + + return sc; +}; + export const addKubevirtAnnotations = (sc: StorageClass): StorageClass => { if ( sc?.parameters?.hasOwnProperty('encrypted') && @@ -10,5 +31,6 @@ export const addKubevirtAnnotations = (sc: StorageClass): StorageClass => { 'cdi.kubevirt.io/clone-strategy': 'copy', }; } - return sc; + + return addClusterParams(sc); }; diff --git a/packages/ocs/storage-class/sc-form.tsx b/packages/ocs/storage-class/sc-form.tsx index 5cebcb600..1c9084e44 100644 --- a/packages/ocs/storage-class/sc-form.tsx +++ b/packages/ocs/storage-class/sc-form.tsx @@ -16,7 +16,9 @@ import { SupportedProviders, DescriptionKey, } from '@odf/core/constants'; -import { OCS_INDEPENDENT_FLAG, FEATURES } from '@odf/core/features'; +import { OCS_INDEPENDENT_FLAG } from '@odf/core/features'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { cephBlockPoolResource, cephClusterResource, @@ -25,8 +27,8 @@ import { ProviderNames, KmsCsiConfigKeysMapping, KMSConfigMap, + K8sResourceObj, } from '@odf/core/types'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { ButtonBar } from '@odf/shared/generic/ButtonBar'; import { StatusBox } from '@odf/shared/generic/status-box'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; @@ -47,7 +49,6 @@ import { import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { getInfrastructurePlatform } from '@odf/shared/utils'; import { - WatchK8sResource, ProvisionerProps, useFlag, useK8sWatchResource, @@ -74,6 +75,7 @@ import { CEPH_INTERNAL_CR_NAME, CLUSTER_STATUS, POOL_STATE, + CEPH_NS_SESSION_STORAGE, } from '../constants'; import { CreateBlockPoolModal } from '../modals/block-pool/create-block-pool-modal'; import { StoragePoolKind } from '../types'; @@ -108,6 +110,11 @@ export const CephFsNameComponent: React.FC = ({ } }, [sc, scLoaded, scLoadError, parameterKey]); + // ToDo (epic 4422): Need to pass the namespace where ceph cluster is deployed (remove from here, add dropdown) + React.useEffect(() => { + sessionStorage.setItem(CEPH_NS_SESSION_STORAGE, 'openshift-storage'); + }, []); + if (scLoaded && !scLoadError) { return (

@@ -143,7 +150,9 @@ export const PoolResourceComponent: React.FC = ({ const launchModal = useModal(); - const [poolData, poolDataLoaded, poolDataLoadError] = useK8sWatchResource< + const { isODFNsLoaded, odfNsLoadError, isNsSafe } = useODFNamespaceSelector(); + + const [poolData, poolDataLoaded, poolDataLoadError] = useSafeK8sWatchResource< StoragePoolKind[] >(cephBlockPoolResource); @@ -216,10 +225,15 @@ export const PoolResourceComponent: React.FC = ({ ] ); - if (cephClusters[0]?.metadata.name === CEPH_INTERNAL_CR_NAME) { + // ToDo (epic 4422): Need to pass the namespace where ceph cluster is deployed (remove from here, add dropdown) + React.useEffect(() => { + sessionStorage.setItem(CEPH_NS_SESSION_STORAGE, 'openshift-storage'); + }, []); + + if (isNsSafe && cephClusters[0]?.metadata.name === CEPH_INTERNAL_CR_NAME) { return ( <> - {!poolDataLoadError && cephClusters && ( + {!poolDataLoadError && !odfNsLoadError && cephClusters && (
)} - {(poolDataLoadError || cephClusterLoadError) && ( + {(poolDataLoadError || cephClusterLoadError || odfNsLoadError) && ( = ({ ); } - if (cephClusters[0]?.metadata.name === CEPH_EXTERNAL_CR_NAME) { + if (isNsSafe && cephClusters[0]?.metadata.name === CEPH_EXTERNAL_CR_NAME) { return (
- history.goBack()} - onNext={({ id }) => { - setCurrentStep(currentStep + 1); - const idIndexPlusOne = StepPositionMap[id]; - const newStepHigherBound = - stepsReached < idIndexPlusOne ? idIndexPlusOne : stepsReached; - setStepsReached(newStepHigherBound); - }} - onBack={() => { - setCurrentStep(currentStep - 1); - }} - onGoToStep={(newStep) => { - setCurrentStep(StepPositionMap[newStep.id]); - }} - /> + + history.goBack()} + onNext={({ id }) => { + setCurrentStep(currentStep + 1); + const idIndexPlusOne = StepPositionMap[id]; + const newStepHigherBound = + stepsReached < idIndexPlusOne ? idIndexPlusOne : stepsReached; + setStepsReached(newStepHigherBound); + }} + onBack={() => { + setCurrentStep(currentStep - 1); + }} + onGoToStep={(newStep) => { + setCurrentStep(StepPositionMap[newStep.id]); + }} + /> +
); diff --git a/packages/odf/components/bucket-class/state.ts b/packages/odf/components/bucket-class/state.ts index 9a65234f3..0f92cdeb5 100644 --- a/packages/odf/components/bucket-class/state.ts +++ b/packages/odf/components/bucket-class/state.ts @@ -7,7 +7,6 @@ import { } from '../../types'; export const initialState = { - namespace: 'openshift-storage', bucketClassName: '', bucketClassType: BucketClassType.STANDARD, namespacePolicyType: NamespacePolicyType.SINGLE, @@ -27,7 +26,6 @@ export const initialState = { }; export type State = { - namespace: string; bucketClassName: string; description: string; bucketClassType: BucketClassType; @@ -47,7 +45,6 @@ export type State = { }; export type Action = - | { type: 'setNamespace'; name: string } | { type: 'setBucketClassName'; name: string } | { type: 'setBucketClassType'; value: BucketClassType } | { type: 'setNamespacePolicyType'; value: NamespacePolicyType } @@ -67,8 +64,6 @@ export type Action = export const reducer = (state: State, action: Action) => { switch (action.type) { - case 'setNamespace': - return Object.assign({}, state, { namespace: action.name }); case 'setBucketClassName': return Object.assign({}, state, { bucketClassName: action.name }); case 'setBucketClassType': diff --git a/packages/odf/components/bucket-class/wizard-pages/general-page.tsx b/packages/odf/components/bucket-class/wizard-pages/general-page.tsx index 5c607926b..f645f9219 100644 --- a/packages/odf/components/bucket-class/wizard-pages/general-page.tsx +++ b/packages/odf/components/bucket-class/wizard-pages/general-page.tsx @@ -9,7 +9,6 @@ import { getName } from '@odf/shared/selectors'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { isValidIP } from '@odf/shared/utils'; import { useYupValidationResolver } from '@odf/shared/yup-validation-resolver'; -import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { TFunction } from 'i18next'; import { useForm } from 'react-hook-form'; import * as Yup from 'yup'; @@ -21,7 +20,6 @@ import { Radio, TextArea, } from '@patternfly/react-core'; -import { FEATURES } from '../../../features'; import { NooBaaBucketClassModel } from '../../../models'; import { BucketClassKind, BucketClassType } from '../../../types'; import validationRegEx from '../../../utils/validation'; @@ -57,8 +55,6 @@ const GeneralPage: React.FC = ({ const [showHelp, setShowHelp] = React.useState(true); - const isNamespaceStoreSupported = useFlag(FEATURES.OCS_NAMESPACE_STORE); - const [data, loaded, loadError] = useK8sList( NooBaaBucketClassModel, namespace @@ -147,35 +143,32 @@ const GeneralPage: React.FC = ({
)}
- {isNamespaceStoreSupported && ( - - {bucketClassTypeRadios(t).map((radio) => { - const checked = radio.value === state.bucketClassType; - return ( - { - dispatch({ - type: 'setBucketClassType', - value: radio.value, - }); - }} - checked={checked} - className="nb-create-bc-step-page-form__radio" - name="bucketclasstype" - /> - ); - })} - - )} - + + {bucketClassTypeRadios(t).map((radio) => { + const checked = radio.value === state.bucketClassType; + return ( + { + dispatch({ + type: 'setBucketClassType', + value: radio.value, + }); + }} + checked={checked} + className="nb-create-bc-step-page-form__radio" + name="bucketclasstype" + /> + ); + })} + = ({ ceph }) => { @@ -20,7 +18,6 @@ export const StoragePopover: React.FC = ({ ceph }) => { const health = getCephHealthState({ ceph }, t); const value = health.message || healthStateMessage(health.state, t); - const isOdfManaged = useFlag(ODF_MANAGED_FLAG); const operatorName = t('Data Foundation'); return ( @@ -36,11 +33,7 @@ export const StoragePopover: React.FC = ({ ceph }) => { secondColumn={t('Health')} >
- {!isOdfManaged ? ( - {operatorName} - ) : ( - operatorName - )} + {operatorName}
diff --git a/packages/odf/components/create-bs/backing-store-dropdown.tsx b/packages/odf/components/create-bs/backing-store-dropdown.tsx index f410f7217..386f2a25a 100644 --- a/packages/odf/components/create-bs/backing-store-dropdown.tsx +++ b/packages/odf/components/create-bs/backing-store-dropdown.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { backingStoreResource } from '@odf/core/resources'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { referenceForModel } from '@odf/shared/utils'; -import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; import { Alert, Dropdown, @@ -11,15 +10,8 @@ import { DropdownSeparator, DropdownToggle, } from '@patternfly/react-core'; -import { NooBaaBackingStoreModel } from '../../models'; import { BackingStoreKind } from '../../types'; -export const backingStoreResource = { - kind: referenceForModel(NooBaaBackingStoreModel), - isList: true, - namespace: CEPH_STORAGE_NAMESPACE, -}; - export const BackingStoreDropdown: React.FC = ({ id, onChange, @@ -32,7 +24,7 @@ export const BackingStoreDropdown: React.FC = ({ const [isOpen, setOpen] = React.useState(false); const [nbsData, , nbsLoadErr] = - useK8sWatchResource(backingStoreResource); + useSafeK8sWatchResource(backingStoreResource); const noobaaBackingStores: BackingStoreKind[] = useDeepCompareMemoize( nbsData, true diff --git a/packages/odf/components/create-bs/create-bs.tsx b/packages/odf/components/create-bs/create-bs.tsx index 63609131b..27b8f1bba 100644 --- a/packages/odf/components/create-bs/create-bs.tsx +++ b/packages/odf/components/create-bs/create-bs.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import NamespaceSafetyBox from '@odf/core/components/utils/safety-box'; +import { useSafeK8sList } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { fieldRequirementsTranslations, formSettings, @@ -7,7 +9,6 @@ import { import StaticDropdown from '@odf/shared/dropdown/StaticDropdown'; import { FormGroupController } from '@odf/shared/form-group-controller'; import { ButtonBar } from '@odf/shared/generic/ButtonBar'; -import { useK8sList } from '@odf/shared/hooks/useK8sList'; import { TextInputWithFieldRequirements } from '@odf/shared/input-with-requirements'; import { SecretModel } from '@odf/shared/models'; import { getName } from '@odf/shared/selectors'; @@ -59,6 +60,8 @@ const CreateBackingStoreForm: React.FC = ( initialState ); + const { odfNamespace } = useODFNamespaceSelector(); + const [inProgress, setProgress] = React.useState(false); const [error, setError] = React.useState(''); const [showSecret, setShowSecret] = React.useState(true); @@ -67,18 +70,12 @@ const CreateBackingStoreForm: React.FC = ( const history = useHistory(); - const { - className, - isPage, - appName, - namespace = CEPH_STORAGE_NAMESPACE, - onCancel, - onClose, - } = props; + const { className, isPage, appName, namespace, onCancel, onClose } = props; + const ns = namespace || odfNamespace; - const [data, loaded, loadError] = useK8sList( + const [data, loaded, loadError] = useSafeK8sList( NooBaaBackingStoreModel, - namespace + ns ); const { schema, fieldRequirements } = React.useMemo(() => { @@ -142,7 +139,7 @@ const CreateBackingStoreForm: React.FC = ( const { secretKey, accessKey, gcpJSON } = providerDataState; const secretPayload = secretPayloadCreator( provider, - namespace, + ns, secretName, accessKey || gcpJSON, secretKey @@ -155,7 +152,7 @@ const CreateBackingStoreForm: React.FC = ( apiVersion: getAPIVersionForModel(NooBaaBackingStoreModel), kind: NooBaaBackingStoreModel.kind, metadata: { - namespace, + namespace: ns, name: bsName, }, spec: { @@ -181,7 +178,7 @@ const CreateBackingStoreForm: React.FC = ( [BUCKET_LABEL_NOOBAA_MAP[provider]]: providerDataState.target, secret: { name: secretName, - namespace, + namespace: ns, }, }, }; @@ -220,7 +217,7 @@ const CreateBackingStoreForm: React.FC = ( isODF ? history.push(`/odf/resource/${resourcePath}`) : history.push( - `/k8s/ns/${namespace}/clusterserviceversions/${appName}/${resourcePath}` + `/k8s/ns/${ns}/clusterserviceversions/${appName}/${resourcePath}` ); else onClose(); }) @@ -231,103 +228,106 @@ const CreateBackingStoreForm: React.FC = ( }; return ( - - - - ( - - )} - /> - {provider === BC_PROVIDERS.GCP && ( - + + - )} - {(provider === BC_PROVIDERS.AWS || - provider === BC_PROVIDERS.S3 || - provider === BC_PROVIDERS.IBM || - provider === BC_PROVIDERS.AZURE) && ( - - )} - {provider === BC_PROVIDERS.PVC && ( - - )} - {!isValid && isSubmitted && ( - ( + + )} /> - )} - - - - - - - + {provider === BC_PROVIDERS.GCP && ( + + )} + {(provider === BC_PROVIDERS.AWS || + provider === BC_PROVIDERS.S3 || + provider === BC_PROVIDERS.IBM || + provider === BC_PROVIDERS.AZURE) && ( + + )} + {provider === BC_PROVIDERS.PVC && ( + + )} + {!isValid && isSubmitted && ( + + )} + + + + + + + + ); }; diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx index 91ca11a25..2c46d1dd5 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/backing-storage-step/backing-storage-step.tsx @@ -3,12 +3,13 @@ import { STORAGE_CLUSTER_SYSTEM_KIND, NO_PROVISIONER, } from '@odf/core/constants'; +import { useSafeK8sGet } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { scResource } from '@odf/core/resources'; import { BackingStorageType, DeploymentType } from '@odf/core/types'; import { getSupportedVendors } from '@odf/core/utils'; import { getStorageClassDescription } from '@odf/core/utils'; import { StorageClassWizardStepExtensionProps as ExternalStorage } from '@odf/odf-plugin-sdk/extensions'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import ResourceDropdown from '@odf/shared/dropdown/ResourceDropdown'; import { useK8sGet } from '@odf/shared/hooks/k8s-get-hook'; import { @@ -191,11 +192,15 @@ export const BackingStorage: React.FC = ({ } = state; const { t } = useCustomTranslation(); + + const { odfNamespace, isODFNsLoaded, odfNsLoadError } = + useODFNamespaceSelector(); + const [sc, scLoaded, scLoadError] = useK8sGet>(StorageClassModel); - const [csvList, csvListLoaded, csvListLoadError] = useK8sGet< + const [csvList, csvListLoaded, csvListLoadError] = useSafeK8sGet< ListKind - >(ClusterServiceVersionModel, null, CEPH_STORAGE_NAMESPACE); + >(ClusterServiceVersionModel, null, odfNamespace); const formattedSS: StorageSystemSet = formatStorageSystemList(storageSystems); const hasOCS: boolean = formattedSS.has(STORAGE_CLUSTER_SYSTEM_KIND); @@ -276,8 +281,8 @@ export const BackingStorage: React.FC = ({ return (
{!hasOCS && ( diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx index 1b0d0bb1f..59a9030ac 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/capacity-and-nodes-step/capacity-and-nodes-step.tsx @@ -19,7 +19,6 @@ import { attachDevices, attachDevicesWithArbiter, } from '@odf/core/constants'; -import { FEATURES } from '@odf/core/features'; import { pvResource, nodeResource } from '@odf/core/resources'; import { NodesPerZoneMap } from '@odf/core/types'; import { getSCAvailablePVs, getAssociatedNodes } from '@odf/core/utils'; @@ -29,10 +28,7 @@ import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; import { K8sResourceKind, NodeKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { humanizeBinaryBytes } from '@odf/shared/utils'; -import { - useK8sWatchResource, - useFlag, -} from '@openshift-console/dynamic-plugin-sdk'; +import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; import { Trans } from 'react-i18next'; import { Checkbox, @@ -46,6 +42,7 @@ import { TextContent, TextInput, } from '@patternfly/react-core'; +import { useODFNamespaceSelector } from '../../../../redux'; import { ValidationMessage } from '../../../utils/common-odf-install-el'; import { ErrorHandler } from '../../error-handler'; import { WizardDispatch, WizardNodeState, WizardState } from '../../reducer'; @@ -57,7 +54,10 @@ import './capacity-and-nodes.scss'; const SelectNodesText: React.FC = React.memo( ({ text }) => { const { t } = useCustomTranslation(); - const label = 'cluster.ocs.openshift.io/openshift-storage=""'; + + const { odfNamespace } = useODFNamespaceSelector(); + + const label = `cluster.ocs.openshift.io/${odfNamespace}=""`; return ( {text} @@ -82,9 +82,8 @@ const EnableTaintNodes: React.FC = ({ enableTaint, }) => { const { t } = useCustomTranslation(); - const isTaintSupported = useFlag(FEATURES.OCS_TAINT_NODES); - return isTaintSupported ? ( + return ( = ({ }) } /> - ) : ( - <> ); }; diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/create-local-volume-set-step/create-local-volume-set-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/create-local-volume-set-step/create-local-volume-set-step.tsx index c6b7c44bf..becbe77bc 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/create-local-volume-set-step/create-local-volume-set-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/create-local-volume-set-step/create-local-volume-set-step.tsx @@ -33,7 +33,6 @@ import { k8sCreate, WatchK8sResource, useK8sWatchResource, - useFlag, } from '@openshift-console/dynamic-plugin-sdk'; import { TFunction } from 'i18next'; import { Trans } from 'react-i18next'; @@ -48,7 +47,6 @@ import { WizardContext, WizardContextType, } from '@patternfly/react-core'; -import { FEATURES } from '../../../../features'; import { ErrorHandler } from '../../error-handler'; import { WizardDispatch, WizardNodeState, WizardState } from '../../reducer'; import { LocalVolumeSetBody } from './body'; @@ -156,7 +154,6 @@ const ConfirmationModal: React.FC = ({ nodes, }) => { const { t } = useCustomTranslation(); - const isArbiterSupported = useFlag(FEATURES.OCS_ARBITER); const { onNext, activeStep } = React.useContext(WizardContext); @@ -195,12 +192,10 @@ const ConfirmationModal: React.FC = ({ {t("After the LocalVolumeSet is created you won't be able to edit it.")} - {isArbiterSupported && ( -

- {t('Note:')} - {arbiterText(t)} -

- )} +

+ {t('Note:')} + {arbiterText(t)} +

); return ( diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/create-storage-class-step/create-storage-class-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/create-storage-class-step/create-storage-class-step.tsx index 07fdbcac0..ff7d7f1ed 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/create-storage-class-step/create-storage-class-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/create-storage-class-step/create-storage-class-step.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; import { getExternalStorage } from '@odf/core/components/utils'; +import { useSafeK8sList } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { IBMFlashSystemModel } from '@odf/ibm/system-models'; import { IBMFlashSystemKind } from '@odf/ibm/system-types'; import { @@ -12,7 +14,6 @@ import { ExternalStateValues, } from '@odf/odf-plugin-sdk/extensions'; import { - CEPH_STORAGE_NAMESPACE, fieldRequirementsTranslations, formSettings, } from '@odf/shared/constants'; @@ -40,6 +41,8 @@ export const CreateStorageClass: React.FC = ({ }) => { const { t } = useCustomTranslation(); + const { odfNamespace } = useODFNamespaceSelector(); + const { component: Component, displayName } = getExternalStorage( externalStorage, supportedExternalStorage @@ -63,10 +66,8 @@ export const CreateStorageClass: React.FC = ({ const [scData, scLoaded, scLoadError] = useK8sList(StorageClassModel); - const [secretData, secretLoaded, secretLoadError] = useK8sList( - SecretModel, - CEPH_STORAGE_NAMESPACE - ); + const [secretData, secretLoaded, secretLoadError] = + useSafeK8sList(SecretModel, odfNamespace); const [flashSystemData, flashSystemLoaded, flashSystemLoadError] = useK8sList(IBMFlashSystemModel); @@ -87,7 +88,7 @@ export const CreateStorageClass: React.FC = ({ const secret = secretData?.find( (secret) => secret.metadata.name === secretName && - secret.metadata.namespace === CEPH_STORAGE_NAMESPACE + secret.metadata.namespace === odfNamespace ); return atob(getSecretManagementAddress(secret)); }); @@ -144,6 +145,7 @@ export const CreateStorageClass: React.FC = ({ dataLoadError, flashSystemData, secretData, + odfNamespace, ]); const resolver = useYupValidationResolver(schema); const { diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx index 4a3c39be9..1f450f8ea 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/review-and-create-step/review-and-create-step.tsx @@ -14,7 +14,6 @@ import { BackingStorageType, DeploymentType } from '@odf/core/types'; import { StorageClassWizardStepExtensionProps as ExternalStorage } from '@odf/odf-plugin-sdk/extensions'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { humanizeBinaryBytes } from '@odf/shared/utils'; -import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; import { TextContent, @@ -23,7 +22,6 @@ import { List, ListItem, } from '@patternfly/react-core'; -import { FEATURES } from '../../../../features'; import { WizardState } from '../../reducer'; import './review-and-create-step.scss'; @@ -42,8 +40,6 @@ export const ReviewAndCreate: React.FC = ({ supportedExternalStorage, }) => { const { t } = useCustomTranslation(); - const isMultusSupported = useFlag(FEATURES.OCS_MULTUS); - const isTaintSupported = useFlag(FEATURES.OCS_TAINT_NODES); const { storageClass, @@ -187,13 +183,11 @@ export const ReviewAndCreate: React.FC = ({ })} )} - {isTaintSupported && ( - - {t('Taint nodes: {{ocsTaintsStatus}}', { - ocsTaintsStatus, - })} - - )} + + {t('Taint nodes: {{ocsTaintsStatus}}', { + ocsTaintsStatus, + })} + {t('Replica-1 pool: {{singleReplicaPoolStatus}}', { singleReplicaPoolStatus, @@ -229,13 +223,11 @@ export const ReviewAndCreate: React.FC = ({ })} )} - {isMultusSupported && ( - - {t('Network: {{networkType}}', { - networkType: NetworkTypeLabels[networkType], - })} - - )} + + {t('Network: {{networkType}}', { + networkType: NetworkTypeLabels[networkType], + })} + ))} {isRhcs && ( diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx index 7c861c7d5..59a1ec0eb 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/configure.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; +import { useSafeK8sWatchResources } from '@odf/core/hooks'; import { NetworkAttachmentDefinitionModel } from '@odf/core/models'; import TechPreviewBadge from '@odf/shared/badges/TechPreviewBadge'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { SingleSelectDropdown } from '@odf/shared/dropdown/singleselectdropdown'; import { FieldLevelHelp } from '@odf/shared/generic/FieldLevelHelp'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; @@ -12,7 +12,6 @@ import { referenceForModel } from '@odf/shared/utils'; import { ResourceIcon, WatchK8sResults, - useK8sWatchResources, } from '@openshift-console/dynamic-plugin-sdk'; import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk-internal/lib/extensions/console-types'; import * as _ from 'lodash-es'; @@ -20,11 +19,11 @@ import { FormGroup, Radio, SelectOption } from '@patternfly/react-core'; import { NetworkType, NADSelectorType } from '../../../../types'; import './configure.scss'; -const resources = { +const resources = (ns: string) => ({ openshift: { isList: true, kind: referenceForModel(NetworkAttachmentDefinitionModel), - namespace: CEPH_STORAGE_NAMESPACE, + namespace: ns, namespaced: true, }, default: { @@ -39,7 +38,7 @@ const resources = { namespace: 'openshift-multus', namespaced: true, }, -}; +}); type MultusWatchResourcesObject = { multus: NetworkAttachmentDefinitionKind[]; @@ -86,8 +85,7 @@ export const MultusDropdown: React.FC = ({ const clusterNetworkUID = getUID(clusterNetwork); const publicNetworkUID = getUID(publicNetwork); - const networkResources = - useK8sWatchResources(resources); + const networkResources = useSafeK8sWatchResources(resources); const networkDevices: K8sResourceCommon[] = React.useMemo(() => { const { loaded: resourcesLoaded, error: resourcesLoadError } = diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx index 70ddd3b00..d9b7baaee 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/encryption.tsx @@ -2,10 +2,8 @@ import * as React from 'react'; import { AdvancedSubscription } from '@odf/shared/badges/advanced-subscription'; import { FieldLevelHelp } from '@odf/shared/generic/FieldLevelHelp'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { Checkbox, FormGroup, Form } from '@patternfly/react-core'; import { KMSEmptyState } from '../../../../constants'; -import { FEATURES } from '../../../../features'; import { KMSConfigure } from '../../../kms-config/kms-config'; import { ValidationMessage, @@ -158,7 +156,6 @@ export const Encryption: React.FC = ({ isExternal, }) => { const { t } = useCustomTranslation(); - const isKmsSupported = useFlag(FEATURES.OCS_KMS); const [encryptionChecked, setEncryptionChecked] = React.useState( encryption.clusterWide || encryption.storageClass ); @@ -251,7 +248,6 @@ export const Encryption: React.FC = ({ description={description} onChange={handleEncryptionOnChange} body={ - isKmsSupported && (isMCG || encryptionChecked) && ( <> {!isMCG && ( diff --git a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx index 74660cee1..ae56519ae 100644 --- a/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx +++ b/packages/odf/components/create-storage-system/create-storage-system-steps/security-and-network-step/security-and-network-step.tsx @@ -1,9 +1,7 @@ import * as React from 'react'; import { StorageClassWizardStepExtensionProps as ExternalStorage } from '@odf/odf-plugin-sdk/extensions'; -import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk-internal/lib/extensions/console-types'; import { Form } from '@patternfly/react-core'; -import { FEATURES } from '../../../../features'; import { NetworkType, NADSelectorType } from '../../../../types'; import { WizardDispatch, WizardState } from '../../reducer'; import { ConnectionDetails } from '../connection-details-step'; @@ -19,8 +17,6 @@ export const SecurityAndNetwork: React.FC = ({ externalStorage, supportedExternalStorage, }) => { - const isMultusSupported = useFlag(FEATURES.OCS_MULTUS); - const { networkType: nwType, clusterNetwork, @@ -61,7 +57,7 @@ export const SecurityAndNetwork: React.FC = ({ infraType={infraType} isExternal={isExternal} /> - {!isExternal && isMultusSupported && ( + {!isExternal && ( > = ({ formState, }) => { const { t } = useCustomTranslation(); + + const { odfNamespace, isNsSafe } = useODFNamespaceSelector(); + const [pods, podsLoaded, podsLoadError] = useK8sGet>(PodModel); const [csv, csvLoaded, csvLoadError] = useFetchCsv({ specName: OCS_OPERATOR, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, + startPollingInstantly: isNsSafe, }); const { fileName, fileData, errorMessage, isLoading } = formState; @@ -160,6 +164,7 @@ export const rhcsPayload: CreatePayload = ({ systemName, state, model, + namespace, inTransitStatus, }) => { return [ @@ -170,7 +175,7 @@ export const rhcsPayload: CreatePayload = ({ kind: SecretModel.kind, metadata: { name: 'rook-ceph-external-cluster-details', - namespace: CEPH_STORAGE_NAMESPACE, + namespace: namespace, }, stringData: { external_cluster_details: state.fileData, @@ -186,7 +191,7 @@ export const rhcsPayload: CreatePayload = ({ kind: model.kind, metadata: { name: systemName, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: namespace, }, spec: { network: { diff --git a/packages/odf/components/create-storage-system/footer.tsx b/packages/odf/components/create-storage-system/footer.tsx index f23df74a0..897fdd87b 100644 --- a/packages/odf/components/create-storage-system/footer.tsx +++ b/packages/odf/components/create-storage-system/footer.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { StorageClassWizardStepExtensionProps as ExternalStorage } from '@odf/odf-plugin-sdk/extensions'; import { OCSStorageClusterModel } from '@odf/shared/models'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; @@ -125,7 +126,8 @@ const handleReviewAndCreateNext = async ( hasOCS: boolean, handleError: (err: string, showError: boolean) => void, history: RouteComponentProps['history'], - supportedExternalStorage: ExternalStorage[] + supportedExternalStorage: ExternalStorage[], + odfNamespace: string ) => { const { connectionDetails, @@ -141,28 +143,38 @@ const handleReviewAndCreateNext = async ( const isMCG: boolean = deployment === DeploymentType.MCG; try { - await labelOCSNamespace(); + await labelOCSNamespace(odfNamespace); if (isMCG) { if (encryption.advanced) await Promise.all( - createClusterKmsResources(kms.providerState, kms.provider, isMCG) + createClusterKmsResources( + kms.providerState, + odfNamespace, + kms.provider, + isMCG + ) ); - await createStorageCluster(state); + await createStorageCluster(state, odfNamespace); } else if ( type === BackingStorageType.EXISTING || type === BackingStorageType.LOCAL_DEVICES ) { - await labelNodes(nodes); + await labelNodes(nodes, odfNamespace); if (capacityAndNodes.enableTaint) await taintNodes(nodes); if (encryption.advanced) await Promise.all( - createClusterKmsResources(kms.providerState, kms.provider) + createClusterKmsResources( + kms.providerState, + odfNamespace, + kms.provider + ) ); await createStorageSystem( OCS_INTERNAL_CR_NAME, - STORAGE_CLUSTER_SYSTEM_KIND + STORAGE_CLUSTER_SYSTEM_KIND, + odfNamespace ); - await createStorageCluster(state); + await createStorageCluster(state, odfNamespace); } else if (type === BackingStorageType.EXTERNAL) { const { createPayload, model, displayName, waitToCreate } = getExternalStorage(externalStorage, supportedExternalStorage) || {}; @@ -180,19 +192,24 @@ const handleReviewAndCreateNext = async ( systemName: subSystemName, state: subSystemState, model, + namespace: odfNamespace, storageClassName: storageClass.name, inTransitStatus: inTransitChecked, }); - await createStorageSystem(subSystemName, subSystemKind); + await createStorageSystem(subSystemName, subSystemKind, odfNamespace); if (!hasOCS && !isRhcs) { - await labelNodes(nodes); + await labelNodes(nodes, odfNamespace); if (capacityAndNodes.enableTaint) await taintNodes(nodes); if (encryption.advanced) await Promise.all( - createClusterKmsResources(kms.providerState, kms.provider) + createClusterKmsResources( + kms.providerState, + odfNamespace, + kms.provider + ) ); - await createStorageCluster(state); + await createStorageCluster(state, odfNamespace); } if (!isRhcs && !!waitToCreate) await waitToCreate(model); await createExternalSubSystem(subSystemPayloads); @@ -215,6 +232,9 @@ export const CreateStorageSystemFooter: React.FC const { t } = useCustomTranslation(); const { activeStep, onNext, onBack } = React.useContext(WizardContext); + + const { odfNamespace, isNsSafe } = useODFNamespaceSelector(); + const [requestInProgress, setRequestInProgress] = React.useState(false); const [requestError, setRequestError] = React.useState(''); const [showErrorAlert, setShowErrorAlert] = React.useState(false); @@ -258,7 +278,8 @@ export const CreateStorageSystemFooter: React.FC hasOCS, handleError, history, - supportedExternalStorage + supportedExternalStorage, + odfNamespace ); setRequestInProgress(false); break; @@ -285,7 +306,9 @@ export const CreateStorageSystemFooter: React.FC - - - - + {!isValid && isSubmitted && ( + + )} + + + + + + + +
); diff --git a/packages/odf/components/namespace-store/create-namespace-store.spec.tsx b/packages/odf/components/namespace-store/create-namespace-store.spec.tsx index 67c0259e0..cf1c9bc1a 100644 --- a/packages/odf/components/namespace-store/create-namespace-store.spec.tsx +++ b/packages/odf/components/namespace-store/create-namespace-store.spec.tsx @@ -1,18 +1,31 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { render } from '@testing-library/react'; import CreateNamespaceStore from './create-namespace-store'; +const odfNamespace = 'test-ns-1'; const params = { ns: 'test-ns', }; const mockNamespaceStoreForm = jest.fn(); +const MockNamespaceStoreForm = (): React.ReactElement => { + return <>; +}; jest.mock('./namespace-store-form', () => (props) => { mockNamespaceStoreForm(props); - return ; + return ; }); +jest.mock('@odf/core/redux', () => ({ + useODFNamespaceSelector: () => ({ + odfNamespace, + isODFNsLoaded: true, + odfNsLoadError: null, + isNsSafe: true, + isFallbackSafe: true, + }), +})); + describe('CreateNamespaceStore test', () => { it('shows the correct heading texts', () => { const { container } = render(); @@ -41,7 +54,7 @@ describe('CreateNamespaceStore test', () => { render(); expect(mockNamespaceStoreForm).toHaveBeenCalledWith( expect.objectContaining({ - namespace: 'test-ns', + namespace: params.ns, }) ); }); @@ -50,7 +63,7 @@ describe('CreateNamespaceStore test', () => { render(); expect(mockNamespaceStoreForm).toHaveBeenCalledWith( expect.objectContaining({ - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, }) ); }); diff --git a/packages/odf/components/namespace-store/create-namespace-store.tsx b/packages/odf/components/namespace-store/create-namespace-store.tsx index 58b84ff26..b8c092133 100644 --- a/packages/odf/components/namespace-store/create-namespace-store.tsx +++ b/packages/odf/components/namespace-store/create-namespace-store.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { getName } from '@odf/shared/selectors'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { referenceForModel } from '@odf/shared/utils'; @@ -14,7 +14,11 @@ const CreateNamespaceStore: React.FC = ({ match, }) => { const { t } = useCustomTranslation(); - const { ns = CEPH_STORAGE_NAMESPACE } = match.params; + + const { odfNamespace } = useODFNamespaceSelector(); + + const { ns } = match.params; + const namespace = ns || odfNamespace; const history = useHistory(); const onCancel = () => history.goBack(); @@ -44,7 +48,7 @@ const CreateNamespaceStore: React.FC = ({ )}/${getName(resources[lastIndex])}`; history.push(`/odf/resource/${resourcePath}`); }} - namespace={ns} + namespace={namespace} className="nb-endpoints-page-form__short" /> diff --git a/packages/odf/components/namespace-store/namespace-store-dropdown.tsx b/packages/odf/components/namespace-store/namespace-store-dropdown.tsx index 6ea428993..e4f4fb45f 100644 --- a/packages/odf/components/namespace-store/namespace-store-dropdown.tsx +++ b/packages/odf/components/namespace-store/namespace-store-dropdown.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; import { getName } from '@odf/shared/selectors'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; import { Alert, Dropdown, @@ -29,8 +29,12 @@ export const NamespaceStoreDropdown: React.FC = ({ const [isOpen, setOpen] = React.useState(false); const [dropdownItems, setDropdownItems] = React.useState([]); - const [nnsData, , nnsLoadErr] = useK8sWatchResource( - namespaceStoreResource + // Operator install namespace is determined using Subscriptions, which non-admin can not access (yet). + // Using "true" in "useSafeK8sWatchResource" so that they can default to "openshift-storage" (if case of access issues), + // which is current use case as well (as we do not officially support UI if ODF is installed in any other Namespace). + const [nnsData, , nnsLoadErr] = useSafeK8sWatchResource( + namespaceStoreResource, + true ); const noobaaNamespaceStores: NamespaceStoreKind[] = useDeepCompareMemoize( nnsData, diff --git a/packages/odf/components/namespace-store/namespace-store-form.spec.tsx b/packages/odf/components/namespace-store/namespace-store-form.spec.tsx index 608b24b0d..40d47bce9 100644 --- a/packages/odf/components/namespace-store/namespace-store-form.spec.tsx +++ b/packages/odf/components/namespace-store/namespace-store-form.spec.tsx @@ -25,8 +25,36 @@ jest.mock('@odf/shared/hooks/useK8sList', () => ({ ], })); +jest.mock('@odf/core/hooks', () => ({ + __esModule: true, + useSafeK8sList: () => [ + [ + { + metadata: { + name: 'existing-ns-name', + }, + }, + ], + true, + undefined, + ], +})); + +jest.mock('@odf/core/redux', () => ({ + useODFNamespaceSelector: () => ({ + odfNamespace: 'test-ns', + isODFNsLoaded: true, + odfNsLoadError: null, + isNsSafe: true, + isFallbackSafe: true, + }), +})); + +const MockResourceDropdown = (): React.ReactElement => { + return <>; +}; jest.mock('@odf/shared/dropdown/ResourceDropdown', () => () => { - return ; + return ; }); describe('NamespaceStoreForm', () => { diff --git a/packages/odf/components/namespace-store/namespace-store-form.tsx b/packages/odf/components/namespace-store/namespace-store-form.tsx index 7ff32c006..4c79e719c 100644 --- a/packages/odf/components/namespace-store/namespace-store-form.tsx +++ b/packages/odf/components/namespace-store/namespace-store-form.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE, formSettings } from '@odf/shared/constants'; +import NamespaceSafetyBox from '@odf/core/components/utils/safety-box'; +import { useSafeK8sList } from '@odf/core/hooks'; +import { formSettings } from '@odf/shared/constants'; import { fieldRequirementsTranslations } from '@odf/shared/constants'; import ResourceDropdown from '@odf/shared/dropdown/ResourceDropdown'; import StaticDropdown from '@odf/shared/dropdown/StaticDropdown'; import { FormGroupController } from '@odf/shared/form-group-controller'; import { ButtonBar } from '@odf/shared/generic/ButtonBar'; -import { useK8sList } from '@odf/shared/hooks/useK8sList'; import { TextInputWithFieldRequirements } from '@odf/shared/input-with-requirements'; import { PersistentVolumeClaimModel, SecretModel } from '@odf/shared/models'; import { getName } from '@odf/shared/selectors'; @@ -125,7 +126,7 @@ const NamespaceStoreForm: React.FC = (props) => { const { onCancel, className, redirectHandler, namespace } = props; - const [data, loaded, loadError] = useK8sList( + const [data, loaded, loadError] = useSafeK8sList( NooBaaNamespaceStoreModel, namespace ); @@ -262,146 +263,152 @@ const NamespaceStoreForm: React.FC = (props) => { }; return ( -
- - - ( - + + - {(provider === BC_PROVIDERS.AWS || - provider === BC_PROVIDERS.S3 || - provider === BC_PROVIDERS.IBM || - provider === BC_PROVIDERS.AZURE) && ( - + + + ( + + )} /> - )} - {provider === BC_PROVIDERS.FILESYSTEM && ( - <> - ( - - id="pvc-name" - resourceModel={PersistentVolumeClaimModel} - resource={{ - kind: PersistentVolumeClaimModel.kind, - isList: true, - namespace, - }} - onSelect={onChange} - onBlur={onBlur} - filterResource={(pvcObj: PersistentVolumeClaimKind) => - pvcObj?.status?.phase === 'Bound' && - pvcObj?.spec?.accessModes.some( - (mode) => mode === 'ReadWriteMany' - ) - } - /> - )} + type={StoreType.NS} + provider={provider} + namespace={namespace} + state={providerDataState} + dispatch={providerDataDispatch} /> - ( - - )} + )} + {provider === BC_PROVIDERS.FILESYSTEM && ( + <> + ( + + id="pvc-name" + resourceModel={PersistentVolumeClaimModel} + resource={{ + kind: PersistentVolumeClaimModel.kind, + isList: true, + namespace, + }} + onSelect={onChange} + onBlur={onBlur} + filterResource={(pvcObj: PersistentVolumeClaimKind) => + pvcObj?.status?.phase === 'Bound' && + pvcObj?.spec?.accessModes.some( + (mode) => mode === 'ReadWriteMany' + ) + } + /> + )} + /> + ( + + )} + /> + + )} + {!isValid && isSubmitted && ( + - - )} - {!isValid && isSubmitted && ( - - )} - - - - - - - + )} + + + + + + + + ); }; diff --git a/packages/odf/components/namespace-store/namespace-store-modal.tsx b/packages/odf/components/namespace-store/namespace-store-modal.tsx index 30412ccf2..160a61390 100644 --- a/packages/odf/components/namespace-store/namespace-store-modal.tsx +++ b/packages/odf/components/namespace-store/namespace-store-modal.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { CommonModalProps } from '@odf/shared/modals/common'; import { ModalBody } from '@odf/shared/modals/Modal'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; @@ -11,6 +11,8 @@ const NamespaceStoreModal: React.FC = (props) => { const { t } = useCustomTranslation(); const { isOpen, closeModal } = props; + const { odfNamespace } = useODFNamespaceSelector(); + return ( = (props) => { )}

diff --git a/packages/odf/components/namespace-store/namespace-store-table.tsx b/packages/odf/components/namespace-store/namespace-store-table.tsx index 9d9347032..d19d0a830 100644 --- a/packages/odf/components/namespace-store/namespace-store-table.tsx +++ b/packages/odf/components/namespace-store/namespace-store-table.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; import { useSelectList } from '@odf/shared/hooks/select-list'; import { getName, getNamespace } from '@odf/shared/selectors'; @@ -12,7 +13,6 @@ import { RowProps, TableColumn, TableData, - useK8sWatchResource, useListPageFilter, VirtualizedTable, } from '@openshift-console/dynamic-plugin-sdk'; @@ -140,13 +140,15 @@ const NamespaceStoreListWrapper: React.FC = ({ onSelectNamespaceStore, preSelected, }) => { - const [nsObjects, loaded, loadError] = useK8sWatchResource< + const { isODFNsLoaded, odfNsLoadError } = useODFNamespaceSelector(); + + const [nsObjects, loaded, loadError] = useSafeK8sWatchResource< NamespaceStoreKind[] - >({ + >((ns: string) => ({ kind: referenceForModel(NooBaaNamespaceStoreModel), isList: true, - namespace: CEPH_STORAGE_NAMESPACE, - }); + namespace: ns, + })); const memoizedResources = useDeepCompareMemoize(nsObjects, true); const [data, filteredData, onFilterChange] = @@ -162,15 +164,15 @@ const NamespaceStoreListWrapper: React.FC = ({ diff --git a/packages/odf/components/object-service-nav-item/object-service.tsx b/packages/odf/components/object-service-nav-item/object-service.tsx index 14ef69331..f0c6a57e8 100644 --- a/packages/odf/components/object-service-nav-item/object-service.tsx +++ b/packages/odf/components/object-service-nav-item/object-service.tsx @@ -12,6 +12,7 @@ import { StatusBox } from '@odf/shared/generic/status-box'; import PageHeading from '@odf/shared/heading/page-heading'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { referenceForModel } from '@odf/shared/utils'; +// import { useODFNamespaceSelector } from '@odf/core/redux'; import { HorizontalNav, useResolvedExtensions, @@ -37,6 +38,8 @@ const ObjectServicePage: React.FC = () => { const { t } = useCustomTranslation(); const title = t('Object Storage'); + // const { isODFNsLoaded, odfNsLoadError, isNsSafe } = useODFNamespaceSelector(); + const [extensions, isLoaded, error] = useResolvedExtensions( isObjectServiceTab as ExtensionTypeGuard ); diff --git a/packages/odf/components/odf-dashboard/dashboard.tsx b/packages/odf/components/odf-dashboard/dashboard.tsx index 92bea3361..f4827a170 100644 --- a/packages/odf/components/odf-dashboard/dashboard.tsx +++ b/packages/odf/components/odf-dashboard/dashboard.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { HorizontalNavTab, isHorizontalNavTab, @@ -95,6 +96,8 @@ const ODFDashboardPage: React.FC = (props) => { [t] ); + const { isODFNsLoaded, odfNsLoadError } = useODFNamespaceSelector(); + const [extensions, isLoaded, error] = useResolvedExtensions( isDashboardTab as ExtensionTypeGuard ); @@ -125,8 +128,8 @@ const ODFDashboardPage: React.FC = (props) => { {/** Todo(bipuladh): Move to usage of common PF Tabs component */} ({ kind: referenceForModel(ODFStorageSystem), - namespace: 'openshift-storage', + namespace: ns, isList: true, -}; +}); const nameSort = (a: RowProps, b: RowProps, c: SortByDirection) => { const negation = c !== SortByDirection.asc; @@ -149,7 +149,7 @@ const PerformanceCard: React.FC = () => { [t] ); - const [systems, systemLoaded, systemLoadError] = useK8sWatchResource< + const [systems, systemLoaded, systemLoadError] = useSafeK8sWatchResource< StorageSystemKind[] >(storageSystemResource); const { duration } = useUtilizationDuration(); diff --git a/packages/odf/components/odf-dashboard/queries.ts b/packages/odf/components/odf-dashboard/queries.ts index 14db297b3..584e844c9 100644 --- a/packages/odf/components/odf-dashboard/queries.ts +++ b/packages/odf/components/odf-dashboard/queries.ts @@ -27,6 +27,7 @@ export const UTILIZATION_QUERY = { [StorageDashboard.THROUGHPUT]: 'odf_system_throughput_total_bytes', }; +// ToDo (epic 4422): Need to update as per updates in the metrics export const STATUS_QUERIES = { [StorageDashboard.HEALTH]: `(label_replace(odf_system_map{target_namespace="openshift-storage"} , "managedBy", "$1", "target_name", "(.*)")) * on (namespace, managedBy) group_right(storage_system) ${SYSTEM_HEALTH_METRIC}`, }; diff --git a/packages/odf/components/odf-dashboard/status-card/status-card.tsx b/packages/odf/components/odf-dashboard/status-card/status-card.tsx index 27d27b6c1..ec19d0578 100644 --- a/packages/odf/components/odf-dashboard/status-card/status-card.tsx +++ b/packages/odf/components/odf-dashboard/status-card/status-card.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { K8sResourceObj } from '@odf/core/types'; import { useGetOCSHealth } from '@odf/ocs/hooks'; import { ODF_OPERATOR } from '@odf/shared/constants'; import HealthItem from '@odf/shared/dashboards/status-card/HealthItem'; @@ -19,11 +21,7 @@ import { referenceForGroupVersionKind, getOperatorHealthState, } from '@odf/shared/utils'; -import { - HealthState, - WatchK8sResource, -} from '@openshift-console/dynamic-plugin-sdk'; -import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; +import { HealthState } from '@openshift-console/dynamic-plugin-sdk'; import { HealthBody } from '@openshift-console/dynamic-plugin-sdk-internal'; import { Gallery, @@ -39,23 +37,23 @@ import { StorageDashboard, STATUS_QUERIES } from '../queries'; import StorageSystemPopup from './storage-system-popup'; import './status-card.scss'; -const operatorResource: WatchK8sResource = { +const operatorResource: K8sResourceObj = (ns) => ({ kind: 'operators.coreos.com~v1alpha1~ClusterServiceVersion', - namespace: 'openshift-storage', + namespace: ns, isList: true, -}; +}); -const storageSystemResource: WatchK8sResource = { +const storageSystemResource: K8sResourceObj = (ns) => ({ kind: referenceForModel(ODFStorageSystem), - namespace: 'openshift-storage', + namespace: ns, isList: true, -}; +}); export const StatusCard: React.FC = () => { const { t } = useCustomTranslation(); const [csvData, csvLoaded, csvLoadError] = - useK8sWatchResource(operatorResource); - const [systems, systemsLoaded, systemsLoadError] = useK8sWatchResource< + useSafeK8sWatchResource(operatorResource); + const [systems, systemsLoaded, systemsLoadError] = useSafeK8sWatchResource< StorageSystemKind[] >(storageSystemResource); diff --git a/packages/odf/components/odf-dashboard/system-capacity-card/capacity-card.tsx b/packages/odf/components/odf-dashboard/system-capacity-card/capacity-card.tsx index 380ef44ea..f5e861b76 100644 --- a/packages/odf/components/odf-dashboard/system-capacity-card/capacity-card.tsx +++ b/packages/odf/components/odf-dashboard/system-capacity-card/capacity-card.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { K8sResourceObj } from '@odf/core/types'; import CapacityCard, { CapacityMetricDatum, } from '@odf/shared/dashboards/capacity-card/capacity-card'; @@ -16,27 +18,23 @@ import { referenceFor, referenceForModel, } from '@odf/shared/utils'; -import { - PrometheusResponse, - WatchK8sResource, -} from '@openshift-console/dynamic-plugin-sdk'; -import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; +import { PrometheusResponse } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; import { Card, CardBody, CardHeader, CardTitle } from '@patternfly/react-core'; import { storageCapacityTooltip } from '../../../constants'; import { StorageDashboard, CAPACITY_QUERIES } from '../queries'; -const storageSystemResource: WatchK8sResource = { +const storageSystemResource: K8sResourceObj = (ns) => ({ kind: referenceForModel(ODFStorageSystem), - namespace: 'openshift-storage', + namespace: ns, isList: true, -}; +}); -const storageClusterResource: WatchK8sResource = { +const storageClusterResource: K8sResourceObj = (ns) => ({ kind: referenceForModel(OCSStorageClusterModel), - namespace: 'openshift-storage', + namespace: ns, isList: true, -}; +}); const getMetricForSystem = ( metric: PrometheusResponse, @@ -48,12 +46,12 @@ const getMetricForSystem = ( const SystemCapacityCard: React.FC = () => { const { t } = useCustomTranslation(); - const [systems, systemsLoaded, systemsLoadError] = useK8sWatchResource< + const [systems, systemsLoaded, systemsLoadError] = useSafeK8sWatchResource< StorageSystemKind[] >(storageSystemResource); const [storageClusters, storageClustersLoaded, storageClustersLoadError] = - useK8sWatchResource(storageClusterResource); + useSafeK8sWatchResource(storageClusterResource); const [usedCapacity, errorUsedCapacity, loadingUsedCapacity] = useCustomPrometheusPoll({ diff --git a/packages/odf/components/resource-pages/BackingStoreDetailsPage.tsx b/packages/odf/components/resource-pages/BackingStoreDetailsPage.tsx index 83e23e3f3..98588896e 100644 --- a/packages/odf/components/resource-pages/BackingStoreDetailsPage.tsx +++ b/packages/odf/components/resource-pages/BackingStoreDetailsPage.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import DetailsPage from '@odf/shared/details-page/DetailsPage'; import { SectionHeading } from '@odf/shared/heading/page-heading'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; @@ -8,7 +9,6 @@ import { K8sResourceKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { referenceForModel } from '@odf/shared/utils'; import { EventStreamWrapped, YAMLEditorWrapped } from '@odf/shared/utils/Tabs'; -import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; import { RouteComponentProps } from 'react-router'; import { NooBaaBackingStoreModel } from '../../models'; import { BackingStoreKind } from '../../types'; @@ -42,12 +42,16 @@ const BackingStoreDetailsPage: React.FC = ({ }) => { const { t } = useCustomTranslation(); const { resourceName: name } = match.params; - const [resource, loaded, loadError] = useK8sWatchResource({ - kind: referenceForModel(NooBaaBackingStoreModel), - name, - namespace: CEPH_STORAGE_NAMESPACE, - isList: false, - }); + + const { isODFNsLoaded, odfNsLoadError } = useODFNamespaceSelector(); + + const [resource, loaded, loadError] = + useSafeK8sWatchResource((ns: string) => ({ + kind: referenceForModel(NooBaaBackingStoreModel), + name, + namespace: ns, + isList: false, + })); const breadcrumbs = [ { @@ -81,8 +85,8 @@ const BackingStoreDetailsPage: React.FC = ({ return ( <> = ({ }) => { const { t } = useCustomTranslation(); const { resourceName: name } = match.params; - const [resource, loaded, loadError] = useK8sWatchResource({ - kind: referenceForModel(NooBaaBucketClassModel), - name, - namespace: CEPH_STORAGE_NAMESPACE, - isList: false, - }); + + const { isODFNsLoaded, odfNsLoadError } = useODFNamespaceSelector(); + + const [resource, loaded, loadError] = + useSafeK8sWatchResource((ns: string) => ({ + kind: referenceForModel(NooBaaBucketClassModel), + name, + namespace: ns, + isList: false, + })); const breadcrumbs = [ { @@ -127,8 +131,8 @@ const BucketClassDetailsPage: React.FC = ({ return ( <> = ({ }) => { const { t } = useCustomTranslation(); const { resourceName: name } = match.params; - const [resource, loaded, loadError] = useK8sWatchResource( - { + + const { isODFNsLoaded, odfNsLoadError } = useODFNamespaceSelector(); + + const [resource, loaded, loadError] = + useSafeK8sWatchResource((ns: string) => ({ kind: referenceForModel(NooBaaNamespaceStoreModel), name, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: ns, isList: false, - } - ); + })); const breadcrumbs = [ { @@ -82,8 +84,8 @@ const NamespaceStoreDetailsPage: React.FC = ({ return ( <> = ({ kebabActions, }) => { const { t } = useCustomTranslation(); - const resource = React.useMemo( - () => ({ - kind: referenceForModel(resourceModel), - namespace: CEPH_STORAGE_NAMESPACE, - isList: true, - }), - [resourceModel] - ); - const [resources, loaded, loadError] = - useK8sWatchResource(resource); + const { isODFNsLoaded, odfNsLoadError } = useODFNamespaceSelector(); + + const [resources, loaded, loadError] = useSafeK8sWatchResource< + K8sResourceCommon[] + >((ns: string) => ({ + kind: referenceForModel(resourceModel), + namespace: ns, + isList: true, + })); const [data, filteredData, onFilterChange] = useListPageFilter(resources); @@ -207,15 +206,15 @@ const GenericListPage: React.FC = ({ diff --git a/packages/odf/components/system-list/odf-system-list.tsx b/packages/odf/components/system-list/odf-system-list.tsx index c0c4c7c94..c0d2e2cc6 100644 --- a/packages/odf/components/system-list/odf-system-list.tsx +++ b/packages/odf/components/system-list/odf-system-list.tsx @@ -41,6 +41,7 @@ import classNames from 'classnames'; import * as _ from 'lodash-es'; import { sortable, wrappable } from '@patternfly/react-table'; import { ODF_QUERIES, ODFQueries } from '../../queries'; +import { useODFNamespaceSelector } from '../../redux'; import { OperandStatus } from '../utils'; import ODFSystemLink from './system-link'; @@ -316,6 +317,9 @@ export const StorageSystemListPage: React.FC = ({ }) => { const { t } = useCustomTranslation(); + const { odfNamespace, isODFNsLoaded, odfNsLoadError } = + useODFNamespaceSelector(); + const [storageSystems, loaded, loadError] = useK8sWatchResource< StorageSystemKind[] >({ @@ -369,7 +373,7 @@ export const StorageSystemListPage: React.FC = ({ ?.metadata?.name : null; - const createLink = `/k8s/ns/openshift-storage/operators.coreos.com~v1alpha1~ClusterServiceVersion/${odfCsvName}/odf.openshift.io~v1alpha1~StorageSystem/~new`; + const createLink = `/k8s/ns/${odfNamespace}/operators.coreos.com~v1alpha1~ClusterServiceVersion/${odfCsvName}/odf.openshift.io~v1alpha1~StorageSystem/~new`; const normalizedMetrics = React.useMemo( () => ({ @@ -388,7 +392,7 @@ export const StorageSystemListPage: React.FC = ({ return ( <> - {odfCsvName && ( + {odfCsvName && odfNamespace && ( {t('Create StorageSystem')} @@ -397,15 +401,15 @@ export const StorageSystemListPage: React.FC = ({ diff --git a/packages/odf/components/topology/Topology.tsx b/packages/odf/components/topology/Topology.tsx index 928072583..0fa661b12 100644 --- a/packages/odf/components/topology/Topology.tsx +++ b/packages/odf/components/topology/Topology.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { useSafeK8sWatchResource } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import HandleErrorAndLoading from '@odf/shared/error-handler/ErrorStateHandler'; import { useDeepCompareMemoize } from '@odf/shared/hooks/deep-compare-memoize'; import { @@ -53,7 +54,7 @@ import { NodeShape, GraphElement, } from '@patternfly/react-topology'; -import { CEPH_STORAGE_LABEL } from '../../constants'; +import { cephStorageLabel } from '../../constants'; import { CEPH_FLAG, MCG_STANDALONE, @@ -433,6 +434,9 @@ const TopologyViewComponent: React.FC = () => { const Error = ({ error }) => <>{error}; const Topology: React.FC = () => { + const { odfNamespace, isODFNsLoaded, odfNsLoadError } = + useODFNamespaceSelector(); + const [controller, setController] = React.useState(null); const [visualizationLevel, setVisualizationLevel] = React.useState(TopologyViewLevel.NODES); @@ -449,22 +453,23 @@ const Topology: React.FC = () => { useK8sWatchResource(storageClusterResource); const [deployments, deploymentsLoaded, deploymentsError] = - useK8sWatchResource(odfDeploymentsResource); + useSafeK8sWatchResource(odfDeploymentsResource); const [pods, podsLoaded, podsError] = - useK8sWatchResource(odfPodsResource); + useSafeK8sWatchResource(odfPodsResource); const [statefulSets, statefulSetLoaded, statefulSetError] = - useK8sWatchResource(odfStatefulSetResource); + useSafeK8sWatchResource(odfStatefulSetResource); const [replicaSets, replicaSetsLoaded, replicaSetsError] = - useK8sWatchResource(odfReplicaSetResource); + useSafeK8sWatchResource(odfReplicaSetResource); const [daemonSets, daemonSetsLoaded, daemonSetError] = - useK8sWatchResource(odfDaemonSetResource); + useSafeK8sWatchResource(odfDaemonSetResource); + const storageLabel = cephStorageLabel(odfNamespace); const odfNodes = nodes.filter((node) => - _.has(node.metadata.labels, CEPH_STORAGE_LABEL) + _.has(node.metadata.labels, storageLabel) ); const memoizedNodes = useDeepCompareMemoize(odfNodes, true); @@ -527,7 +532,8 @@ const Topology: React.FC = () => { !podsLoaded || !statefulSetLoaded || !replicaSetsLoaded || - !daemonSetsLoaded; + !daemonSetsLoaded || + !isODFNsLoaded; const zones = memoizedNodes.map(getTopologyDomain); @@ -561,7 +567,8 @@ const Topology: React.FC = () => { podsError || replicaSetsError || daemonSetError || - statefulSetError + statefulSetError || + odfNsLoadError } ErrorMessage={Error} > @@ -582,13 +589,16 @@ const TopologyViewErrorMessage: React.FC = ({ isExternalMode, }) => { const { t } = useCustomTranslation(); - const [csv, csvLoaded, csvError] = useK8sWatchResource< + + const { odfNamespace, isNsSafe } = useODFNamespaceSelector(); + + const [csv, csvLoaded, csvError] = useSafeK8sWatchResource< ClusterServiceVersionKind[] - >({ + >((ns: string) => ({ kind: referenceForModel(ClusterServiceVersionModel), isList: true, - namespace: CEPH_STORAGE_NAMESPACE, - }); + namespace: ns, + })); const odfCsvName: string = csvLoaded && !csvError @@ -596,8 +606,9 @@ const TopologyViewErrorMessage: React.FC = ({ ?.metadata?.name : null; - const createLink = `/k8s/ns/openshift-storage/operators.coreos.com~v1alpha1~ClusterServiceVersion/${odfCsvName}/odf.openshift.io~v1alpha1~StorageSystem/~new`; + const createLink = `/k8s/ns/${odfNamespace}/operators.coreos.com~v1alpha1~ClusterServiceVersion/${odfCsvName}/odf.openshift.io~v1alpha1~StorageSystem/~new`; + const showCreateSSOption = !isExternalMode && isNsSafe; return ( @@ -607,9 +618,10 @@ const TopologyViewErrorMessage: React.FC = ({ : t('No StorageCluster found')} - {!isExternalMode && t('Set up a storage cluster to view the topology')} + {showCreateSSOption && + t('Set up a storage cluster to view the topology')} - {!isExternalMode && ( + {showCreateSSOption && ( {t('Create StorageSystem')} )} diff --git a/packages/odf/components/topology/sidebar/TopologySideBarContent.tsx b/packages/odf/components/topology/sidebar/TopologySideBarContent.tsx index 5af430d71..70fb75ab2 100644 --- a/packages/odf/components/topology/sidebar/TopologySideBarContent.tsx +++ b/packages/odf/components/topology/sidebar/TopologySideBarContent.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { StorageClusterModel } from '@odf/ocs/models'; import { DetailsPageTitle } from '@odf/shared/details-page/DetailsPage'; import { LoadingBox } from '@odf/shared/generic/status-box'; @@ -72,6 +73,8 @@ const DetailsTabComponent: React.FC = ({ const ResourceTabComponent: React.FC = ({ resource, }) => { + const { odfNamespace } = useODFNamespaceSelector(); + const kind = React.useMemo(() => resource?.kind, [resource?.kind]); const Component = React.useMemo(() => { switch (kind) { @@ -84,12 +87,14 @@ const ResourceTabComponent: React.FC = ({ } }, [kind]); - return ; + return ; }; const ObserveTabComponent: React.FC = ({ resource, }) => { + const { odfNamespace } = useODFNamespaceSelector(); + const kind = React.useMemo(() => resource?.kind, [resource?.kind]); const Component = React.useMemo(() => { switch (kind) { @@ -102,7 +107,7 @@ const ObserveTabComponent: React.FC = ({ } }, [kind]); - return ; + return ; }; const TopologySideBarContent: React.FC = ({ diff --git a/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterDetails.tsx b/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterDetails.tsx index 1e7a92279..2b8445a7f 100644 --- a/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterDetails.tsx +++ b/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterDetails.tsx @@ -1,18 +1,15 @@ import * as React from 'react'; -import { CEPH_STORAGE_LABEL } from '@odf/core/constants'; +import { cephStorageLabel } from '@odf/core/constants'; import { CEPH_FLAG, OCS_INDEPENDENT_FLAG } from '@odf/core/features'; +import { useSafeK8sList } from '@odf/core/hooks'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { nodeResource } from '@odf/core/resources'; -import { CEPH_NS } from '@odf/ocs/constants'; import { getDataResiliencyState } from '@odf/ocs/dashboards/persistent-internal/status-card/utils'; import { StorageEfficiencyContent } from '@odf/ocs/dashboards/persistent-internal/storage-efficiency-card/storage-efficiency-card'; import { StorageClusterModel } from '@odf/ocs/models'; import { DATA_RESILIENCY_QUERY, StorageDashboardQuery } from '@odf/ocs/queries'; import { getCephNodes, getOperatorVersion } from '@odf/ocs/utils'; -import { - CEPH_STORAGE_NAMESPACE, - DASH, - ODF_OPERATOR, -} from '@odf/shared/constants'; +import { DASH, ODF_OPERATOR } from '@odf/shared/constants'; import { SectionHeading } from '@odf/shared/heading/page-heading'; import { useCustomPrometheusPoll, @@ -20,7 +17,6 @@ import { } from '@odf/shared/hooks/custom-prometheus-poll'; import { useK8sGet } from '@odf/shared/hooks/k8s-get-hook'; import { useFetchCsv } from '@odf/shared/hooks/use-fetch-csv'; -import { useK8sList } from '@odf/shared/hooks/useK8sList'; import { ClusterServiceVersionModel, InfrastructureModel, @@ -59,11 +55,14 @@ export const StorageClusterDetails: React.FC = ({ resource: storageCluster, }) => { const { t } = useCustomTranslation(); + + const { odfNamespace, isNsSafe } = useODFNamespaceSelector(); + const [infrastructure, infrastructureLoaded, infrastructureError] = useK8sGet(InfrastructureModel, 'cluster'); - const [ocsData, ocsLoaded, ocsError] = useK8sList( + const [ocsData, ocsLoaded, ocsError] = useSafeK8sList( StorageClusterModel, - CEPH_NS + odfNamespace ); const cluster = ocsData?.find( (item: StorageClusterKind) => item.status.phase !== 'Ignored' @@ -94,20 +93,22 @@ export const StorageClusterDetails: React.FC = ({ const [csv, csvLoaded, csvError] = useFetchCsv({ specName: ODF_OPERATOR, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, + startPollingInstantly: isNsSafe, }); const serviceVersion = csvLoaded && _.isEmpty(csvError) ? getOperatorVersion(csv) : DASH; const servicePath = `${resourcePathFromModel( ClusterServiceVersionModel, getName(csv), - CEPH_NS + odfNamespace )}`; const serviceName = t('Data Foundation'); const [nodesData, nodesLoaded, nodesLoadError] = useK8sWatchResource(nodeResource); - const ocsNodesHref = `/search?kind=${NodeModel.kind}&q=${CEPH_STORAGE_LABEL}`; + const storageLabel = cephStorageLabel(odfNamespace); + const ocsNodesHref = `/search?kind=${NodeModel.kind}&q=${storageLabel}`; return (
@@ -139,7 +140,7 @@ export const StorageClusterDetails: React.FC = ({ isLoading={!nodesLoaded} error={!!nodesLoadError} kind={NodeModel as any} - resources={getCephNodes(nodesData)} + resources={getCephNodes(nodesData, odfNamespace)} mapper={getNodeStatusGroups} basePath={ocsNodesHref} /> @@ -154,7 +155,7 @@ export const StorageClusterDetails: React.FC = ({
{t('Service name')}
- {csvLoaded && !csvError ? ( + {isNsSafe && csvLoaded && !csvError ? ( {serviceName} diff --git a/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterObserve.tsx b/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterObserve.tsx index 334f93fba..a015b5474 100644 --- a/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterObserve.tsx +++ b/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterObserve.tsx @@ -7,15 +7,17 @@ import { } from '@openshift-console/dynamic-plugin-sdk'; import { CEPH_FLAG, OCS_INDEPENDENT_FLAG } from '../../../../features'; -export const StorageClusterObserve: React.FC<{ resource?: K8sResourceCommon }> = - () => { - const isIndependent = useFlag(OCS_INDEPENDENT_FLAG); - const isCephAvailable = useFlag(CEPH_FLAG); - const isInternal = !isIndependent && isCephAvailable; +export const StorageClusterObserve: React.FC<{ + resource?: K8sResourceCommon; + odfNamespace?: string; +}> = () => { + const isIndependent = useFlag(OCS_INDEPENDENT_FLAG); + const isCephAvailable = useFlag(CEPH_FLAG); + const isInternal = !isIndependent && isCephAvailable; - return isInternal ? ( - - ) : ( - - ); - }; + return isInternal ? ( + + ) : ( + + ); +}; diff --git a/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterResources.tsx b/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterResources.tsx index 97af93995..3cf11cfba 100644 --- a/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterResources.tsx +++ b/packages/odf/components/topology/sidebar/storage-cluster/StorageClusterResources.tsx @@ -1,38 +1,40 @@ import * as React from 'react'; -import { CEPH_STORAGE_LABEL } from '@odf/core/constants'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { cephStorageLabel } from '@odf/core/constants'; +import { useSafeK8sWatchResources } from '@odf/core/hooks'; import { DataUnavailableError } from '@odf/shared/generic/Error'; import { DeploymentModel, NodeModel } from '@odf/shared/models'; import ResourceLink from '@odf/shared/resource-link/resource-link'; import { getUID } from '@odf/shared/selectors'; import { nodeStatus } from '@odf/shared/status/Node'; import { Status } from '@odf/shared/status/Status'; +import { DeploymentKind, NodeKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { resourcePathFromModel } from '@odf/shared/utils'; import { K8sResourceCommon, ResourceStatus, - useK8sWatchResources, } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; -import { DeploymentKind, NodeKind } from 'packages/shared/types'; import { Title } from '@patternfly/react-core'; import '@odf/shared/topology/sidebar/common/resources-tab.scss'; -const watchResources = { - nodes: { - isList: true, - kind: NodeModel.kind, - selector: { - matchLabels: { [CEPH_STORAGE_LABEL]: '' }, +const watchResources = (ns: string) => { + const storageLabel = cephStorageLabel(ns); + return { + nodes: { + isList: true, + kind: NodeModel.kind, + selector: { + matchLabels: { [storageLabel]: '' }, + }, }, - }, - deployments: { - isList: true, - kind: DeploymentModel.kind, - namespace: CEPH_STORAGE_NAMESPACE, - namespaced: true, - }, + deployments: { + isList: true, + kind: DeploymentModel.kind, + namespace: ns, + namespaced: true, + }, + }; }; type NodeOverviewItemProps = { @@ -130,9 +132,10 @@ const DeploymentOverviewList: React.FC = ({ export const StorageClusterResources: React.FC<{ resource?: K8sResourceCommon; + odfNamespace?: string; }> = () => { const { t } = useCustomTranslation(); - const clusterResources = useK8sWatchResources(watchResources); + const clusterResources = useSafeK8sWatchResources(watchResources); const nodes = (clusterResources?.nodes?.data ?? []) as NodeKind[]; const nodesLoaded = clusterResources?.nodes?.loaded; diff --git a/packages/odf/components/utils/common.ts b/packages/odf/components/utils/common.ts index 64bd5e3ce..97038ae53 100644 --- a/packages/odf/components/utils/common.ts +++ b/packages/odf/components/utils/common.ts @@ -14,7 +14,6 @@ import { shouldDeployAsMinimal, } from '@odf/core/utils'; import { StorageClassWizardStepExtensionProps as ExternalStorage } from '@odf/odf-plugin-sdk/extensions'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { getLabel, getName, @@ -368,6 +367,7 @@ type OCSRequestData = { shouldSetCephRBDAsDefault?: boolean; isSingleReplicaPoolEnabled?: boolean; enableRDRPreparation?: boolean; + odfNamespace: string; }; export const getOCSRequestData = ({ @@ -388,6 +388,7 @@ export const getOCSRequestData = ({ shouldSetCephRBDAsDefault, isSingleReplicaPoolEnabled, enableRDRPreparation, + odfNamespace, }: OCSRequestData): StorageClusterKind => { const scName: string = storageClass.name; const isNoProvisioner: boolean = storageClass?.provisioner === NO_PROVISIONER; @@ -405,7 +406,7 @@ export const getOCSRequestData = ({ kind: 'StorageCluster', metadata: { name: OCS_INTERNAL_CR_NAME, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, }, spec: {}, }; diff --git a/packages/odf/components/utils/safety-box.tsx b/packages/odf/components/utils/safety-box.tsx new file mode 100644 index 000000000..9d2ad29b1 --- /dev/null +++ b/packages/odf/components/utils/safety-box.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; +import { StatusBox } from '@odf/shared/generic'; + +type SafetyBoxWrapperProps = { + areResourcesLoaded?: boolean; + resourcesError?: unknown; + allowFallback?: boolean; +}; + +type SafetyBoxProps = { + isAllSafe: boolean; + isAllLoaded: boolean; + anyError: unknown; +}; + +const SafetyBox: React.FC = ({ + children, + isAllSafe, + isAllLoaded, + anyError, +}) => + !isAllSafe ? ( + + ) : ( + <>{children} + ); + +/** + * ODF can be installed in any namespace, so figuring it out and storing it in Redux will be a prerequisite before using any CRUD pages. + * "NamespaceSafetyBox" uses "StatusBox" which will ensure to add loading state (when namespace is being fetched) or reload button (in case of any error). + * Where to use "NamespaceSafetyBox": can be used at root of any CRUD related page, which can be entry component for "Create" or "Edit" pages. + * Where not to use "NamespaceSafetyBox": some of the imported/exposed components (eg: "DetailsPage", "VirtualizedTable" etc) internally add the functionality of "StatusBox", + * those places we can simply pass the required props. + * Also, no need to add it to the child component again, if root already handles this check. + */ +const NamespaceSafetyBox: React.FC = ({ + children, + areResourcesLoaded = true, + resourcesError = null, + allowFallback = false, +}) => { + const { isODFNsLoaded, odfNsLoadError, isNsSafe, isFallbackSafe } = + useODFNamespaceSelector(); + const isAllLoaded = isODFNsLoaded && areResourcesLoaded; + const anyError = odfNsLoadError || resourcesError; + const canUseFallback = allowFallback && isFallbackSafe; + const isAllSafe = + (isNsSafe || canUseFallback) && areResourcesLoaded && !resourcesError; + + return ( + + {children} + + ); +}; + +export default NamespaceSafetyBox; diff --git a/packages/odf/constants/common.ts b/packages/odf/constants/common.ts index f30931cf8..2d22ed935 100644 --- a/packages/odf/constants/common.ts +++ b/packages/odf/constants/common.ts @@ -11,14 +11,15 @@ export const LABEL_OPERATOR = 'In'; export const OCS_SUPPORT_ANNOTATION = 'features.ocs.openshift.io/enabled'; export const OCS_DISABLED_ANNOTATION = 'features.ocs.openshift.io/disabled'; export const ODF_VENDOR_ANNOTATION = 'vendors.odf.openshift.io/kind'; -export const CEPH_STORAGE_LABEL = 'cluster.ocs.openshift.io/openshift-storage'; -export const ODF_MANAGED_LABEL = 'odf-managed-service'; export const OCS_OPERATOR = 'ocs-operator'; export const OCS_DEVICE_SET_FLEXIBLE_REPLICA = 1; export const OCS_DEVICE_SET_MINIMUM_REPLICAS = 3; export const MINIMUM_NODES = 3; export const SECOND = 1000; +export const cephStorageLabel = (ns: string) => + `cluster.ocs.openshift.io/${ns}`; + export enum defaultRequestSize { BAREMETAL = '1', NON_BAREMETAL = '2Ti', diff --git a/packages/odf/constants/mcg.ts b/packages/odf/constants/mcg.ts index cd6d1f69b..98b89ac66 100644 --- a/packages/odf/constants/mcg.ts +++ b/packages/odf/constants/mcg.ts @@ -94,7 +94,7 @@ export enum NamespacePolicyType { CACHE = 'Cache', } -export const RGW_PROVISIONER = 'openshift-storage.ceph.rook.io/bucket'; -export const NOOBAA_PROVISIONER = 'openshift-storage.noobaa.io/obc'; +export const RGW_PROVISIONER = 'ceph.rook.io/bucket'; +export const NOOBAA_PROVISIONER = 'noobaa.io/obc'; export const ATTACH_DEPLOYMENT = 'ATTACH_DEPLOYMENT'; diff --git a/packages/odf/constants/providerSchema.ts b/packages/odf/constants/providerSchema.ts index 9721f9f06..48868869b 100644 --- a/packages/odf/constants/providerSchema.ts +++ b/packages/odf/constants/providerSchema.ts @@ -1,4 +1,4 @@ -import { PersistentVolumeClaimKind } from 'packages/shared/types'; +import { PersistentVolumeClaimKind } from '@odf/shared/types'; import * as Yup from 'yup'; import { BC_PROVIDERS } from './mcg'; diff --git a/packages/odf/features.ts b/packages/odf/features.ts index 45ae7867e..275274d50 100644 --- a/packages/odf/features.ts +++ b/packages/odf/features.ts @@ -1,101 +1,36 @@ -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { ODFStorageSystem, OCSStorageClusterModel, StorageClassModel, - NamespaceModel, CephClusterModel, - ClusterServiceVersionModel, } from '@odf/shared/models'; import { SelfSubjectAccessReviewModel } from '@odf/shared/models'; -import { getAnnotations, getName } from '@odf/shared/selectors'; import { - ListKind, StorageClusterKind, StorageClassResourceKind, - ClusterServiceVersionKind, } from '@odf/shared/types'; import { SetFeatureFlag, - k8sGet, k8sList, k8sCreate, K8sResourceCommon, SelfSubjectAccessReviewKind, } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; -import { - SECOND, - OCS_OPERATOR, - RGW_PROVISIONER, - NOOBAA_PROVISIONER, - ODF_MANAGED_LABEL, - OCS_SUPPORT_ANNOTATION, - OCS_DISABLED_ANNOTATION, -} from './constants'; -import { NooBaaSystemModel } from './models'; +import { SECOND, RGW_PROVISIONER, NOOBAA_PROVISIONER } from './constants'; export const ODF_MODEL_FLAG = 'ODF_MODEL'; // Based on the existence of StorageSystem CRD export const OCS_INDEPENDENT_FLAG = 'OCS_INDEPENDENT'; // Set to "true" if it is external mode StorageCluster export const OCS_CONVERGED_FLAG = 'OCS_CONVERGED'; // Set to "true" if it is internal mode StorageCluster -export const ODF_MANAGED_FLAG = 'ODF_MANAGED'; // Set to "true" if we are using ODF managed services +export const ROSA_FLAG = 'ROSA'; // Set to "true" if we are using ROSA export const RGW_FLAG = 'RGW'; // Based on the existence of StorageClass with RGW provisioner ("openshift-storage.ceph.rook.io/bucket") export const MCG_STANDALONE = 'MCG_STANDALONE'; // Based on the existence of NooBaa only system (no Ceph) -export const MCG_FLAG = 'MCG'; // Based on the existence of NooBaaSystem +export const MCG_FLAG = 'MCG'; // Based on the existence of NooBaa StorageClass (which only gets created if NooBaaSystem is present) export const CEPH_FLAG = 'CEPH'; // Based on the existence of CephCluster export const OCS_FLAG = 'OCS'; // Based on the existence of StorageCluster export const OCS_NFS_ENABLED = 'NFS'; // Based on the enablement of NFS from StorageCluster spec export const ODF_ADMIN = 'ODF_ADMIN'; // Set to "true" if user is an "openshift-storage" admin (access to StorageSystems) -export enum FEATURES { - // Flag names to be prefixed with "OCS_" so as to seperate from console flags - OCS_MULTUS = 'OCS_MULTUS', - OCS_ARBITER = 'OCS_ARBITER', - OCS_KMS = 'OCS_KMS', - OCS_FLEXIBLE_SCALING = 'OCS_FLEXIBLE_SCALING', - OCS_TAINT_NODES = 'OCS_TAINT_NODES', - OCS_THICK_PROVISION = 'OCS_THICK_PROVISION', - OCS_POOL_MANAGEMENT = 'OCS_POOL_MANAGEMENT', - OCS_NAMESPACE_STORE = 'OCS_NAMESPACE_STORE', - ODF_MCG_STANDALONE = 'ODF_MCG_STANDALONE', - ODF_HPCS_KMS = 'ODF_HPCS_KMS', - ODF_VAULT_SA_KMS = 'ODF_VAULT_SA_KMS', - SS_LIST = 'ODF_SS_LIST', - ADD_CAPACITY = 'ODF_ADD_CAPACITY', - ODF_WIZARD = 'ODF_WIZARD', - BLOCK_POOL = 'BLOCK_POOL', - MCG_RESOURCE = 'MCG_RESOURCE', - ODF_DASHBOARD = 'ODF_DASHBOARD', - COMMON_FLAG = 'COMMON_FLAG', - DASHBOARD_RESOURCES = 'ODF_DASHBOARD_RESOURCES', -} - -export const OCS_FEATURE_FLAGS = { - // [flag name]: - [FEATURES.OCS_MULTUS]: 'multus', - [FEATURES.OCS_ARBITER]: 'arbiter', - [FEATURES.OCS_KMS]: 'kms', - [FEATURES.OCS_FLEXIBLE_SCALING]: 'flexible-scaling', - [FEATURES.OCS_TAINT_NODES]: 'taint-nodes', - [FEATURES.OCS_THICK_PROVISION]: 'thick-provision', - [FEATURES.OCS_POOL_MANAGEMENT]: 'pool-management', - [FEATURES.OCS_NAMESPACE_STORE]: 'namespace-store', - [FEATURES.ODF_MCG_STANDALONE]: 'mcg-standalone', - [FEATURES.ODF_HPCS_KMS]: 'hpcs-kms', - [FEATURES.ODF_VAULT_SA_KMS]: 'vault-sa-kms', -}; - -export const ODF_BLOCK_FLAG = { - [FEATURES.SS_LIST]: 'ss-list', - [FEATURES.ADD_CAPACITY]: 'add-capacity', - [FEATURES.ODF_WIZARD]: 'install-wizard', - [FEATURES.BLOCK_POOL]: 'block-pool', - [FEATURES.MCG_RESOURCE]: 'mcg-resource', - [FEATURES.ODF_DASHBOARD]: 'odf-dashboard', - [FEATURES.COMMON_FLAG]: 'common', - [FEATURES.DASHBOARD_RESOURCES]: 'dashboard-resources', -}; - // Check the user's access to some resources. const ssarChecks = [ { @@ -104,7 +39,6 @@ const ssarChecks = [ group: ODFStorageSystem.apiGroup, resource: ODFStorageSystem.plural, verb: 'list', - namespace: CEPH_STORAGE_NAMESPACE, }, }, ]; @@ -130,7 +64,7 @@ export const setOCSFlags = async (setFlag: SetFeatureFlag) => { const storageClusters: StorageClusterKind[] = (await k8sList({ model: OCSStorageClusterModel, - queryParams: { CEPH_STORAGE_NAMESPACE }, + queryParams: { ns: null }, requestInit: null, })) as StorageClusterKind[]; if (storageClusters?.length > 0) { @@ -196,11 +130,11 @@ export const detectRGW: FeatureDetector = async (setFlag: SetFeatureFlag) => { const logicHandler = () => k8sList({ model: StorageClassModel, queryParams: { ns: null } }) .then((data: StorageClassResourceKind[]) => { - const isRGWPresent = data.some( - (sc) => sc.provisioner === RGW_PROVISIONER + const isRGWPresent = data.some((sc) => + sc.provisioner?.endsWith(RGW_PROVISIONER) ); - const isNooBaaPresent = data.some( - (sc) => sc.provisioner === NOOBAA_PROVISIONER + const isNooBaaPresent = data.some((sc) => + sc.provisioner?.endsWith(NOOBAA_PROVISIONER) ); if (isRGWPresent) { setFlag(RGW_FLAG, true); @@ -234,22 +168,9 @@ export const detectRGW: FeatureDetector = async (setFlag: SetFeatureFlag) => { id = setInterval(logicHandler, 15 * SECOND); }; -export const detectManagedODF: FeatureDetector = async ( - setFlag: SetFeatureFlag -) => { - try { - const ns = await k8sGet({ - model: NamespaceModel, - name: CEPH_STORAGE_NAMESPACE, - }); - if (ns) { - const isManagedCluster = ns?.metadata?.labels?.[ODF_MANAGED_LABEL]; - setFlag(ODF_MANAGED_FLAG, !!isManagedCluster); - } - } catch (error) { - handleError(error, [ODF_MANAGED_FLAG], setFlag, detectManagedODF); - } -}; +// ToDo: Add logic to detect ROSA environment here +export const detectROSA: FeatureDetector = async (setFlag: SetFeatureFlag) => + setFlag(ROSA_FLAG, false); export const detectSSAR = (setFlag: SetFeatureFlag) => { const ssar = { @@ -281,11 +202,12 @@ export const detectComponents: FeatureDetector = async ( ) => { let cephIntervalId = null; let noobaaIntervalId = null; + const cephDetector = async () => { try { const cephClusters = (await k8sList({ model: CephClusterModel, - queryParams: { ns: CEPH_STORAGE_NAMESPACE }, + queryParams: { ns: null }, })) as K8sResourceCommon[]; if (cephClusters?.length > 0) { setFlag(CEPH_FLAG, true); @@ -295,16 +217,20 @@ export const detectComponents: FeatureDetector = async ( setFlag(CEPH_FLAG, false); } }; + + // Setting flag based on presence of NooBaa StorageClass gets created only if NooBaa CR is present const noobaaDetector = async () => { try { - const noobaaSystems = (await k8sList({ - model: NooBaaSystemModel, - queryParams: { ns: CEPH_STORAGE_NAMESPACE }, - })) as K8sResourceCommon[]; - if (noobaaSystems?.length > 0) { + const storageClasses = (await k8sList({ + model: StorageClassModel, + queryParams: { ns: null }, + })) as StorageClassResourceKind[]; + const isNooBaaPresent = storageClasses?.some((sc) => + sc?.provisioner?.endsWith(NOOBAA_PROVISIONER) + ); + if (isNooBaaPresent) { setFlag(MCG_FLAG, true); clearInterval(noobaaIntervalId); - clearInterval(cephIntervalId); } } catch { setFlag(MCG_FLAG, false); @@ -319,48 +245,4 @@ export const detectComponents: FeatureDetector = async ( noobaaIntervalId = setInterval(noobaaDetector, 15 * SECOND); }; -const detectFeatures = ( - setFlag: SetFeatureFlag, - csv: ClusterServiceVersionKind -) => { - const support = JSON.parse(getAnnotations(csv)?.[OCS_SUPPORT_ANNOTATION]); - _.keys(OCS_FEATURE_FLAGS).forEach((feature) => { - setFlag(feature, support.includes(OCS_FEATURE_FLAGS[feature])); - }); - - const disabled = JSON.parse( - getAnnotations(csv)?.[OCS_DISABLED_ANNOTATION] || '[]' - ); - _.keys(ODF_BLOCK_FLAG).forEach((feature) => { - setFlag(feature, disabled.includes(ODF_BLOCK_FLAG[feature])); - }); -}; - -export const detectOCSSupportedFeatures: FeatureDetector = async ( - setFlag: SetFeatureFlag -) => { - try { - const csvList = (await k8sGet({ - model: ClusterServiceVersionModel, - ns: CEPH_STORAGE_NAMESPACE, - })) as ListKind; - const ocsCSV = csvList.items.find((obj) => - _.startsWith(getName(obj), OCS_OPERATOR) - ); - if (ocsCSV) { - detectFeatures(setFlag, ocsCSV); - } else { - // If OCS CSV is not present then poll - setTimeout(() => detectOCSSupportedFeatures(setFlag), 15 * SECOND); - } - } catch (error) { - handleError( - error, - _.keys(OCS_FEATURE_FLAGS), - setFlag, - detectOCSSupportedFeatures - ); - } -}; - export type FeatureDetector = (setFlag: SetFeatureFlag) => Promise; diff --git a/packages/odf/hooks/index.ts b/packages/odf/hooks/index.ts new file mode 100644 index 000000000..636d6d3d3 --- /dev/null +++ b/packages/odf/hooks/index.ts @@ -0,0 +1,4 @@ +export * from './useSafeK8sGet'; +export * from './useSafeK8sList'; +export * from './useSafeK8sWatchResources'; +export * from './useSafeK8sWatchResource'; diff --git a/packages/odf/hooks/useSafeK8sGet.ts b/packages/odf/hooks/useSafeK8sGet.ts new file mode 100644 index 000000000..49cb512cd --- /dev/null +++ b/packages/odf/hooks/useSafeK8sGet.ts @@ -0,0 +1,29 @@ +import { useK8sGet } from '@odf/shared'; +import { getValidK8sOptions } from '@odf/shared/utils'; +import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk'; +import { K8sKind } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-types'; +import { useODFNamespaceSelector } from '../redux'; + +/** + * Wrapper around "useK8sGet" hook. + * Ensures no API request is made unless its "safe" to do so (ODF installed namespace is fetched successfully). + */ +export const useSafeK8sGet = ( + kind: K8sKind, + name?: string, + namespace?: string, + cluster?: string, + allowFallback = false +): [R, boolean, unknown] => { + const { odfNamespace, isNsSafe, isFallbackSafe } = useODFNamespaceSelector(); + + const canUseFallback = allowFallback && isFallbackSafe; + const k8sGetArgs = getValidK8sOptions( + isNsSafe || canUseFallback, + kind, + name, + namespace || odfNamespace, + cluster + ) as [K8sKind, string, string, string]; + return useK8sGet(...k8sGetArgs); +}; diff --git a/packages/odf/hooks/useSafeK8sList.ts b/packages/odf/hooks/useSafeK8sList.ts new file mode 100644 index 000000000..bcffba250 --- /dev/null +++ b/packages/odf/hooks/useSafeK8sList.ts @@ -0,0 +1,25 @@ +import { useK8sList } from '@odf/shared'; +import { getValidK8sOptions } from '@odf/shared/utils'; +import { K8sResourceCommon } from '@openshift-console/dynamic-plugin-sdk'; +import { K8sKind } from '@openshift-console/dynamic-plugin-sdk/lib/api/common-types'; +import { useODFNamespaceSelector } from '../redux'; + +/** + * Wrapper around "useK8sList" hook. + * Ensures no API request is made unless its "safe" to do so (ODF installed namespace is fetched successfully). + */ +export const useSafeK8sList = ( + kind: K8sKind, + namespace?: string, + allowFallback = false +): [R[], boolean, unknown] => { + const { odfNamespace, isNsSafe, isFallbackSafe } = useODFNamespaceSelector(); + + const canUseFallback = allowFallback && isFallbackSafe; + const k8sListArgs = getValidK8sOptions( + isNsSafe || canUseFallback, + kind, + namespace || odfNamespace + ) as [K8sKind, string]; + return useK8sList(...k8sListArgs); +}; diff --git a/packages/odf/hooks/useSafeK8sWatchResource.ts b/packages/odf/hooks/useSafeK8sWatchResource.ts new file mode 100644 index 000000000..8d56c6def --- /dev/null +++ b/packages/odf/hooks/useSafeK8sWatchResource.ts @@ -0,0 +1,34 @@ +import { K8sResourceObj } from '@odf/core/types'; +import { getValidWatchK8sResourceObj } from '@odf/shared/utils'; +import { + useK8sWatchResource, + K8sResourceCommon, + WatchK8sResult, +} from '@openshift-console/dynamic-plugin-sdk'; +import { useODFNamespaceSelector } from '../redux'; + +type UseSafeK8sWatchResource = < + R extends K8sResourceCommon | K8sResourceCommon[] +>( + initResource: K8sResourceObj | null, + allowFallback?: boolean +) => WatchK8sResult; + +/** + * Wrapper around "useK8sWatchResource" hook. + * Ensures no API request is made unless its "safe" to do so (ODF installed namespace is fetched successfully). + */ +export const useSafeK8sWatchResource: UseSafeK8sWatchResource = ( + initResource, + allowFallback = false +) => { + const { odfNamespace, isNsSafe, isFallbackSafe } = useODFNamespaceSelector(); + + const canUseFallback = allowFallback && isFallbackSafe; + return useK8sWatchResource( + getValidWatchK8sResourceObj( + initResource(odfNamespace), + isNsSafe || canUseFallback + ) + ); +}; diff --git a/packages/odf/hooks/useSafeK8sWatchResources.ts b/packages/odf/hooks/useSafeK8sWatchResources.ts new file mode 100644 index 000000000..b80a84fbf --- /dev/null +++ b/packages/odf/hooks/useSafeK8sWatchResources.ts @@ -0,0 +1,32 @@ +import { getValidWatchK8sResourcesObj } from '@odf/shared/utils'; +import { + useK8sWatchResources, + WatchK8sResources, + ResourcesObject, + WatchK8sResults, +} from '@openshift-console/dynamic-plugin-sdk'; +import { useODFNamespaceSelector } from '../redux'; + +type UseSafeK8sWatchResources = ( + initResources: (ns: string) => WatchK8sResources, + allowFallback?: boolean +) => WatchK8sResults; + +/** + * Wrapper around "useK8sWatchResources" hook. + * Ensures no API request is made unless its "safe" to do so (ODF installed namespace is fetched successfully). + */ +export const useSafeK8sWatchResources: UseSafeK8sWatchResources = ( + initResources, + allowFallback = false +) => { + const { odfNamespace, isNsSafe, isFallbackSafe } = useODFNamespaceSelector(); + + const canUseFallback = allowFallback && isFallbackSafe; + return useK8sWatchResources( + getValidWatchK8sResourcesObj( + initResources(odfNamespace), + isNsSafe || canUseFallback + ) + ); +}; diff --git a/packages/odf/modals/add-capacity/add-capacity-modal.tsx b/packages/odf/modals/add-capacity/add-capacity-modal.tsx index 7225dd3d7..c48c1c00d 100644 --- a/packages/odf/modals/add-capacity/add-capacity-modal.tsx +++ b/packages/odf/modals/add-capacity/add-capacity-modal.tsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import NamespaceSafetyBox from '@odf/core/components/utils/safety-box'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { getStorageClassDescription } from '@odf/core/utils'; import { getCephNodes } from '@odf/ocs/utils/common'; import ResourceDropdown from '@odf/shared/dropdown/ResourceDropdown'; @@ -204,6 +206,8 @@ export const AddCapacityModal: React.FC = ({ }) => { const { t } = useCustomTranslation(); + const { odfNamespace } = useODFNamespaceSelector(); + const [cephTotal, totalError, totalLoading] = useCustomPrometheusPoll({ endpoint: 'api/v1/query' as PrometheusEndpoint, query: CAPACITY_INFO_QUERIES[StorageDashboardQuery.RAW_CAPACITY_TOTAL], @@ -248,7 +252,7 @@ export const AddCapacityModal: React.FC = ({ const replica = getDeviceSetReplica( isArbiterEnabled, hasFlexibleScaling, - createWizardNodeState(getCephNodes(nodesData)) + createWizardNodeState(getCephNodes(nodesData, odfNamespace)) ); const name = ocsConfig?.metadata?.name; const totalCapacityMetric = values?.[0]; @@ -402,89 +406,93 @@ export const AddCapacityModal: React.FC = ({ className="add-capacity-modal" aria-label="Add Capacity" > - - - Adding capacity for {{ name }}, may increase your - expenses. - - {storageClassTooltip(t)}} - isRequired - > -
+ + + Adding capacity for {{ name }}, may increase your + expenses. + + {storageClassTooltip(t)} + } + isRequired > - setStorageClass(sc)} - data-test="add-cap-sc-dropdown" - initialSelection={preSelectionFilter} - filter={storageClassFilter} - /> -
- {!selectedSCName && ( -
+
+ setStorageClass(sc)} + data-test="add-cap-sc-dropdown" + initialSelection={preSelectionFilter} + filter={storageClassFilter} + /> +
+ {!selectedSCName && ( +
+ )} + + {!!selectedSCName && + (isNoProvionerSC ? ( + + ) : ( + <> + {!!osdSizeWithoutUnit && ( + + )} + + {t('Currently Used:')}  + {currentCapacity} + + + ))} + {errorMessage && ( + + {(errorMessage as any)?.message || errorMessage} + )} - - {!!selectedSCName && - (isNoProvionerSC ? ( - - ) : ( - <> - {!!osdSizeWithoutUnit && ( - - )} - - {t('Currently Used:')}  - {currentCapacity} - - - ))} - {errorMessage && ( - - {(errorMessage as any)?.message || errorMessage} - - )} - - - - {!loading || !inProgress ? ( + + - ) : ( - - )} - + {!loading || !inProgress ? ( + + ) : ( + + )} + + ); }; diff --git a/packages/odf/modals/advanced-kms-modal/advanced-vault-modal.tsx b/packages/odf/modals/advanced-kms-modal/advanced-vault-modal.tsx index 5dddd20fb..74b373b07 100644 --- a/packages/odf/modals/advanced-kms-modal/advanced-vault-modal.tsx +++ b/packages/odf/modals/advanced-kms-modal/advanced-vault-modal.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { useODFNamespaceSelector } from '@odf/core/redux'; import { FieldLevelHelp } from '@odf/shared/generic/FieldLevelHelp'; import { ModalBody, ModalFooter, ModalHeader } from '@odf/shared/modals/Modal'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; @@ -27,6 +28,9 @@ const AdvancedVaultModal: ModalComponent = (props) => { const kms = state.kms.providerState as VaultConfig; const { t } = useCustomTranslation(); + + const { odfNamespace } = useODFNamespaceSelector(); + const [backendPath, setBackendPath] = React.useState(kms?.backend || ''); const [authPath, setAuthPath] = React.useState(kms?.providerAuthPath || ''); const [authNamespace, setAuthNamespace] = React.useState( @@ -109,13 +113,19 @@ const AdvancedVaultModal: ModalComponent = (props) => { }; caCertificate && caCertificate !== '' - ? (kmsAdvanced.caCert = generateCASecret(caCertificate)) + ? (kmsAdvanced.caCert = generateCASecret(caCertificate, odfNamespace)) : (kmsAdvanced.caCert = null); clientCertificate && clientCertificate !== '' - ? (kmsAdvanced.clientCert = generateClientSecret(clientCertificate)) + ? (kmsAdvanced.clientCert = generateClientSecret( + clientCertificate, + odfNamespace + )) : (kmsAdvanced.clientCert = null); clientKey && clientCertificate !== '' - ? (kmsAdvanced.clientKey = generateClientKeySecret(clientKey)) + ? (kmsAdvanced.clientKey = generateClientKeySecret( + clientKey, + odfNamespace + )) : (kmsAdvanced.clientKey = null); dispatch({ diff --git a/packages/odf/redux/actions/index.ts b/packages/odf/redux/actions/index.ts new file mode 100644 index 000000000..311c45d9b --- /dev/null +++ b/packages/odf/redux/actions/index.ts @@ -0,0 +1 @@ +export * from './odf-namespace'; diff --git a/packages/odf/redux/actions/odf-namespace.ts b/packages/odf/redux/actions/odf-namespace.ts new file mode 100644 index 000000000..c7a43aee4 --- /dev/null +++ b/packages/odf/redux/actions/odf-namespace.ts @@ -0,0 +1,19 @@ +import { action } from 'typesafe-actions'; + +export type nsPayload = { + odfNamespace: string | null; + isODFNsLoaded: boolean; + odfNsLoadError: unknown; +}; + +export enum nsActions { + SetODFNamespace = 'setODFNamespace', +} + +export type nsActionTypes = { + type: nsActions; + payload: nsPayload; +}; + +export const setODFNamespace = (payload: nsPayload): nsActionTypes => + action(nsActions.SetODFNamespace, payload); diff --git a/packages/odf/redux/combineReducers.ts b/packages/odf/redux/combineReducers.ts new file mode 100644 index 000000000..b38a95f9f --- /dev/null +++ b/packages/odf/redux/combineReducers.ts @@ -0,0 +1,7 @@ +import { combineReducers } from 'redux'; +import { odfNamespaceReducer } from './reducers'; +import { odfNamespaceReducerName } from './selectors'; + +export default combineReducers({ + [odfNamespaceReducerName]: odfNamespaceReducer, +}); diff --git a/packages/odf/redux/dispatchers/index.ts b/packages/odf/redux/dispatchers/index.ts new file mode 100644 index 000000000..311c45d9b --- /dev/null +++ b/packages/odf/redux/dispatchers/index.ts @@ -0,0 +1 @@ +export * from './odf-namespace'; diff --git a/packages/odf/redux/dispatchers/odf-namespace.ts b/packages/odf/redux/dispatchers/odf-namespace.ts new file mode 100644 index 000000000..ccf9b5091 --- /dev/null +++ b/packages/odf/redux/dispatchers/odf-namespace.ts @@ -0,0 +1,16 @@ +import { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import { nsPayload, setODFNamespace } from '../actions'; + +type UseODFNamespaceDispatch = () => (payload: nsPayload) => void; + +export const useODFNamespaceDispatch: UseODFNamespaceDispatch = () => { + const dispatch = useDispatch(); + + return useCallback( + (payload: nsPayload) => { + dispatch(setODFNamespace(payload)); + }, + [dispatch] + ); +}; diff --git a/packages/odf/redux/index.ts b/packages/odf/redux/index.ts new file mode 100644 index 000000000..3064f6227 --- /dev/null +++ b/packages/odf/redux/index.ts @@ -0,0 +1,3 @@ +export { default } from './combineReducers'; +export * from './provider-hooks'; +export * from './selectors'; diff --git a/packages/odf/redux/provider-hooks/index.ts b/packages/odf/redux/provider-hooks/index.ts new file mode 100644 index 000000000..d7292f191 --- /dev/null +++ b/packages/odf/redux/provider-hooks/index.ts @@ -0,0 +1 @@ +export * from './useODFNamespace'; diff --git a/packages/odf/redux/provider-hooks/useODFNamespace.ts b/packages/odf/redux/provider-hooks/useODFNamespace.ts new file mode 100644 index 000000000..59c269de5 --- /dev/null +++ b/packages/odf/redux/provider-hooks/useODFNamespace.ts @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { SubscriptionModel } from '@odf/shared/models'; +import { getNamespace } from '@odf/shared/selectors'; +import { SubscriptionKind } from '@odf/shared/types'; +import { k8sList } from '@openshift-console/dynamic-plugin-sdk'; +import { useODFNamespaceDispatch } from '../dispatchers'; + +const FALLBACK_NAMESPACE = 'openshift-storage'; +const SPEC_NAME = 'odf-operator'; + +const isAbortError = (err): boolean => err?.name === 'AbortError'; + +const namespaceDetector = async (maxAttempt = 5): Promise => { + let attempt = 0; + let ns = null; + let shouldRetry = true; + + while (shouldRetry) { + shouldRetry = false; + attempt++; + try { + // this rule is to prevent independent async calls in a loop, + // here for retrying async operations (dependent), it is fine to disable it. + // eslint-disable-next-line no-await-in-loop + const subscriptions = (await k8sList({ + model: SubscriptionModel, + queryParams: { ns: null }, + })) as SubscriptionKind[]; + ns = getNamespace( + subscriptions.find((subscription) => + subscription.spec.name.includes(SPEC_NAME) + ) + ); + if (!ns) throw new Error('ODF install namespace not found'); + } catch (err) { + if (attempt <= maxAttempt && !isAbortError(err)) shouldRetry = true; + else throw err; + } + } + + return ns; +}; + +export const useODFNamespace = (): void => { + const dispatch = useODFNamespaceDispatch(); + + React.useEffect(() => { + namespaceDetector() + .then((ns) => + dispatch({ + odfNamespace: ns, + isODFNsLoaded: true, + odfNsLoadError: undefined, + }) + ) + /** + * ODF can be installed in any namespace, still recommended namespace is "openshift-storage". + * Using it as a fallback alongside storing the error object to the redux store, + * up to the consumer component what action to take (stick with the fallback or show an error to the user). + */ + .catch((err) => + dispatch({ + odfNamespace: FALLBACK_NAMESPACE, + isODFNsLoaded: true, + odfNsLoadError: err, + }) + ); + }, [dispatch]); +}; diff --git a/packages/odf/redux/reducers/index.ts b/packages/odf/redux/reducers/index.ts new file mode 100644 index 000000000..311c45d9b --- /dev/null +++ b/packages/odf/redux/reducers/index.ts @@ -0,0 +1 @@ +export * from './odf-namespace'; diff --git a/packages/odf/redux/reducers/odf-namespace.ts b/packages/odf/redux/reducers/odf-namespace.ts new file mode 100644 index 000000000..f8d4e7dec --- /dev/null +++ b/packages/odf/redux/reducers/odf-namespace.ts @@ -0,0 +1,25 @@ +import { produce } from 'immer'; +import { nsPayload, nsActions, nsActionTypes } from '../actions'; + +const initialState: nsPayload = { + odfNamespace: null, + isODFNsLoaded: false, + odfNsLoadError: undefined, +}; + +export const odfNamespaceReducer = ( + odfNamespaceState = initialState, + action: nsActionTypes +): nsPayload => { + const payload = action.payload; + switch (action.type) { + case nsActions.SetODFNamespace: + return produce(odfNamespaceState, (draft) => { + draft.odfNamespace = payload.odfNamespace; + draft.isODFNsLoaded = payload.isODFNsLoaded; + draft.odfNsLoadError = payload.odfNsLoadError; + }); + default: + return odfNamespaceState; + } +}; diff --git a/packages/odf/redux/selectors/index.ts b/packages/odf/redux/selectors/index.ts new file mode 100644 index 000000000..311c45d9b --- /dev/null +++ b/packages/odf/redux/selectors/index.ts @@ -0,0 +1 @@ +export * from './odf-namespace'; diff --git a/packages/odf/redux/selectors/odf-namespace.ts b/packages/odf/redux/selectors/odf-namespace.ts new file mode 100644 index 000000000..eb0673214 --- /dev/null +++ b/packages/odf/redux/selectors/odf-namespace.ts @@ -0,0 +1,29 @@ +import { useSelector } from 'react-redux'; +import { nsPayload } from '../actions'; + +export const odfNamespaceReducerName = 'odfInstallNs'; +export const reduxReducerScope = 'odfConsoleRedux'; + +const getODFNamespace = (state): nsPayload => + state.plugins?.[reduxReducerScope]?.[odfNamespaceReducerName] || {}; + +export const useODFNamespaceSelector = (): nsPayload & { + isNsSafe: boolean; + isFallbackSafe: boolean; +} => { + const { odfNamespace, isODFNsLoaded, odfNsLoadError } = + useSelector(getODFNamespace); + + return { + // installed operator namespace + odfNamespace, + // is namespace loaded and stored in redux + isODFNsLoaded, + // namespace loading error object (if any) + odfNsLoadError, + // is safe to use installed operator namespace + isNsSafe: !!odfNamespace && isODFNsLoaded && !odfNsLoadError, + // is safe to use fallback namespace (in case of any error, a fallback namespace is set alongside storing the error object to the redux store) + isFallbackSafe: !!odfNamespace && isODFNsLoaded, + }; +}; diff --git a/packages/odf/resources.ts b/packages/odf/resources.ts index 14e813d2c..6dcb55dcb 100644 --- a/packages/odf/resources.ts +++ b/packages/odf/resources.ts @@ -4,7 +4,7 @@ import { NooBaaNamespaceStoreModel, LocalVolumeDiscoveryResult, } from '@odf/core/models'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; +import { K8sResourceObj } from '@odf/core/types'; import { PersistentVolumeModel, StorageClassModel, @@ -14,7 +14,6 @@ import { SecretModel, SubscriptionModel, CephClusterModel, - ODFStorageSystem, PodModel, DeploymentModel, StatefulSetModel, @@ -60,12 +59,12 @@ export const subscriptionResource: WatchK8sResource = { namespaced: false, }; -export const cephBlockPoolResource: WatchK8sResource = { +export const cephBlockPoolResource: K8sResourceObj = (ns) => ({ kind: referenceForModel(CephBlockPoolModel), namespaced: true, isList: true, - namespace: CEPH_STORAGE_NAMESPACE, -}; + namespace: ns, +}); export const nodeResource: WatchK8sResource = { kind: NodeModel.kind, @@ -85,66 +84,59 @@ export const storageClusterResource: WatchK8sResource = { namespaced: false, }; -export const odfPodsResource: WatchK8sResource = { +export const odfPodsResource: K8sResourceObj = (ns) => ({ isList: true, kind: referenceForModel(PodModel), namespaced: true, - namespace: 'openshift-storage', -}; + namespace: ns, +}); -export const odfDeploymentsResource: WatchK8sResource = { +export const odfDeploymentsResource: K8sResourceObj = (ns) => ({ isList: true, kind: referenceForModel(DeploymentModel), namespaced: true, - namespace: 'openshift-storage', -}; + namespace: ns, +}); -export const odfStatefulSetResource: WatchK8sResource = { +export const odfStatefulSetResource: K8sResourceObj = (ns) => ({ isList: true, kind: referenceForModel(StatefulSetModel), namespaced: true, - namespace: 'openshift-storage', -}; + namespace: ns, +}); -export const odfReplicaSetResource: WatchK8sResource = { +export const odfReplicaSetResource: K8sResourceObj = (ns) => ({ isList: true, kind: referenceForModel(ReplicaSetModel), namespaced: true, - namespace: 'openshift-storage', -}; + namespace: ns, +}); -export const odfDaemonSetResource: WatchK8sResource = { +export const odfDaemonSetResource: K8sResourceObj = (ns) => ({ isList: true, kind: referenceForModel(DaemonSetModel), namespaced: true, - namespace: 'openshift-storage', -}; + namespace: ns, +}); -export const odfSystemResource: WatchK8sResource = { - isList: false, - kind: referenceForModel(ODFStorageSystem), - namespace: 'openshift-storage', - name: 'ocs-storagecluster-storagesystem', -}; - -export const secretResource: WatchK8sResource = { +export const secretResource: K8sResourceObj = (ns) => ({ isList: false, kind: SecretModel.kind, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: ns, name: 'rook-ceph-external-cluster-details', -}; +}); -export const backingStoreResource = { +export const backingStoreResource: K8sResourceObj = (ns) => ({ kind: referenceForModel(NooBaaBackingStoreModel), isList: true, - namespace: CEPH_STORAGE_NAMESPACE, -}; + namespace: ns, +}); -export const namespaceStoreResource = { +export const namespaceStoreResource: K8sResourceObj = (ns) => ({ kind: referenceForModel(NooBaaNamespaceStoreModel), isList: true, - namespace: CEPH_STORAGE_NAMESPACE, -}; + namespace: ns, +}); export const namespaceResource = { kind: referenceForModel(NamespaceModel), diff --git a/packages/odf/types/common.ts b/packages/odf/types/common.ts index ff23610f6..3e61cc95a 100644 --- a/packages/odf/types/common.ts +++ b/packages/odf/types/common.ts @@ -1,4 +1,7 @@ import { StorageClusterResource, ResourceConstraints } from '@odf/shared/types'; +import { WatchK8sResource } from '@openshift-console/dynamic-plugin-sdk'; + +export type K8sResourceObj = (ns: string) => WatchK8sResource; export enum BackingStorageType { EXISTING = 'existing', diff --git a/packages/odf/utils/mcg.ts b/packages/odf/utils/mcg.ts index a4b9b8643..18a6181c9 100644 --- a/packages/odf/utils/mcg.ts +++ b/packages/odf/utils/mcg.ts @@ -3,7 +3,7 @@ import { getAPIVersion } from '@odf/shared/selectors'; import { DeploymentKind, K8sResourceKind, - StorageClass, + StorageClassResourceKind, } from '@odf/shared/types'; import { getAPIVersionForModel, @@ -195,13 +195,13 @@ export const secretPayloadCreator = ( return payload; }; -const objectStorageProvisioners = [ - 'openshift-storage.noobaa.io/obc', - 'openshift-storage.ceph.rook.io/bucket', +const objectStorageProvisioners = (ns: string) => [ + `${ns}.noobaa.io/obc`, + `${ns}.ceph.rook.io/bucket`, ]; -export const isObjectSC = (sc: StorageClass) => - objectStorageProvisioners.includes(sc.provisioner); +export const isObjectSC = (sc: StorageClassResourceKind, ns: string) => + objectStorageProvisioners(ns).includes(sc?.provisioner); export const awsRegionItems = _.zipObject(AWS_REGIONS, AWS_REGIONS); diff --git a/packages/odf/utils/ocs.ts b/packages/odf/utils/ocs.ts index a2b59cc6c..a0f5daa34 100644 --- a/packages/odf/utils/ocs.ts +++ b/packages/odf/utils/ocs.ts @@ -1,4 +1,3 @@ -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { NamespaceModel } from '@odf/shared/models'; import { DeviceSet, @@ -232,12 +231,12 @@ export const isValidTopology = ( return zones.size >= MINIMUM_NODES || racks.size >= MINIMUM_NODES; }; -export const labelOCSNamespace = (): Promise => +export const labelOCSNamespace = (ns: string): Promise => k8sPatch({ model: NamespaceModel, resource: { metadata: { - name: CEPH_STORAGE_NAMESPACE, + name: ns, }, }, data: [ diff --git a/packages/shared/src/dashboards/breakdown-card/breakdown-body.tsx b/packages/shared/src/dashboards/breakdown-card/breakdown-body.tsx index f717244f2..a35d59a6d 100644 --- a/packages/shared/src/dashboards/breakdown-card/breakdown-body.tsx +++ b/packages/shared/src/dashboards/breakdown-card/breakdown-body.tsx @@ -20,6 +20,7 @@ export const BreakdownCardBody: React.FC = ({ ocsVersion = '', labelPadding, isPersistentInternal, + odfNamespace, }) => { const { t } = useCustomTranslation(); @@ -83,6 +84,7 @@ export const BreakdownCardBody: React.FC = ({ metricModel={metricModel} ocsVersion={ocsVersion} labelPadding={labelPadding} + odfNamespace={odfNamespace} /> @@ -101,4 +103,5 @@ export type BreakdownBodyProps = { ocsVersion?: string; labelPadding?: LabelPadding; isPersistentInternal?: boolean; + odfNamespace?: string; }; diff --git a/packages/shared/src/dashboards/breakdown-card/breakdown-chart.tsx b/packages/shared/src/dashboards/breakdown-card/breakdown-chart.tsx index b8ecc23f4..8eca4171d 100644 --- a/packages/shared/src/dashboards/breakdown-card/breakdown-chart.tsx +++ b/packages/shared/src/dashboards/breakdown-card/breakdown-chart.tsx @@ -19,7 +19,7 @@ import './breakdown-card.scss'; export const LinkableLegend: React.FC = React.memo( (props: LinkableLegendProps) => { - const { metricModel, datum, ocsVersion } = props; + const { metricModel, datum, ocsVersion, odfNamespace } = props; let href: string = metricModel ? resourcePathFromModel(metricModel, datum.link, datum.ns) : ''; @@ -43,8 +43,8 @@ export const LinkableLegend: React.FC = React.memo( return customLegend; } if (metricModel.kind === BUCKETCLASSKIND) { - if (ocsVersion) { - href = `/k8s/ns/openshift-storage/clusterserviceversions/${ocsVersion}/${referenceForModel( + if (ocsVersion && odfNamespace) { + href = `/k8s/ns/${odfNamespace}/clusterserviceversions/${ocsVersion}/${referenceForModel( metricModel )}/${datum.link}`; } else { @@ -67,6 +67,7 @@ export const BreakdownChart: React.FC = ({ metricModel, ocsVersion, labelPadding, + odfNamespace, }) => ( <> = ({ data={legends} y={40} labelComponent={ - + } orientation="horizontal" symbolSpacer={7} @@ -137,6 +142,7 @@ export type BreakdownChartProps = { metricModel: K8sKind; ocsVersion?: string; labelPadding?: LabelPadding; + odfNamespace?: string; }; export type LabelPadding = { @@ -152,4 +158,5 @@ export type LinkableLegendProps = { [key: string]: any; }; ocsVersion?: string; + odfNamespace?: string; }; diff --git a/packages/shared/src/hooks/custom-prometheus-poll/custom-prometheus-poll-hook.ts b/packages/shared/src/hooks/custom-prometheus-poll/custom-prometheus-poll-hook.ts index 7b60d6e75..ebd13f2d2 100644 --- a/packages/shared/src/hooks/custom-prometheus-poll/custom-prometheus-poll-hook.ts +++ b/packages/shared/src/hooks/custom-prometheus-poll/custom-prometheus-poll-hook.ts @@ -7,7 +7,7 @@ import { PrometheusResponse } from '@openshift-console/dynamic-plugin-sdk'; import { getPrometheusURL } from './helpers'; import { useURLPoll } from './use-url-poll'; -type CustomPrometheusPollProps = { +export type CustomPrometheusPollProps = { delay?: number; endpoint: PrometheusEndpoint; endTime?: number; diff --git a/packages/shared/src/hooks/custom-prometheus-poll/utils.ts b/packages/shared/src/hooks/custom-prometheus-poll/utils.ts index a2ca91961..c0c057e06 100644 --- a/packages/shared/src/hooks/custom-prometheus-poll/utils.ts +++ b/packages/shared/src/hooks/custom-prometheus-poll/utils.ts @@ -1,8 +1,8 @@ import { useFlag } from '@openshift-console/dynamic-plugin-sdk'; -const ODF_MANAGED = 'ODF_MANAGED'; -const ODF_MANAGED_PROXY_ENDPOINT = - '/api/proxy/plugin/odf-console/odf-managed-service-prom'; +const ROSA_FLAG = 'ROSA'; +// ToDo: Add ROSA proxy endpoint here +const ROSA_PROXY_ENDPOINT = '/api/proxy/plugin/odf-console/rosa'; export const usePrometheusBasePath = () => - useFlag(ODF_MANAGED) ? ODF_MANAGED_PROXY_ENDPOINT : ''; + useFlag(ROSA_FLAG) ? ROSA_PROXY_ENDPOINT : ''; diff --git a/packages/shared/src/hooks/k8s-get-hook.ts b/packages/shared/src/hooks/k8s-get-hook.ts index f3f669a6a..4ff15200c 100644 --- a/packages/shared/src/hooks/k8s-get-hook.ts +++ b/packages/shared/src/hooks/k8s-get-hook.ts @@ -34,7 +34,8 @@ export const useK8sGet = ( setLoaded(true); } }; - fetch(); + if (kind) fetch(); + else setLoaded(true); }, [kind, name, namespace, cluster]); return [data, loaded, loadError]; diff --git a/packages/shared/src/hooks/use-fetch-csv.tsx b/packages/shared/src/hooks/use-fetch-csv.tsx index 3aa19a464..7b8c5d353 100644 --- a/packages/shared/src/hooks/use-fetch-csv.tsx +++ b/packages/shared/src/hooks/use-fetch-csv.tsx @@ -5,49 +5,68 @@ import { } from '@odf/shared/models'; import { ClusterServiceVersionKind, SubscriptionKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { referenceForModel } from '@odf/shared/utils'; +import { + referenceForModel, + getValidWatchK8sResourceObj, +} from '@odf/shared/utils'; import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk'; export const useFetchCsv = ({ specName, namespace, cluster, + startPollingInstantly = true, }: UseFetchCsvProps): UseFetchCsvResult => { const { t } = useCustomTranslation(); const [subs, subsLoaded, subsLoadError] = useK8sWatchResource< SubscriptionKind[] - >({ - isList: true, - kind: referenceForModel(SubscriptionModel), - namespace, - cluster: cluster, + >( + getValidWatchK8sResourceObj( + { + isList: true, + kind: referenceForModel(SubscriptionModel), + namespace, + cluster: cluster, + }, + startPollingInstantly + ) + ); + const [csvDetails, setCSVDetails] = React.useState({ + csvName: null, + csvNamespace: null, }); - const csvName = React.useRef(null); - const csvNamespace = React.useRef(null); React.useEffect(() => { - if (subsLoaded && !subsLoadError && subs.length) { + const alreadyHaveDetails = + !!csvDetails.csvName && !!csvDetails.csvNamespace; + if (subsLoaded && !subsLoadError && subs?.length && !alreadyHaveDetails) { const sub = subs.find((s) => s.spec.name === specName); - csvName.current = sub?.status?.installedCSV; - csvNamespace.current = sub?.metadata?.namespace; + const csvName = sub?.status?.installedCSV; + const csvNamespace = sub?.metadata?.namespace; + setCSVDetails({ csvName, csvNamespace }); } - }, [specName, subs, subsLoadError, subsLoaded]); + }, [specName, subs, subsLoadError, subsLoaded, csvDetails, setCSVDetails]); const [csv, csvLoaded, csvLoadError] = - useK8sWatchResource({ - kind: referenceForModel(ClusterServiceVersionModel), - name: csvName.current, - namespaced: true, - namespace: csvNamespace.current, - isList: false, - cluster: cluster, - }); + useK8sWatchResource( + getValidWatchK8sResourceObj( + { + kind: referenceForModel(ClusterServiceVersionModel), + name: csvDetails.csvName, + namespaced: true, + namespace: csvDetails.csvNamespace, + isList: false, + cluster: cluster, + }, + csvDetails.csvName !== null && csvDetails.csvNamespace !== null + ) + ); - if (csvName.current === null || csvNamespace.current === null) { + if (csvDetails.csvName === null || csvDetails.csvNamespace === null) { return [undefined, false, undefined]; } - if (!csvName.current || !csvNamespace.current) { + if (!csvDetails.csvName || !csvDetails.csvNamespace) { return [undefined, true, new Error(t('Not found'))]; } @@ -59,4 +78,5 @@ type UseFetchCsvProps = { specName: string; namespace?: string; cluster?: string; + startPollingInstantly?: boolean; }; diff --git a/packages/shared/src/hooks/useK8sList.ts b/packages/shared/src/hooks/useK8sList.ts index b4cdb87c5..f2699aa9a 100644 --- a/packages/shared/src/hooks/useK8sList.ts +++ b/packages/shared/src/hooks/useK8sList.ts @@ -33,7 +33,8 @@ export const useK8sList = ( setLoaded(true); } }; - fetch(); + if (kind) fetch(); + else setLoaded(true); }, [kind, namespace]); return [data, loaded, loadError]; diff --git a/packages/shared/src/queries/pod.ts b/packages/shared/src/queries/pod.ts index 1dc694c9c..e48ef299e 100644 --- a/packages/shared/src/queries/pod.ts +++ b/packages/shared/src/queries/pod.ts @@ -3,9 +3,7 @@ export enum PodMetrics { MEMORY = 'MEMORY', } -export const POD_QUERIES = { - [PodMetrics.CPU]: - 'pod:container_cpu_usage:sum{namespace="openshift-storage"}', - [PodMetrics.MEMORY]: - 'sum(container_memory_working_set_bytes{namespace="openshift-storage", container=""}) BY (pod, namespace)', -}; +export const POD_QUERIES = (ns: string) => ({ + [PodMetrics.CPU]: `pod:container_cpu_usage:sum{namespace="${ns}"}`, + [PodMetrics.MEMORY]: `sum(container_memory_working_set_bytes{namespace="${ns}", container=""}) BY (pod, namespace)`, +}); diff --git a/packages/shared/src/topology/sidebar/common/PodList.tsx b/packages/shared/src/topology/sidebar/common/PodList.tsx index 073402a9c..2290da510 100644 --- a/packages/shared/src/topology/sidebar/common/PodList.tsx +++ b/packages/shared/src/topology/sidebar/common/PodList.tsx @@ -4,6 +4,7 @@ import ResourceLink from '@odf/shared/resource-link/resource-link'; import { getUID } from '@odf/shared/selectors'; import { resourceStatus } from '@odf/shared/status/Resource'; import { Status } from '@odf/shared/status/Status'; +import { PodKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { formatBytesAsMiB, @@ -12,7 +13,6 @@ import { } from '@odf/shared/utils'; import { ResourceStatus } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; -import { PodKind } from 'packages/shared/types'; type PodOverviewItemProps = { pod: PodWithMetricsKind; diff --git a/packages/shared/src/topology/sidebar/deployments/DeploymentObserve.tsx b/packages/shared/src/topology/sidebar/deployments/DeploymentObserve.tsx index 4dd16799a..8be9d2dfd 100644 --- a/packages/shared/src/topology/sidebar/deployments/DeploymentObserve.tsx +++ b/packages/shared/src/topology/sidebar/deployments/DeploymentObserve.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { PrometheusUtilizationItem } from '@odf/shared/dashboards/utilization-card/prometheus-utilization-item'; import { DeploymentModel } from '@odf/shared/models'; import { @@ -17,17 +16,19 @@ import '../node/node-observe.scss'; type NodeObserveProps = { resource: DeploymentKind; + odfNamespace?: string; }; export const DeploymentObserve: React.FC = ({ resource: deployment, + odfNamespace, }) => { const { t } = useCustomTranslation(); const name = getName(deployment); const utilizationQueries = getUtilizationQueries( - CEPH_STORAGE_NAMESPACE, + odfNamespace, name, DeploymentModel.kind.toLowerCase() ); diff --git a/packages/shared/src/topology/sidebar/deployments/DeploymentResources.tsx b/packages/shared/src/topology/sidebar/deployments/DeploymentResources.tsx index 61734d9fd..adb50624f 100644 --- a/packages/shared/src/topology/sidebar/deployments/DeploymentResources.tsx +++ b/packages/shared/src/topology/sidebar/deployments/DeploymentResources.tsx @@ -1,16 +1,10 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { StatusBox } from '@odf/shared/generic'; import { useCustomPrometheusPoll, usePrometheusBasePath, } from '@odf/shared/hooks/custom-prometheus-poll'; -import { - DaemonSetModel, - PodModel, - ReplicaSetModel, - StatefulSetModel, -} from '@odf/shared/models'; +import { PodModel } from '@odf/shared/models'; import { POD_QUERIES, PodMetrics } from '@odf/shared/queries/pod'; import { PodsOverviewList, @@ -18,37 +12,14 @@ import { } from '@odf/shared/topology/sidebar/common/PodList'; import { DeploymentKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; -import { referenceForModel } from '@odf/shared/utils'; import { PrometheusResponse, PrometheusResult, useK8sWatchResource, - WatchK8sResource, } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; import '@odf/shared/topology/sidebar/common/resources-tab.scss'; -export const odfStatefulSetResource: WatchK8sResource = { - isList: true, - kind: referenceForModel(StatefulSetModel), - namespaced: true, - namespace: 'openshift-storage', -}; - -export const odfReplicaSetResource: WatchK8sResource = { - isList: true, - kind: referenceForModel(ReplicaSetModel), - namespaced: true, - namespace: 'openshift-storage', -}; - -export const odfDaemonSetResource: WatchK8sResource = { - isList: true, - kind: referenceForModel(DaemonSetModel), - namespaced: true, - namespace: 'openshift-storage', -}; - const getPodMetric = ( metrics: PrometheusResponse['data']['result'], podName @@ -62,10 +33,12 @@ const getPodMetric = ( type DeploymentResourceProps = { resource: DeploymentKind; + odfNamespace: string; }; export const DeploymentResources: React.FC = ({ resource: deployment, + odfNamespace, }) => { const { t } = useCustomTranslation(); const podSelector = deployment.spec.selector; @@ -75,20 +48,20 @@ export const DeploymentResources: React.FC = ({ kind: PodModel.kind, isList: true, namespaced: true, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, selector: podSelector, }); const basePath = usePrometheusBasePath(); const [usedCpu, errorUsedCpu, loadingUsedCpu] = useCustomPrometheusPoll({ endpoint: 'api/v1/query' as any, - query: POD_QUERIES[PodMetrics.CPU], + query: POD_QUERIES(odfNamespace)[PodMetrics.CPU], basePath: basePath, }); const [usedMemory, errorUsedMemory, loadingUsedMemory] = useCustomPrometheusPoll({ endpoint: 'api/v1/query' as any, - query: POD_QUERIES[PodMetrics.MEMORY], + query: POD_QUERIES(odfNamespace)[PodMetrics.MEMORY], basePath: basePath, }); diff --git a/packages/shared/src/topology/sidebar/node/NodeObserve.tsx b/packages/shared/src/topology/sidebar/node/NodeObserve.tsx index be86c2a97..d6e3ffb10 100644 --- a/packages/shared/src/topology/sidebar/node/NodeObserve.tsx +++ b/packages/shared/src/topology/sidebar/node/NodeObserve.tsx @@ -19,6 +19,7 @@ import './node-observe.scss'; type NodeObserveProps = { resource: NodeKind; + odfNamespace?: string; }; export const NodeObserve: React.FC = ({ resource: node }) => { diff --git a/packages/shared/src/topology/sidebar/node/NodeResources.tsx b/packages/shared/src/topology/sidebar/node/NodeResources.tsx index 6c81fea47..09e66dfe5 100644 --- a/packages/shared/src/topology/sidebar/node/NodeResources.tsx +++ b/packages/shared/src/topology/sidebar/node/NodeResources.tsx @@ -1,5 +1,4 @@ import * as React from 'react'; -import { CEPH_STORAGE_NAMESPACE } from '@odf/shared/constants'; import { DataUnavailableError } from '@odf/shared/generic/Error'; import { useCustomPrometheusPoll, @@ -11,13 +10,13 @@ import { PodsOverviewList, PodWithMetricsKind, } from '@odf/shared/topology/sidebar/common/PodList'; +import { NodeKind, PodKind } from '@odf/shared/types'; import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook'; import { PrometheusResponse, useK8sWatchResource, } from '@openshift-console/dynamic-plugin-sdk'; import * as _ from 'lodash-es'; -import { NodeKind, PodKind } from 'packages/shared/types'; import { Title } from '@patternfly/react-core'; import '@odf/shared/topology/sidebar/common/resources-tab.scss'; @@ -34,17 +33,19 @@ const getPodMetric = ( type NodeResourcesProps = { resource: NodeKind; + odfNamespace: string; }; export const NodeResources: React.FC = ({ resource: node, + odfNamespace, }) => { const { t } = useCustomTranslation(); const [pods, loaded, loadError] = useK8sWatchResource({ kind: PodModel.kind, isList: true, namespaced: true, - namespace: CEPH_STORAGE_NAMESPACE, + namespace: odfNamespace, }); const filteredPods = @@ -57,13 +58,13 @@ export const NodeResources: React.FC = ({ const basePath = usePrometheusBasePath(); const [usedCpu, errorUsedCpu, loadingUsedCpu] = useCustomPrometheusPoll({ endpoint: 'api/v1/query' as any, - query: POD_QUERIES[PodMetrics.CPU], + query: POD_QUERIES(odfNamespace)[PodMetrics.CPU], basePath: basePath, }); const [usedMemory, errorUsedMemory, loadingUsedMemory] = useCustomPrometheusPoll({ endpoint: 'api/v1/query' as any, - query: POD_QUERIES[PodMetrics.MEMORY], + query: POD_QUERIES(odfNamespace)[PodMetrics.MEMORY], basePath: basePath, }); diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index ea17c8774..b93b50272 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -9,3 +9,4 @@ export * from './node'; export * from './pod-health'; export * from './AlertFilters'; export * from './link'; +export * from './valid-k8s-resources'; diff --git a/packages/shared/src/utils/valid-k8s-resources.ts b/packages/shared/src/utils/valid-k8s-resources.ts new file mode 100644 index 000000000..428524f9b --- /dev/null +++ b/packages/shared/src/utils/valid-k8s-resources.ts @@ -0,0 +1,49 @@ +import { + WatchK8sResource, + WatchK8sResources, + ResourcesObject, + K8sModel, +} from '@openshift-console/dynamic-plugin-sdk'; +import { CustomPrometheusPollProps } from '../hooks/custom-prometheus-poll/custom-prometheus-poll-hook'; + +/** + * Returns "undefined" to make sure that no unnecessary API request is made unless needed (or "isSafe" to do so). + * Example use case with "useK8sWatchResource" hook. + */ +export const getValidWatchK8sResourceObj = ( + initResource: WatchK8sResource, + isSafe: boolean +): WatchK8sResource | undefined => (isSafe ? initResource : undefined); + +/** + * Returns "{}" to make sure that no unnecessary API request is made unless needed (or "isSafe" to do so). + * Example use case with "useK8sWatchResources" hook. + */ +export const getValidWatchK8sResourcesObj = ( + initResources: WatchK8sResources, + isSafe: boolean +): WatchK8sResources | undefined => + isSafe ? initResources : {}; + +type SafeOptions = [K8sModel, string, string, string] | [K8sModel, string]; +type UnSafeOptions = [undefined]; +type K8sOptions = SafeOptions | UnSafeOptions; +/** + * Returns "[undefined]" to make sure that no unnecessary API request is made unless needed (or "isSafe" to do so). + * Need to spread (...) before passing these as arguments to the the hook. + * Example use case with "useK8sGet", "useK8sList" hooks. + */ +export const getValidK8sOptions = ( + isSafe: boolean, + ...initOptions: SafeOptions +): K8sOptions => (isSafe ? initOptions : [undefined]); + +/** + * Returns "{ endpoint: undefined, query: undefined }" to make sure that no unnecessary API request is made unless needed (or "isSafe" to do so). + * Example use case with "useCustomPrometheusPoll" hook. + */ +export const getValidPrometheusPollObj = ( + initProps: CustomPrometheusPollProps, + isSafe: boolean +): CustomPrometheusPollProps => + isSafe ? initProps : { endpoint: undefined, query: undefined }; diff --git a/plugins/odf/console-extensions.json b/plugins/odf/console-extensions.json index c87777079..97904508a 100644 --- a/plugins/odf/console-extensions.json +++ b/plugins/odf/console-extensions.json @@ -19,7 +19,7 @@ "type": "console.flag", "properties": { "handler": { - "$codeRef": "features.detectOCSSupportedFeatures" + "$codeRef": "features.detectRGW" } } }, @@ -27,7 +27,7 @@ "type": "console.flag", "properties": { "handler": { - "$codeRef": "features.detectRGW" + "$codeRef": "features.detectROSA" } } }, @@ -35,7 +35,7 @@ "type": "console.flag", "properties": { "handler": { - "$codeRef": "features.detectManagedODF" + "$codeRef": "features.detectComponents" } } }, @@ -43,15 +43,22 @@ "type": "console.flag", "properties": { "handler": { - "$codeRef": "features.detectComponents" + "$codeRef": "features.detectSSAR" } } }, { - "type": "console.flag", + "type": "console.redux-reducer", + "properties": { + "scope": "odfConsoleRedux", + "reducer": { "$codeRef": "odfReduxReducers" } + } + }, + { + "type": "console.flag/hookProvider", "properties": { "handler": { - "$codeRef": "features.detectSSAR" + "$codeRef": "odfReduxReducers.useODFNamespace" } } }, @@ -269,12 +276,6 @@ "provisioner": "rbd.csi.ceph.com", "allowVolumeExpansion": true, "parameters": { - "clusterID": { - "name": "Cluster ID", - "hintText": "The namespace where Ceph is deployed", - "value": "openshift-storage", - "visible": false - }, "pool": { "name": "Storage Pool", "hintText": "Storage pool into which volume data shall be stored", @@ -302,24 +303,12 @@ "value": "rook-csi-rbd-provisioner", "visible": false }, - "csi.storage.k8s.io/provisioner-secret-namespace": { - "name": "Provisioner Secret Namespace", - "hintText": "The namespace where provisioner secret is created", - "value": "openshift-storage", - "visible": false - }, "csi.storage.k8s.io/node-stage-secret-name": { "name": "Node Stage Secret Name", "hintText": "The name of Node Stage secret", "value": "rook-csi-rbd-node", "visible": false }, - "csi.storage.k8s.io/node-stage-secret-namespace": { - "name": "Node Stage Secret Namespace", - "hintText": "The namespace where provisioner secret is created", - "value": "openshift-storage", - "visible": false - }, "csi.storage.k8s.io/fstype": { "name": "Filesystem Type", "hintText": "Ceph RBD filesystem type. Default set to ext4", @@ -332,12 +321,6 @@ "value": "rook-csi-rbd-provisioner", "visible": false }, - "csi.storage.k8s.io/controller-expand-secret-namespace": { - "name": "Expand Secret Namespace", - "hintText": "The namespace where provisioner secret is created", - "value": "openshift-storage", - "visible": false - }, "encrypted": { "name": "Enable Encryption", "hintText": "The namespace where provisioner secret is created", @@ -380,12 +363,6 @@ "provisioner": "cephfs.csi.ceph.com", "allowVolumeExpansion": true, "parameters": { - "clusterID": { - "name": "Cluster ID", - "hintText": "The namespace where Ceph is deployed", - "value": "openshift-storage", - "visible": false - }, "fsName": { "name": "Filesystem Name", "hintText": "CephFS filesystem name into which the volume shall be created", @@ -401,36 +378,21 @@ "value": "rook-csi-cephfs-provisioner", "visible": false }, - "csi.storage.k8s.io/provisioner-secret-namespace": { - "name": "Provisioner Secret Namespace", - "hintText": "The namespace where provisioner secret is created", - "value": "openshift-storage", - "visible": false - }, "csi.storage.k8s.io/node-stage-secret-name": { "name": "Node Stage Secret Name", "hintText": "The name of Node Stage secret", "value": "rook-csi-cephfs-node", "visible": false }, - "csi.storage.k8s.io/node-stage-secret-namespace": { - "name": "Node Stage Secret Namespace", - "hintText": "The namespace where provisioner secret is created", - "value": "openshift-storage", - "visible": false - }, "csi.storage.k8s.io/controller-expand-secret-name": { "name": "Expand Secret Name", "hintText": "The namespace where provisioner secret is created", "value": "rook-csi-cephfs-provisioner", "visible": false - }, - "csi.storage.k8s.io/controller-expand-secret-namespace": { - "name": "Expand Secret Namespace", - "hintText": "The namespace where provisioner secret is created", - "value": "openshift-storage", - "visible": false } + }, + "mutator": { + "$codeRef": "mutators.addClusterParams" } } } diff --git a/plugins/odf/console-plugin.json b/plugins/odf/console-plugin.json index dcf403d2f..e4ec959f2 100644 --- a/plugins/odf/console-plugin.json +++ b/plugins/odf/console-plugin.json @@ -37,6 +37,7 @@ "topology": "@odf/core/components/topology/Topology", "ibmStorage": "@odf/ibm/system-connection-details", "objectService": "@odf/core/components/object-service-nav-item/object-service", - "mutators": "@odf/ocs/storage-class/mutators" + "mutators": "@odf/ocs/storage-class/mutators", + "odfReduxReducers": "@odf/core/redux" } } diff --git a/yarn.lock b/yarn.lock index 190a089d8..29bd6e68a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6571,6 +6571,11 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +immer@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.3.tgz#a8de42065e964aa3edf6afc282dfc7f7f34ae3c9" + integrity sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A== + immutable@3.x: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"