From 308f64d72dd3b138c5e212e1929966a48b37c2d7 Mon Sep 17 00:00:00 2001 From: Naman Munet Date: Wed, 19 Feb 2025 20:44:26 +0530 Subject: [PATCH] mgr/dashboard: disable deleting bucket with objects Fixes: https://tracker.ceph.com/issues/70078 Signed-off-by: Naman Munet (cherry picked from commit 11677c29ee6ee60d9191edfdbfbe37b5308eb45e) Conflicts: src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts --- src/pybind/mgr/dashboard/controllers/rgw.py | 17 ++++++++++++----- .../rgw-bucket-list.component.ts | 10 ++++++++++ .../app/shared/api/rgw-bucket.service.spec.ts | 14 ++------------ .../src/app/shared/api/rgw-bucket.service.ts | 3 +-- src/pybind/mgr/dashboard/openapi.yaml | 5 ----- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/pybind/mgr/dashboard/controllers/rgw.py b/src/pybind/mgr/dashboard/controllers/rgw.py index a1a0d86107d6e..e2eeaff05e0eb 100755 --- a/src/pybind/mgr/dashboard/controllers/rgw.py +++ b/src/pybind/mgr/dashboard/controllers/rgw.py @@ -636,11 +636,18 @@ class RgwBucket(RgwRESTController): self._delete_lifecycle(bucket_name, daemon_name, uid) return self._append_bid(result) if result else None - def delete(self, bucket, purge_objects='true', daemon_name=None): - return self.proxy(daemon_name, 'DELETE', 'bucket', { - 'bucket': bucket, - 'purge-objects': purge_objects - }, json_response=False) + def delete(self, bucket, daemon_name=None): + try: + bucket_info = self.proxy(daemon_name, 'GET', 'bucket', {'bucket': bucket}) + num_objects = bucket_info.get('usage', {}).get('rgw.main', {}).get('num_objects', 0) + if num_objects > 0: + raise DashboardException(msg='Unable to delete bucket"{}" - Bucket is not empty. ' + 'Remove all objects before deletion.'.format(bucket)) + return self.proxy(daemon_name, 'DELETE', 'bucket', { + 'bucket': bucket + }, json_response=False) + except (DashboardException, RequestException) as e: # pragma: no cover + raise DashboardException(e, component='rgw') @RESTController.Collection(method='PUT', path='/setEncryptionConfig') @allow_empty_body diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts index 23f77001ecb2e..3ecb85316c08f 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts @@ -117,12 +117,22 @@ export class RgwBucketListComponent extends ListWithDetails implements OnInit, O permission: 'delete', icon: Icons.destroy, click: () => this.deleteAction(), + disable: () => this.isDeleteDisabled(), name: this.actionLabels.DELETE }; this.tableActions = [addAction, editAction, deleteAction]; this.setTableRefreshTimeout(); } + isDeleteDisabled(): boolean | string { + if (!this.selection.first()) { + return true; + } + return this.selection.first()?.num_objects > 0 + ? $localize`Bucket is not empty. Remove all objects before deletion.` + : false; + } + getBucketList(context: CdTableFetchDataContext) { this.setTableRefreshTimeout(); this.subs.add( diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts index ec0da64df9964..6375cf3d4eacb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts @@ -100,19 +100,9 @@ describe('RgwBucketService', () => { expect(req.request.method).toBe('PUT'); }); - it('should call delete, with purgeObjects = true', () => { + it('should call delete', () => { service.delete('foo').subscribe(); - const req = httpTesting.expectOne( - `api/rgw/bucket/foo?${RgwHelper.DAEMON_QUERY_PARAM}&purge_objects=true` - ); - expect(req.request.method).toBe('DELETE'); - }); - - it('should call delete, with purgeObjects = false', () => { - service.delete('foo', false).subscribe(); - const req = httpTesting.expectOne( - `api/rgw/bucket/foo?${RgwHelper.DAEMON_QUERY_PARAM}&purge_objects=false` - ); + const req = httpTesting.expectOne(`api/rgw/bucket/foo?${RgwHelper.DAEMON_QUERY_PARAM}`); expect(req.request.method).toBe('DELETE'); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts index ed3134f5cae6b..dcac09f5e8e9b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts @@ -190,9 +190,8 @@ export class RgwBucketService extends ApiClient { }); } - delete(bucket: string, purgeObjects = true) { + delete(bucket: string) { return this.rgwDaemonService.request((params: HttpParams) => { - params = params.append('purge_objects', purgeObjects ? 'true' : 'false'); return this.http.delete(`${this.url}/${bucket}`, { params: params }); }); } diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 8d983a8784a2f..38f990f97af34 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -10859,11 +10859,6 @@ paths: required: true schema: type: string - - default: 'true' - in: query - name: purge_objects - schema: - type: string - allowEmptyValue: true in: query name: daemon_name -- 2.39.5