From b658dc35d46712c84c52bdc299fd0274e9e1bf70 Mon Sep 17 00:00:00 2001 From: Avan Thakkar Date: Wed, 28 Apr 2021 00:51:28 +0530 Subject: [PATCH] mgr/dashboard: fix bucket versioning when locking is enabled Fixes: https://tracker.ceph.com/issues/50545 Signed-off-by: Avan Thakkar (cherry picked from commit e90ce333c69725f870acc6665c5e9bb4aa33de3f) --- qa/tasks/mgr/dashboard/test_rgw.py | 10 +++++ src/pybind/mgr/dashboard/controllers/rgw.py | 6 ++- .../integration/rgw/buckets.e2e-spec.ts | 18 +++++++++ .../cypress/integration/rgw/buckets.po.ts | 37 ++++++++++++++++++- .../rgw-bucket-form.component.ts | 3 ++ 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/qa/tasks/mgr/dashboard/test_rgw.py b/qa/tasks/mgr/dashboard/test_rgw.py index 5029c12d32259..f545c74832906 100644 --- a/qa/tasks/mgr/dashboard/test_rgw.py +++ b/qa/tasks/mgr/dashboard/test_rgw.py @@ -433,6 +433,16 @@ class RgwBucketTest(RgwTestCase): self.assertEqual(data['lock_retention_period_days'], 15) self.assertEqual(data['lock_retention_period_years'], 0) self.assertStatus(200) + + # Update: Disabling bucket versioning should fail if object locking enabled + self._put('/api/rgw/bucket/teuth-test-bucket', + params={ + 'bucket_id': data['id'], + 'uid': 'teuth-test-user', + 'versioning_state': 'Suspended' + }) + self.assertStatus(409) + # Delete self._delete('/api/rgw/bucket/teuth-test-bucket') self.assertStatus(204) diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index ba0490a6283dc..bd395f47315ae 100644 --- a/src/pybind/mgr/dashboard/controllers/rgw.py +++ b/src/pybind/mgr/dashboard/controllers/rgw.py @@ -300,12 +300,16 @@ class RgwBucket(RgwRESTController): uid_tenant = uid[:uid.find('$')] if uid.find('$') >= 0 else None bucket_name = RgwBucket.get_s3_bucket_name(bucket, uid_tenant) + locking = self._get_locking(uid, daemon_name, bucket_name) if versioning_state: + if versioning_state == 'Suspended' and locking['lock_enabled']: + raise DashboardException(msg='Bucket versioning cannot be disabled/suspended ' + 'on buckets with object lock enabled ', + http_status_code=409, component='rgw') self._set_versioning(uid, daemon_name, bucket_name, versioning_state, mfa_delete, mfa_token_serial, mfa_token_pin) # Update locking if it is enabled. - locking = self._get_locking(uid, daemon_name, bucket_name) if locking['lock_enabled']: self._set_locking(uid, daemon_name, bucket_name, lock_mode, lock_retention_period_days, diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts index 737c112682153..75ff8c7c277b1 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.e2e-spec.ts @@ -35,6 +35,24 @@ describe('RGW buckets page', () => { it('should delete bucket', () => { buckets.delete(bucket_name); }); + + it('should create bucket with object locking enabled', () => { + buckets.navigateTo('create'); + buckets.create( + bucket_name, + '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', + 'default-placement', + true + ); + buckets.getFirstTableCell(bucket_name).should('exist'); + }); + + it('should not allow to edit versioning if object locking is enabled', () => { + buckets.edit(bucket_name, 'dev', true); + buckets.getDataTables().should('contain.text', 'dev'); + + buckets.delete(bucket_name); + }); }); describe('Invalid Input in Create and Edit tests', () => { diff --git a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.po.ts b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.po.ts index 7d0b46c256e72..6183ae25790cc 100644 --- a/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.po.ts +++ b/src/pybind/mgr/dashboard/frontend/cypress/integration/rgw/buckets.po.ts @@ -19,8 +19,12 @@ export class BucketsPageHelper extends PageHelper { return this.selectOption('placement-target', placementTarget); } + private selectLockMode(lockMode: string) { + return this.selectOption('lock_mode', lockMode); + } + @PageHelper.restrictTo(pages.create.url) - create(name: string, owner: string, placementTarget: string) { + create(name: string, owner: string, placementTarget: string, isLocking = false) { // Enter in bucket name cy.get('#bid').type(name); @@ -32,6 +36,15 @@ export class BucketsPageHelper extends PageHelper { this.selectPlacementTarget(placementTarget); cy.get('#placement-target').should('have.class', 'ng-valid'); + if (isLocking) { + cy.get('#lock_enabled').click({ force: true }); + // Select lock mode: + this.selectLockMode('Compliance'); + cy.get('#lock_mode').should('have.class', 'ng-valid'); + cy.get('#lock_retention_period_days').type('3'); + cy.get('#lock_retention_period_years').type('0'); + } + // Click the create button and wait for bucket to be made cy.contains('button', 'Create Bucket').click(); @@ -39,12 +52,32 @@ export class BucketsPageHelper extends PageHelper { } @PageHelper.restrictTo(pages.index.url) - edit(name: string, new_owner: string) { + edit(name: string, new_owner: string, isLocking = false) { this.navigateEdit(name); cy.get('input[name=placement-target]').should('have.value', 'default-placement'); this.selectOwner(new_owner); + // If object locking is enabled versioning shouldn't be visible + if (isLocking) { + cy.get('input[id=versioning]').should('be.disabled'); + cy.contains('button', 'Edit Bucket').click(); + + // wait to be back on buckets page with table visible and click + this.getExpandCollapseElement(name).click(); + + // check its details table for edited owner field + cy.get('.table.table-striped.table-bordered') + .first() + .should('contains.text', new_owner) + .as('bucketDataTable'); + + // Check versioning enabled: + cy.get('@bucketDataTable').find('tr').its(2).find('td').last().should('have.text', new_owner); + cy.get('@bucketDataTable').find('tr').its(11).find('td').last().as('versioningValueCell'); + + return cy.get('@versioningValueCell').should('have.text', this.versioningStateEnabled); + } // Enable versioning cy.get('input[id=versioning]').should('not.be.checked'); cy.get('label[for=versioning]').click(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts index 8bf17755d67a3..1c51d701d12d7 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-form/rgw-bucket-form.component.ts @@ -147,6 +147,9 @@ export class RgwBucketFormComponent extends CdForm implements OnInit { this.isVersioningAlreadyEnabled = this.isVersioningEnabled; this.isMfaDeleteAlreadyEnabled = this.isMfaDeleteEnabled; this.setMfaDeleteValidators(); + if (value['lock_enabled']) { + this.bucketForm.controls['versioning'].disable(); + } } } -- 2.39.5