]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/feedback: make tracker URL configurable and stop hitting live tracker in tests 68297/head
authorKefu Chai <k.chai@proxmox.com>
Fri, 10 Apr 2026 04:41:00 +0000 (12:41 +0800)
committerKefu Chai <k.chai@proxmox.com>
Fri, 10 Apr 2026 04:54:35 +0000 (12:54 +0800)
The feedback module hard-coded tracker.ceph.com as the Ceph issue
tracker hostname. This meant the dashboard test
test_issue_tracker_create_with_invalid_key actually performed an
HTTPS POST against the live production tracker on every CI run.
Whenever tracker.ceph.com returned an unexpected response (5xx,
gateway error, transient failure), the test would fail on
completely unrelated pull requests.

Add a runtime module option 'mgr/feedback/tracker_url' that
controls which host the CephTrackerClient talks to. The default
keeps the existing behaviour (tracker.ceph.com). The dashboard
test now sets this option to an unreachable hostname in setUpClass
so create_issue fails fast with a ConnectionError instead of
depending on tracker.ceph.com being reachable.

Combined with the prior commit's error-handling fix, the test now
deterministically verifies that any tracker failure is surfaced as
HTTP 400, without requiring network access.

Signed-off-by: Kefu Chai <k.chai@proxmox.com>
qa/tasks/mgr/dashboard/test_feedback.py
src/pybind/mgr/feedback/module.py
src/pybind/mgr/feedback/service.py

index 1a5a66154e100e803773efeb5045899372bf99e8..a395adf4865d278e618360d61888e04b8deecee9 100644 (file)
@@ -7,6 +7,14 @@ class FeedbackTest(MgrModuleTestCase):
     def setUpClass(cls):
         super().setUpClass()
         cls._ceph_cmd(['mgr', 'module', 'enable', 'feedback'], wait=3)
+        # Point the feedback module at an unreachable host so the test
+        # does not depend on tracker.ceph.com being available. Any
+        # create_issue call will fail fast with a ConnectionError
+        # (a RequestException subclass) which the dashboard controller
+        # is expected to surface as HTTP 400.
+        cls._ceph_cmd(['config', 'set', 'mgr',
+                       'mgr/feedback/tracker_url',
+                       'invalid.example.invalid'])
         cls._get(
             '/api/mgr/module',
             retries=5,
index 6c76e0d5a92643657bf8b919987571365afd67e4..9d1e0ae66f1d0a97a101402800071030ef8aa82a 100644 (file)
@@ -9,7 +9,7 @@ from requests.exceptions import RequestException
 
 from .cli import FeedbackCLICommand
 
-from mgr_module import HandleCommandResult, MgrModule
+from mgr_module import HandleCommandResult, MgrModule, Option
 import errno
 
 from .service import CephTrackerClient
@@ -19,6 +19,15 @@ from .model import Feedback
 class FeedbackModule(MgrModule):
     CLICommand = FeedbackCLICommand
 
+    MODULE_OPTIONS = [
+        Option(
+            name='tracker_url',
+            type='str',
+            default='tracker.ceph.com',
+            desc='Hostname of the Ceph issue tracker (Redmine) instance',
+            runtime=True),
+    ]
+
     # there are CLI commands we implement
     @FeedbackCLICommand.Read('feedback set api-key')
     def _cmd_feedback_set_api_key(self, key: str) -> HandleCommandResult:
@@ -60,7 +69,7 @@ class FeedbackModule(MgrModule):
         """
         Fetch issue list
         """
-        tracker_client = CephTrackerClient()
+        tracker_client = CephTrackerClient(self.get_module_option('tracker_url'))
         try:
             response = tracker_client.list_issues()
         except Exception:
@@ -83,7 +92,7 @@ class FeedbackModule(MgrModule):
                 return HandleCommandResult(stderr='Issue tracker key is not set. Set key with `ceph set issue_key <your_key>`')
         except Exception as error:
             return HandleCommandResult(stderr=f'Error in retreiving issue tracker API key: {error}')
-        tracker_client = CephTrackerClient()
+        tracker_client = CephTrackerClient(self.get_module_option('tracker_url'))
         try:
             response = tracker_client.create_issue(feedback, current_api_key)
         except RequestException as error:
@@ -121,13 +130,13 @@ class FeedbackModule(MgrModule):
         return 'Successfully deleted API key'
 
     def get_issues(self):
-        tracker_client = CephTrackerClient()
+        tracker_client = CephTrackerClient(self.get_module_option('tracker_url'))
         return tracker_client.list_issues()
 
     def validate_and_create_issue(self, project: str, tracker: str, subject: str, description: str, api_key=None):
         feedback = Feedback(Feedback.Project[project].value,
                                 Feedback.TrackerType[tracker].value, subject, description)
-        tracker_client = CephTrackerClient()
+        tracker_client = CephTrackerClient(self.get_module_option('tracker_url'))
         stored_api_key = self.get_store('api_key')
         try:
             if api_key:
index dc8c6b64a6d5a1fed34d3583d932768f58586e73..75db80f8a3d7be64aae4585903e4f74f017980d2 100644 (file)
@@ -6,12 +6,14 @@ from requests.exceptions import RequestException
 
 from .model import Feedback
 
-class config:
-    url = 'tracker.ceph.com'
-    port = 443
+DEFAULT_TRACKER_URL = 'tracker.ceph.com'
+
 
 class CephTrackerClient():
 
+    def __init__(self, tracker_url: str = DEFAULT_TRACKER_URL):
+        self.tracker_url = tracker_url
+
     def list_issues(self):
         '''
         Fetch an issue from the Ceph Issue tracker
@@ -20,7 +22,7 @@ class CephTrackerClient():
             'Content-Type': 'application/json',
         }
         response = requests.get(
-            f'https://{config.url}/issues.json', headers=headers)
+            f'https://{self.tracker_url}/issues.json', headers=headers)
         if not response.ok:
             if response.status_code == 404:
                 raise FileNotFoundError
@@ -40,7 +42,7 @@ class CephTrackerClient():
             raise Exception("Ceph Tracker API Key not set")
         data = json.dumps(feedback.as_dict())
         response = requests.post(
-            f'https://{config.url}/projects/{feedback.project_id}/issues.json',
+            f'https://{self.tracker_url}/projects/{feedback.project_id}/issues.json',
             headers=headers, data=data)
         if not response.ok:
             if response.status_code == 401: