UI has an async validator which calls the GET bucket API to make sure
the bucket name doesn't exist, but the proxy
was not properly handling the http_status_codes which results in raising
a massive traceback in logs whenever you type things in the bucket name
field. So handling that gracefully by capturing the proper status codes
for both RequestException and DashboardException
BEFORE
```
File "/usr/share/ceph/mgr/dashboard/services/exception.py", line 47, in dashboard_exception_handler
return handler(*args, **kwargs)
File "/lib/python3.9/site-packages/cherrypy/_cpdispatch.py", line 54, in _call_
return self.callable(*self.args, **self.kwargs)
File "/usr/share/ceph/mgr/dashboard/controllers/_base_controller.py", line 263, in inner
ret = func(*args, **kwargs)
File "/usr/share/ceph/mgr/dashboard/controllers/_rest_controller.py", line 193, in wrapper
return func(*vpath, **params)
File "/usr/share/ceph/mgr/dashboard/controllers/rgw.py", line 357, in get
result = self.proxy(daemon_name, 'GET', 'bucket', {'bucket': bucket})
File "/usr/share/ceph/mgr/dashboard/controllers/rgw.py", line 213, in proxy
raise DashboardException(e, http_status_code=http_status_code, component='rgw')
dashboard.exceptions.DashboardException: RGW REST API failed request with status code 404
(b'{"Code":"NoSuchBucket","Message":"","RequestId":"tx00000f14e08c1af0d5615-006'
b'
71f54a7-3bc6-default","HostId":"3bc6-default-default"}')
2024-10-28T09:08:55.990+0000
7f89e045b640 0 [dashboard INFO request] [::ffff:10.74.18.122:51853] [GET] [500] [0.007s] [admin] [200.0B] /api/rgw/bucket/bucket-das
```
AFTER
```
Jul 08 09:28:28 ceph-node-00 ceph-mgr[2243]: [dashboard ERROR dashboard.rest_client] RGW REST API failed GET req status: 404
Jul 08 09:28:28 ceph-node-00 ceph-mgr[2243]: [dashboard INFO dashboard.services.exception] Dashboard Exception: RGW REST API failed request with status code 404
(b'{"Code":"NoSuchBucket","Message":"","RequestId":"tx00000c029a81e5d154844-006'
b'
a4e183c-14251-default","HostId":"14251-default-default"}')
Jul 08 09:28:28 ceph-node-00 ceph-mgr[2243]: [dashboard INFO dashboard.tools] [::ffff:192.168.100.1:60408] [GET] [404] [0.078s] [admin] [200.0B] /api/rgw/bucket/testsa
```
Fixes: https://tracker.ceph.com/issues/78038
Signed-off-by: Nizamudeen A <nia@redhat.com>
self._delete('/api/rgw/user/teuth-test-user')
self.assertStatus(204)
self.get_rgw_user('teuth-test-user')
- self.assertStatus(500)
+ self.assertStatus(404)
resp = self.jsonBody()
self.assertIn('detail', resp)
self.assertIn('failed request with status code 404', resp['detail'])
self._delete('/api/rgw/user/test01$teuth-test-user')
self.assertStatus(204)
self.get_rgw_user('test01$teuth-test-user')
- self.assertStatus(500)
+ self.assertStatus(404)
resp = self.jsonBody()
self.assertIn('detail', resp)
self.assertIn('failed request with status code 404', resp['detail'])
result = json_str_to_object(result)
return result
except (DashboardException, RequestException) as e:
- http_status_code = e.status if isinstance(e, DashboardException) else 500
+ response = getattr(e, 'response', None)
+ if isinstance(e, RequestException) and response is not None:
+ http_status_code = getattr(response, 'status_code', 500)
+ else:
+ http_status_code = getattr(e, 'status_code', getattr(e, 'status', None)) or 500
raise DashboardException(e, http_status_code=http_status_code, component='rgw')
from unittest.mock import Mock, call, patch
from .. import mgr
-from ..controllers.rgw import Rgw, RgwDaemon, RgwTopic, RgwUser
+from ..controllers.rgw import Rgw, RgwBucket, RgwDaemon, RgwTopic, RgwUser
from ..rest_client import RequestException
from ..services.rgw_client import RgwClient, RgwMultisite
from ..tests import ControllerTestCase, RgwStub
result = controller.delete(key='RGW22222222222222222:HttpTest')
mock_delete_topic.assert_called_with(key='RGW22222222222222222:HttpTest')
self.assertEqual(result, None)
+
+
+class RgwBucketControllerTestCase(ControllerTestCase):
+ @classmethod
+ def setup_server(cls):
+ cls.setup_controllers([RgwBucket], '/test')
+
+ @patch('dashboard.services.rgw_client.RgwClient.admin_instance')
+ def test_get_bucket_not_found(self, mock_admin_instance):
+ mock_instance = Mock()
+ mock_admin_instance.return_value = mock_instance
+
+ mock_response = Mock()
+ mock_response.status_code = 404
+ mock_exception = RequestException('RGW REST API failed request')
+ mock_exception.response = mock_response
+ mock_instance.proxy.side_effect = mock_exception
+
+ self._get('/test/api/rgw/bucket/i-do-not-exist')
+ self.assertStatus(404)
+
+ @patch('dashboard.services.rgw_client.RgwClient.admin_instance')
+ def test_get_bucket_not_found_with_fallback_attribute(self, mock_admin_instance):
+
+ mock_instance = Mock()
+ mock_admin_instance.return_value = mock_instance
+
+ # mock that lacks a .response but has a direct .status_code attribute.
+ mock_exception = RequestException('RGW REST API failed request')
+ mock_exception.status_code = 404
+
+ mock_instance.proxy.side_effect = mock_exception
+
+ self._get('/test/api/rgw/bucket/i-do-not-exist')
+ self.assertStatus(404)
+
+ @patch('dashboard.services.rgw_client.RgwClient.admin_instance')
+ def test_get_bucket_server_error(self, mock_admin_instance):
+ mock_instance = Mock()
+ mock_admin_instance.return_value = mock_instance
+
+ mock_exception = RequestException('Internal Server Error')
+ mock_exception.status_code = 500
+
+ mock_instance.proxy.side_effect = mock_exception
+
+ self._get('/test/api/rgw/bucket/i-do-not-exist')
+ self.assertStatus(500)