]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: disable deleting bucket with objects
authorNaman Munet <naman.munet@ibm.com>
Wed, 19 Feb 2025 15:14:26 +0000 (20:44 +0530)
committerNaman Munet <naman.munet@ibm.com>
Fri, 28 Feb 2025 09:36:11 +0000 (15:06 +0530)
Fixes: https://tracker.ceph.com/issues/70078
Signed-off-by: Naman Munet <naman.munet@ibm.com>
(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
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-bucket-list/rgw-bucket-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rgw-bucket.service.ts
src/pybind/mgr/dashboard/openapi.yaml

index f2c6acf9f464fae8e3bbc9b3ba3e5537b97bebd2..46c192323f93c4a6753ffa0e3c275f3537f49555 100644 (file)
@@ -457,11 +457,18 @@ class RgwBucket(RgwRESTController):
             self._set_acl(bucket_name, canned_acl, uid, daemon_name)
         return self._append_bid(result)
 
-    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
index 23f77001ecb2e848db2cda879fa5603b14bd123c..3ecb85316c08f1f55c80f6cc3c809e3f219cd941 100644 (file)
@@ -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(
index eaed2c4abac2fd96a4018dba98b02f5c563be85c..6481bb2041d717166551891fc51f21f38253204e 100644 (file)
@@ -97,19 +97,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');
   });
 
index 8dbebff1f61addfca94f77ce8517ba49c740dfe9..0d17c7abdd40d1565b3254a85e86e7f71f473c20 100644 (file)
@@ -184,9 +184,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 });
     });
   }
index 560ed870e6fe31fc655174f1d3306d6aeff35753..14ad6c9e306c73faf9a152e096995ab552256549 100644 (file)
@@ -9759,11 +9759,6 @@ paths:
         required: true
         schema:
           type: string
-      - default: 'true'
-        in: query
-        name: purge_objects
-        schema:
-          type: string
       - allowEmptyValue: true
         in: query
         name: daemon_name