Skip to content

Commit e0a06af

Browse files
authored
Fix-streaming-regressions (#1383)
* Fix crash when exporting with keys and async * Never exit on BAD_REQUEST, NOT_FOUND and UNAUTHORIZED * Fix error when app key does not exists * Async write only if WRITE_CALLBACK is set * Use sync export if format is YAML
1 parent e12b0a4 commit e0a06af

File tree

3 files changed

+66
-24
lines changed

3 files changed

+66
-24
lines changed

cli/config.py

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,19 @@
7575
_WRITE_LOCK = Lock()
7676

7777

78+
_EXPORT_CALLS = {
79+
options.WHAT_SETTINGS: [__JSON_KEY_SETTINGS, platform.export],
80+
options.WHAT_RULES: [__JSON_KEY_RULES, rules.export],
81+
options.WHAT_PROFILES: [__JSON_KEY_PROFILES, qualityprofiles.export],
82+
options.WHAT_GATES: [__JSON_KEY_GATES, qualitygates.export],
83+
options.WHAT_PROJECTS: [__JSON_KEY_PROJECTS, projects.export],
84+
options.WHAT_APPS: [__JSON_KEY_APPS, applications.export],
85+
options.WHAT_PORTFOLIOS: [__JSON_KEY_PORTFOLIOS, portfolios.export],
86+
options.WHAT_USERS: [__JSON_KEY_USERS, users.export],
87+
options.WHAT_GROUPS: [__JSON_KEY_GROUPS, groups.export],
88+
}
89+
90+
7891
def __parse_args(desc):
7992
parser = options.set_common_args(desc)
8093
parser = options.set_key_arg(parser)
@@ -173,8 +186,8 @@ def __convert_for_yaml(json_export: dict[str, any]) -> dict[str, any]:
173186
return json_export
174187

175188

176-
def __export_config(endpoint: platform.Platform, what: list[str], **kwargs) -> None:
177-
"""Exports a platform configuration in a JSON file"""
189+
def __export_config_sync(endpoint: platform.Platform, what: list[str], **kwargs) -> None:
190+
"""Exports config in a synchronous way"""
178191
export_settings = {
179192
"INLINE_LISTS": not kwargs["dontInlineLists"],
180193
"EXPORT_DEFAULTS": kwargs["exportDefaults"],
@@ -186,42 +199,66 @@ def __export_config(endpoint: platform.Platform, what: list[str], **kwargs) -> N
186199
non_existing_projects = [key for key in kwargs[options.KEYS] if not projects.exists(key, endpoint)]
187200
if len(non_existing_projects) > 0:
188201
utilities.exit_fatal(f"Project key(s) '{','.join(non_existing_projects)}' do(es) not exist", errcodes.NO_SUCH_KEY)
202+
log.info("Exporting configuration synchronously from %s", kwargs[options.URL])
203+
key_list = kwargs[options.KEYS]
204+
sq_settings = {__JSON_KEY_PLATFORM: endpoint.basics()}
205+
for what_item, call_data in _EXPORT_CALLS.items():
206+
if what_item not in what:
207+
continue
208+
ndx, func = call_data
209+
try:
210+
sq_settings[ndx] = func(endpoint, export_settings=export_settings, key_list=key_list)
211+
except exceptions.UnsupportedOperation as e:
212+
log.warning(e.message)
213+
except exceptions.ObjectNotFound as e:
214+
log.error(e.message)
215+
sq_settings = utilities.remove_empties(sq_settings)
216+
if not kwargs["dontInlineLists"]:
217+
sq_settings = utilities.inline_lists(sq_settings, exceptions=("conditions",))
218+
__write_export(sq_settings, kwargs[options.REPORT_FILE], kwargs[options.FORMAT])
219+
log.info("Synchronous export of configuration from %s completed", kwargs["url"])
189220

190-
calls = {
191-
options.WHAT_SETTINGS: [__JSON_KEY_SETTINGS, platform.export],
192-
options.WHAT_RULES: [__JSON_KEY_RULES, rules.export],
193-
options.WHAT_PROFILES: [__JSON_KEY_PROFILES, qualityprofiles.export],
194-
options.WHAT_GATES: [__JSON_KEY_GATES, qualitygates.export],
195-
# options.WHAT_PROJECTS: [__JSON_KEY_PROJECTS, projects.export],
196-
options.WHAT_APPS: [__JSON_KEY_APPS, applications.export],
197-
options.WHAT_PORTFOLIOS: [__JSON_KEY_PORTFOLIOS, portfolios.export],
198-
options.WHAT_USERS: [__JSON_KEY_USERS, users.export],
199-
options.WHAT_GROUPS: [__JSON_KEY_GROUPS, groups.export],
200-
}
201221

202-
log.info("Exporting configuration from %s", kwargs[options.URL])
222+
def __export_config_async(endpoint: platform.Platform, what: list[str], **kwargs) -> None:
223+
"""Exports a platform configuration in a JSON file"""
224+
export_settings = {
225+
"INLINE_LISTS": not kwargs["dontInlineLists"],
226+
"EXPORT_DEFAULTS": kwargs["exportDefaults"],
227+
"FULL_EXPORT": kwargs["fullExport"],
228+
"THREADS": kwargs[options.NBR_THREADS],
229+
options.REPORT_FILE: kwargs[options.REPORT_FILE],
230+
"WRITE_CALLBACK": write_project,
231+
}
232+
log.info("Exporting configuration from %s (asynchronously)", kwargs[options.URL])
203233
key_list = kwargs[options.KEYS]
204234
sq_settings = {__JSON_KEY_PLATFORM: endpoint.basics()}
205-
for what_item, call_data in calls.items():
235+
for what_item, call_data in _EXPORT_CALLS.items():
206236
if what_item not in what or what_item == options.WHAT_PROJECTS:
207237
continue
208238
ndx, func = call_data
209239
try:
210-
sq_settings[ndx] = func(endpoint, export_settings=export_settings, key_list=key_list)
240+
sq_settings[ndx] = utilities.remove_empties(func(endpoint, export_settings=export_settings, key_list=key_list))
211241
__write_export(sq_settings, kwargs[options.REPORT_FILE], kwargs[options.FORMAT])
212242
except exceptions.UnsupportedOperation as e:
213243
log.warning(e.message)
214-
sq_settings = utilities.remove_empties(sq_settings)
244+
except exceptions.ObjectNotFound as e:
245+
log.error(e.message)
215246
if not kwargs["dontInlineLists"]:
216247
sq_settings = utilities.inline_lists(sq_settings, exceptions=("conditions",))
248+
__write_export(sq_settings, kwargs[options.REPORT_FILE], kwargs[options.FORMAT])
217249

218-
export_settings["WRITE_CALLBACK"] = write_project
219250
__remove_chars_at_end(kwargs[options.REPORT_FILE], 3)
220251
__add_project_header(kwargs[options.REPORT_FILE])
221-
projects.export(endpoint, export_settings=export_settings, key_list=key_list)
252+
projects.export(endpoint, export_settings=export_settings, key_list=None)
222253
__add_project_footer(kwargs[options.REPORT_FILE])
254+
log.info("Asynchronous export of configuration from %s completed", kwargs["url"])
223255

224-
log.info("Exporting configuration from %s completed", kwargs["url"])
256+
257+
def __export_config(endpoint: platform.Platform, what: list[str], **kwargs) -> None:
258+
if kwargs[options.KEYS] or options.WHAT_PROJECTS not in what or kwargs[options.FORMAT] != "json":
259+
__export_config_sync(endpoint=endpoint, what=what, **kwargs)
260+
else:
261+
__export_config_async(endpoint=endpoint, what=what, **kwargs)
225262

226263

227264
def __import_config(endpoint: platform.Platform, what: list[str], **kwargs) -> None:

sonar/platform.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@
5757
_SONAR_TOOLS_AGENT = f"sonar-tools {version.PACKAGE_VERSION}"
5858
_UPDATE_CENTER = "https://raw.githubusercontent.com/SonarSource/sonar-update-center-properties/master/update-center-source.properties"
5959

60+
_NORMAL_HTTP_ERRORS = (HTTPStatus.UNAUTHORIZED, HTTPStatus.NOT_FOUND, HTTPStatus.BAD_REQUEST)
61+
6062
LTA = None
6163
LATEST = None
6264
_HARDCODED_LTA = (9, 9, 6)
@@ -246,7 +248,7 @@ def __run_request(
246248
self.url = new_url
247249
r.raise_for_status()
248250
except requests.exceptions.HTTPError as e:
249-
if exit_on_error or (r.status_code not in mute and r.status_code == HTTPStatus.UNAUTHORIZED):
251+
if exit_on_error: # or (r.status_code not in mute and r.status_code not in _NORMAL_HTTP_ERRORS):
250252
util.log_and_exit(r)
251253
else:
252254
_, msg = util.http_error(r)

sonar/projects.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,9 +1472,12 @@ def __export_thread(queue: Queue[Project], results: dict[str, str], export_setti
14721472
"""Project export callback function for multitheaded export"""
14731473
while not queue.empty():
14741474
project = queue.get()
1475-
results[project.key] = project.export(export_settings=export_settings)
1476-
export_settings["WRITE_CALLBACK"](results[project.key], export_settings["file"])
1477-
results[project.key].pop("key", None)
1475+
exp_json = project.export(export_settings=export_settings)
1476+
if export_settings.get("WRITE_CALLBACK", None):
1477+
export_settings["WRITE_CALLBACK"](exp_json, export_settings["file"])
1478+
else:
1479+
results[project.key] = exp_json
1480+
results[project.key].pop("key", None)
14781481
with _CLASS_LOCK:
14791482
export_settings["EXPORTED"] += 1
14801483
nb, tot = export_settings["EXPORTED"], export_settings["NBR_PROJECTS"]

0 commit comments

Comments
 (0)