From f66110240eeba07c75a8bcbb7e4b261c8320316c Mon Sep 17 00:00:00 2001 From: Bipul Adhikari Date: Mon, 10 Oct 2022 18:31:08 +0545 Subject: [PATCH] Write e2e tests for Compression Signed-off-by: Bipul Adhikari --- cypress/mocks/compression.ts | 137 ++++++++++++++++++++++++++++++ cypress/support.ts | 20 +++++ cypress/tests/compression.spec.ts | 59 +++++++++++++ cypress/views/block-pool.ts | 2 +- cypress/views/common.ts | 8 +- 5 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 cypress/mocks/compression.ts create mode 100644 cypress/tests/compression.spec.ts diff --git a/cypress/mocks/compression.ts b/cypress/mocks/compression.ts new file mode 100644 index 000000000..4bdd59756 --- /dev/null +++ b/cypress/mocks/compression.ts @@ -0,0 +1,137 @@ +export const compressionPool = { + apiVersion: 'ceph.rook.io/v1', + kind: 'CephBlockPool', + metadata: { name: 'plasma-reactor-pool', namespace: 'openshift-storage' }, + spec: { + compressionMode: 'aggressive', + deviceClass: 'ssd', + failureDomain: 'zone', + parameters: { compression_mode: 'aggressive' }, + replicated: { size: 2 }, + }, +}; + +export const compressionStorageClass = { + kind: 'StorageClass', + apiVersion: 'storage.k8s.io/v1', + metadata: { + name: 'compression-class', + }, + provisioner: 'openshift-storage.rbd.csi.ceph.com', + parameters: { + 'csi.storage.k8s.io/fstype': 'ext4', + 'csi.storage.k8s.io/provisioner-secret-namespace': 'openshift-storage', + 'csi.storage.k8s.io/provisioner-secret-name': 'rook-csi-rbd-provisioner', + 'csi.storage.k8s.io/node-stage-secret-name': 'rook-csi-rbd-node', + 'csi.storage.k8s.io/controller-expand-secret-name': + 'rook-csi-rbd-provisioner', + imageFormat: '2', + clusterID: 'openshift-storage', + imageFeatures: 'layering,deep-flatten,exclusive-lock,object-map,fast-diff', + 'csi.storage.k8s.io/controller-expand-secret-namespace': + 'openshift-storage', + pool: 'plasma-reactor-pool', + 'csi.storage.k8s.io/node-stage-secret-namespace': 'openshift-storage', + }, + reclaimPolicy: 'Delete', + allowVolumeExpansion: true, + volumeBindingMode: 'Immediate', +}; + +export const fioPVC = { + kind: 'PersistentVolumeClaim', + apiVersion: 'v1', + metadata: { + name: 'fio-claim', + }, + spec: { + storageClassName: 'compression-class', + accessModes: ['ReadWriteOnce'], + volumeMode: 'Filesystem', + resources: { + requests: { + storage: '5Gi', + }, + }, + }, +}; + +export const fioConfig = `kind: ConfigMap +apiVersion: v1 +metadata: + name: fio-job-config +data: + fio.job: |- + [global] + ioengine=psync + direct=1 + buffered=0 + size=1G + iodepth=1000 + numjobs=2 + group_reporting + refill_buffers + rwmixread=80 + norandommap + randrepeat=0 + percentage_random=0 + bs=512K + buffer_compress_percentage=50 + rw=read + [testjob] +`; + +export const fioJob = { + apiVersion: 'batch/v1', + kind: 'Job', + metadata: { + name: 'fio', + labels: { + app: 'fio', + }, + }, + spec: { + template: { + spec: { + containers: [ + { + name: 'fio', + image: 'quay.io/badhikar/fio:latest', + command: ['sh'], + args: [ + '-c', + // eslint-disable-next-line no-template-curly-in-string + 'echo ${HOSTNAME} && mkdir -p /scratch/${HOSTNAME} && fio /configs/fio.job --eta=never --directory=/scratch/${HOSTNAME}', + ], + volumeMounts: [ + { + name: 'fio-config-vol', + mountPath: '/configs', + }, + { + name: 'fio-data', + mountPath: '/scratch', + }, + ], + imagePullPolicy: 'Always', + }, + ], + restartPolicy: 'OnFailure', + volumes: [ + { + name: 'fio-config-vol', + configMap: { + name: 'fio-job-config', + }, + }, + { + name: 'fio-data', + persistentVolumeClaim: { + claimName: 'fio-claim', + }, + }, + ], + }, + }, + }, +}; diff --git a/cypress/support.ts b/cypress/support.ts index 83703e63e..3d61cead5 100644 --- a/cypress/support.ts +++ b/cypress/support.ts @@ -2,11 +2,13 @@ import { CLUSTER_NAMESPACE, STORAGE_SYSTEM_NAME, OCS_SC_STATE } from './consts'; import './support/selectors'; import './support/login'; +import { commandPoll } from './views/common'; declare global { namespace Cypress { interface Chainable { install(encrypted?: boolean): Chainable; + enableToolboxPod(): void; } } } @@ -87,3 +89,21 @@ Cypress.Commands.add('install', () => { } }); }); + +Cypress.Commands.add('enableToolboxPod', () => { + cy.exec( + `oc patch ocsinitialization ocsinit -n openshift-storage --type json --patch '[{ "op": "replace", "path": "/spec/enableCephTools", "value": true }]'` + ); + // wait for the pod CR to be registered + // eslint-disable-next-line cypress/no-unnecessary-waiting + commandPoll( + 'oc get pod -l "app=rook-ceph-tools" -n openshift-storage', + null, + false, + 30, + 0 + ); + cy.exec( + `oc wait --for=condition=ready pod -l "app=rook-ceph-tools" -n openshift-storage` + ); +}); diff --git a/cypress/tests/compression.spec.ts b/cypress/tests/compression.spec.ts new file mode 100644 index 000000000..7d52c6076 --- /dev/null +++ b/cypress/tests/compression.spec.ts @@ -0,0 +1,59 @@ +import { + compressionPool, + compressionStorageClass, + fioConfig, + fioJob, + fioPVC, +} from '../mocks/compression'; +import { navigateToBlockPool } from '../views/block-pool'; +import { commandPoll } from '../views/common'; + +describe('Test Pool Compression', () => { + before(() => { + cy.login(); + cy.visit('/'); + cy.install(); + cy.enableToolboxPod(); + cy.exec(`echo '${JSON.stringify(compressionPool)}' | oc apply -f -`); + cy.exec( + `echo '${JSON.stringify(compressionStorageClass)}' | oc apply -f -` + ); + cy.exec(`echo '${fioConfig}' | oc apply -f -`); + cy.exec(`echo '${JSON.stringify(fioPVC)}' | oc apply -f -`); + cy.exec(`echo '${JSON.stringify(fioJob)}' | oc apply -f -`); + commandPoll('oc get job fio', null, false, 30, 0); + cy.exec(`oc wait --for=condition=complete job fio`, { timeout: 120000 }); + }); + + after(() => { + cy.exec(`echo ${JSON.stringify(fioJob)} | oc delete -f -`); + cy.exec(`echo ${JSON.stringify(fioPVC)} | oc delete -f -`); + cy.exec(`echo ${JSON.stringify(fioConfig)} | oc delete -f -`); + cy.exec(`echo ${JSON.stringify(compressionPool)} | oc delete -f -`); + cy.exec(`echo ${JSON.stringify(compressionStorageClass)} | oc delete -f -`); + cy.logout(); + }); + + it('Tests compression statistics are correct on the BlockPool list Page', () => { + navigateToBlockPool(); + cy.byTestID('name-filter-input').type(compressionPool.metadata.name); + // TableData does not support data-test ids + // eslint-disable-next-line cypress/require-data-selectors + cy.get('#compressionStatus').should('have.text', 'Enabled'); + cy.exec( + `oc exec -it $(oc get pods -n openshift-storage -l app=rook-ceph-tools -o name) -n openshift-storage -- /usr/bin/rados df -f json` + ).then(({ stdout }) => { + const poolData = JSON.parse(stdout).pools; + const plasmaPool = poolData.find( + (pool) => pool.name === compressionPool.metadata.name + ); + const compressionSavings = plasmaPool.compress_bytes_used; + // TableData does not support data-test ids + // eslint-disable-next-line cypress/require-data-selectors + cy.get('#compressionSavings').should( + 'have.text', + (compressionSavings / 1024 ** 2).toFixed(2) + ); + }); + }); +}); diff --git a/cypress/views/block-pool.ts b/cypress/views/block-pool.ts index b614e3765..4b333d4d2 100644 --- a/cypress/views/block-pool.ts +++ b/cypress/views/block-pool.ts @@ -20,7 +20,7 @@ export const poolMessage: { export const navigateToBlockPool = () => { ODFCommon.visitStorageDashboard(); - cy.byLegacyTestID('horizontal-link-Storage Systems').click(); + cy.byTestID('horizontal-link-Storage Systems').click(); cy.byLegacyTestID('item-filter').type('ocs-storagecluster-storagesystem'); cy.byTestRows('resource-row').get('td a').first().click(); cy.byLegacyTestID('horizontal-link-BlockPools').click(); diff --git a/cypress/views/common.ts b/cypress/views/common.ts index 9b81bd315..8a81d85a6 100644 --- a/cypress/views/common.ts +++ b/cypress/views/common.ts @@ -3,6 +3,7 @@ export const commonFlows = { cy.clickNavLink(['Storage', 'Data Foundation']); //cy.byTestOperatorRow('OpenShift Data Foundation').click(); }, + // eslint-disable-next-line cypress/require-data-selectors checkAll: () => cy.get('input[name=check-all]'), }; @@ -10,10 +11,11 @@ export const commandPoll = ( cmd: string, expected: string, failOnNonZeroExit: boolean = true, - retry: number = 300 + retry: number = 300, + expectedCode?: number ) => { cy.exec(cmd, { failOnNonZeroExit }).then((res) => { - if (res.stdout === expected) { + if (res.stdout === expected || expectedCode === res.code) { assert(true); return; } @@ -22,6 +24,6 @@ export const commandPoll = ( return; } - commandPoll(cmd, expected, failOnNonZeroExit, retry - 1); + commandPoll(cmd, expected, failOnNonZeroExit, retry - 1, expectedCode); }); };