Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display storage class selected in workbenches #3214

Merged
merged 1 commit into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion frontend/src/__mocks__/mockPVCK8sResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type MockResourceConfigType = {
name?: string;
namespace?: string;
storage?: string;
storageClassName?: string;
displayName?: string;
uid?: string;
};
Expand All @@ -13,6 +14,7 @@ export const mockPVCK8sResource = ({
name = 'test-storage',
namespace = 'test-project',
storage = '5Gi',
storageClassName = 'gp3',
displayName = 'Test Storage',
uid = genUID('pvc'),
}: MockResourceConfigType): PersistentVolumeClaimKind => ({
Expand All @@ -38,7 +40,7 @@ export const mockPVCK8sResource = ({
},
},
volumeName: 'pvc-8644e33b-a710-45a3-9d54-7f987494643a',
storageClassName: 'gp3',
storageClassName,
volumeMode: 'Filesystem',
},
status: {
Expand Down
26 changes: 26 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/clusterStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ class ClusterStorageRow extends TableRow {
this.find().findByRole('button', { name: 'Details' }).click();
}

findDeprecatedLabel() {
return this.find().findByTestId('storage-class-deprecated');
}

shouldHaveDeprecatedTooltip() {
cy.findByTestId('storage-class-deprecated-tooltip').should('be.visible');
return this;
}

findStorageClassColumn() {
return this.find().find('[data-label="Storage class"]');
}

shouldHaveStorageSize(name: string) {
this.find().siblings().find('[data-label=Size]').contains(name).should('exist');
return this;
Expand Down Expand Up @@ -118,6 +131,19 @@ class ClusterStorage {
return this.findClusterStorageTable().find('thead').findByRole('button', { name });
}

shouldHaveDeprecatedAlertMessage() {
return cy
.findByTestId('storage-class-deprecated-alert')
.should(
'contain.text',
'Warning alert:Deprecated storage classA storage class has been deprecated by your administrator, but the cluster storage using it is still active. If you want to migrate your data to cluster storage instance using a different storage class, contact your administrator.',
);
}

closeDeprecatedAlert() {
cy.findByTestId('storage-class-deprecated-alert-close-button').click();
}

getClusterStorageRow(name: string) {
return new ClusterStorageRow(() =>
this.findClusterStorageTable().find(`[data-label=Name]`).contains(name).parents('tr'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {
buildMockStorageClass,
mockDashboardConfig,
mockK8sResourceList,
mockNotebookK8sResource,
mockProjectK8sResource,
mockStorageClasses,
mockStorageClassList,
} from '~/__mocks__';

import { mockClusterSettings } from '~/__mocks__/mockClusterSettings';
import { mockPVCK8sResource } from '~/__mocks__/mockPVCK8sResource';
import { mockPodK8sResource } from '~/__mocks__/mockPodK8sResource';
Expand All @@ -27,9 +30,10 @@ import { storageClassesTable } from '~/__tests__/cypress/cypress/pages/storageCl

type HandlersProps = {
isEmpty?: boolean;
storageClassName?: string;
};

const initInterceptors = ({ isEmpty = false }: HandlersProps) => {
const initInterceptors = ({ isEmpty = false, storageClassName }: HandlersProps) => {
cy.interceptOdh('GET /api/cluster-settings', mockClusterSettings({}));
cy.interceptK8sList(PodModel, mockK8sResourceList([mockPodK8sResource({})]));
cy.interceptK8sList(ProjectModel, mockK8sResourceList([mockProjectK8sResource({})]));
Expand All @@ -44,7 +48,7 @@ const initInterceptors = ({ isEmpty = false }: HandlersProps) => {
isEmpty
? []
: [
mockPVCK8sResource({ uid: 'test-id' }),
mockPVCK8sResource({ uid: 'test-id', storageClassName }),
mockPVCK8sResource({ displayName: 'Another Cluster Storage' }),
],
),
Expand All @@ -55,6 +59,43 @@ const initInterceptors = ({ isEmpty = false }: HandlersProps) => {
const [openshiftDefaultStorageClass, otherStorageClass] = mockStorageClasses;

describe('ClusterStorage', () => {
describe('when StorageClasses feature flag is enabled', () => {
beforeEach(() => {
cy.interceptOdh(
'GET /api/config',
mockDashboardConfig({
disableStorageClasses: false,
}),
);

cy.interceptOdh(
'GET /api/k8s/apis/storage.k8s.io/v1/storageclasses',
{},
mockStorageClassList(),
);
});

it('Check whether the Storage class column is present', () => {
initInterceptors({ storageClassName: 'openshift-default-sc' });
clusterStorage.visit('test-project');
const clusterStorageRow = clusterStorage.getClusterStorageRow('Test Storage');
clusterStorageRow.findStorageClassColumn().should('exist');
});

it('Check whether the Storage class is deprecated', () => {
initInterceptors({ storageClassName: 'test-storage-class-1' });
clusterStorage.visit('test-project');

const clusterStorageRow = clusterStorage.getClusterStorageRow('Test Storage');
clusterStorageRow.findDeprecatedLabel().should('exist');

clusterStorageRow.findDeprecatedLabel().trigger('mouseenter');
clusterStorageRow.shouldHaveDeprecatedTooltip();
clusterStorage.shouldHaveDeprecatedAlertMessage();
clusterStorage.closeDeprecatedAlert();
});
});

it('Empty state', () => {
initInterceptors({ isEmpty: true });
clusterStorage.visit('test-project');
Expand Down Expand Up @@ -121,10 +162,17 @@ describe('ClusterStorage', () => {
});
});

it('list accelerator profiles and Table sorting', () => {
it('list cluster storage and Table sorting', () => {
cy.interceptOdh(
'GET /api/config',
mockDashboardConfig({
disableStorageClasses: true,
}),
);
initInterceptors({});
clusterStorage.visit('test-project');
const clusterStorageRow = clusterStorage.getClusterStorageRow('Test Storage');
clusterStorageRow.findStorageClassColumn().should('not.exist');
clusterStorageRow.shouldHaveStorageTypeValue('Persistent storage');
clusterStorageRow.findConnectedWorkbenches().should('have.text', 'No connections');
clusterStorageRow.toggleExpandableContent();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import * as React from 'react';
import { Alert, AlertActionCloseButton } from '@patternfly/react-core';
import { Table } from '~/components/table';
import { PersistentVolumeClaimKind } from '~/k8sTypes';
import DeletePVCModal from '~/pages/projects/pvc/DeletePVCModal';
import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas';
import { getStorageClassConfig } from '~/pages/storageClasses/utils';
import useStorageClasses from '~/concepts/k8s/useStorageClasses';
import StorageTableRow from './StorageTableRow';
import { columns } from './data';
import ManageStorageModal from './ManageStorageModal';
import { StorageTableData } from './types';

type StorageTableProps = {
pvcs: PersistentVolumeClaimKind[];
Expand All @@ -15,20 +20,64 @@ type StorageTableProps = {
const StorageTable: React.FC<StorageTableProps> = ({ pvcs, refresh, onAddPVC }) => {
const [deleteStorage, setDeleteStorage] = React.useState<PersistentVolumeClaimKind | undefined>();
const [editPVC, setEditPVC] = React.useState<PersistentVolumeClaimKind | undefined>();
const isStorageClassesAvailable = useIsAreaAvailable(SupportedArea.STORAGE_CLASSES).status;
const [storageClasses, storageClassesLoaded] = useStorageClasses();
const [alertDismissed, setAlertDismissed] = React.useState<boolean>(false);
const storageTableData: StorageTableData[] = pvcs.map((pvc) => ({
pvc,
storageClass: storageClasses.find((sc) => sc.metadata.name === pvc.spec.storageClassName),
Gkrumbach07 marked this conversation as resolved.
Show resolved Hide resolved
}));
const isDeprecatedAlert = React.useMemo(
() =>
storageClassesLoaded &&
storageTableData.some(
(data) => !data.storageClass || !getStorageClassConfig(data.storageClass)?.isEnabled,
),
[storageClassesLoaded, storageTableData],
);
const shouldShowAlert = isDeprecatedAlert && !alertDismissed && isStorageClassesAvailable;

const getStorageColumns = () => {
let storageColumns = columns;

if (!isStorageClassesAvailable) {
storageColumns = columns.filter((column) => column.field !== 'storage');
}
return storageColumns;
};

return (
<>
{shouldShowAlert && (
<Alert
data-testid="storage-class-deprecated-alert"
variant="warning"
isInline
title="Deprecated storage class"
actionClose={
<AlertActionCloseButton
data-testid="storage-class-deprecated-alert-close-button"
onClose={() => setAlertDismissed(true)}
/>
}
>
A storage class has been deprecated by your administrator, but the cluster storage using
it is still active. If you want to migrate your data to cluster storage instance using a
different storage class, contact your administrator.
</Alert>
)}
<Table
data={pvcs}
columns={columns}
data={storageTableData}
columns={getStorageColumns()}
disableRowRenderSupport
data-testid="storage-table"
variant="compact"
rowRenderer={(pvc, i) => (
rowRenderer={(data, i) => (
<StorageTableRow
key={pvc.metadata.uid}
key={data.pvc.metadata.uid}
rowIndex={i}
obj={pvc}
obj={data}
storageClassesLoaded={storageClassesLoaded}
onEditPVC={setEditPVC}
onDeletePVC={setDeleteStorage}
onAddPVC={onAddPVC}
Expand Down
Loading
Loading