-
Notifications
You must be signed in to change notification settings - Fork 3.3k
[App Service] Fix #32290: az functionapp config appsettings set --slot-settings: Fix the command failure to update existing slot settings
#32291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
3e1176b
a5e185a
40e0045
f0f7e9d
7dc68fd
ce9d520
5be2f8d
301c400
261a86b
47a6108
f65e501
36ba1ca
71ed94f
ff33936
39f759e
4ed5e9a
5f445af
a916b4b
fdf6f2e
4c27292
cef992e
2446b64
0ae0770
8e63318
6f88a7f
b871eef
988fca4
4ff1ce1
ec9dee7
f97e57c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -511,60 +511,87 @@ def update_app_settings_functionapp(cmd, resource_group_name, name, settings=Non | |||||
| return update_app_settings(cmd, resource_group_name, name, settings, slot, slot_settings) | ||||||
|
|
||||||
|
|
||||||
| def _parse_json_setting(s, result, slot_result, setting_type): | ||||||
| """ | ||||||
| Parse JSON format settings. | ||||||
|
|
||||||
| Parameters: | ||||||
| s (str): The input string containing JSON-formatted settings. | ||||||
| result (dict): A dictionary to store the parsed key-value pairs from the settings. | ||||||
| slot_result (dict): A dictionary to store slot setting flags for each key. | ||||||
| setting_type (str): The type of settings being parsed, either "SlotSettings" or "Settings". | ||||||
|
|
||||||
| Returns: | ||||||
| bool: True if parsing was successful, False otherwise. | ||||||
| """ | ||||||
| try: | ||||||
| temp = shell_safe_json_parse(s) | ||||||
| if isinstance(temp, list): # Accept the output of the "list" command | ||||||
| for t in temp: | ||||||
| if 'slotSetting' in t.keys(): | ||||||
| slot_result[t['name']] = t['slotSetting'] | ||||||
| elif setting_type == "SlotSettings": | ||||||
| slot_result[t['name']] = True | ||||||
| result[t['name']] = t['value'] | ||||||
| else: | ||||||
| # Handle JSON objects: setting_type is either "SlotSettings" or "Settings" | ||||||
| # Different logic needed for slot settings vs regular settings | ||||||
| if setting_type == "SlotSettings": | ||||||
| # For slot settings JSON objects, add values to result and mark as slot settings | ||||||
| result.update(temp) | ||||||
| for key in temp: | ||||||
| slot_result[key] = True | ||||||
| else: | ||||||
| # For regular settings JSON objects, add values to result only | ||||||
| result.update(temp) | ||||||
| return True | ||||||
| except InvalidArgumentValueError: | ||||||
| return False | ||||||
|
|
||||||
|
|
||||||
| def update_app_settings(cmd, resource_group_name, name, settings=None, slot=None, slot_settings=None): | ||||||
| if not settings and not slot_settings: | ||||||
| raise MutuallyExclusiveArgumentError('Usage Error: --settings |--slot-settings') | ||||||
| raise MutuallyExclusiveArgumentError('Please provide either --settings or --slot-settings parameter.') | ||||||
|
|
||||||
| settings = settings or [] | ||||||
| slot_settings = slot_settings or [] | ||||||
|
|
||||||
| app_settings = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, | ||||||
| 'list_application_settings', slot) | ||||||
| result, slot_result = {}, {} | ||||||
| # pylint: disable=too-many-nested-blocks | ||||||
| for src, dest, setting_type in [(settings, result, "Settings"), (slot_settings, slot_result, "SlotSettings")]: | ||||||
|
|
||||||
| for src, setting_type in [(settings, "Settings"), (slot_settings, "SlotSettings")]: | ||||||
| for s in src: | ||||||
| # Check if this looks like a simple key=value pair without JSON/dict syntax | ||||||
| # If so, parse it directly to avoid unnecessary warnings from ast.literal_eval | ||||||
| if ('=' in s and not s.lstrip().startswith(('{"', "[", "{")) and | ||||||
| not s.startswith('@')): # @ indicates file input | ||||||
| try: | ||||||
| setting_name, value = s.split('=', 1) | ||||||
| dest[setting_name] = value | ||||||
| continue | ||||||
| except ValueError: | ||||||
| pass # Fall back to JSON parsing if split fails | ||||||
| # Try simple key=value parsing first | ||||||
| if '=' in s and not s.lstrip().startswith(('{"', "[", "{")) and not s.startswith('@'): | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The '{"' is redundant ... it always matches if "{" matches. I also don't see how it adds clarity, so I would remove it. But it is also not something that this PR introduces, it has been there before, so I personally wouldn't consider this a blocker. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| k, v = s.split('=', 1) | ||||||
| result[k] = v | ||||||
| if setting_type == "SlotSettings": | ||||||
| slot_result[k] = True | ||||||
| continue | ||||||
|
|
||||||
| # Try JSON parsing | ||||||
| if _parse_json_setting(s, result, slot_result, setting_type): | ||||||
| continue | ||||||
|
|
||||||
| # Fallback to key=value parsing with error handling | ||||||
jcassanji-southworks marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| try: | ||||||
| temp = shell_safe_json_parse(s) | ||||||
| if isinstance(temp, list): # a bit messy, but we'd like accept the output of the "list" command | ||||||
| for t in temp: | ||||||
| if 'slotSetting' in t.keys(): | ||||||
| slot_result[t['name']] = t['slotSetting'] | ||||||
| elif setting_type == "SlotSettings": | ||||||
| slot_result[t['name']] = True | ||||||
| result[t['name']] = t['value'] | ||||||
| else: | ||||||
| dest.update(temp) | ||||||
| except CLIError: | ||||||
| setting_name, value = s.split('=', 1) | ||||||
| dest[setting_name] = value | ||||||
| result.update(dest) | ||||||
| k, v = s.split('=', 1) | ||||||
| except ValueError as ex: | ||||||
| raise InvalidArgumentValueError( | ||||||
| f"Invalid setting format: '{s}'. Expected 'key=value' format or valid JSON.", | ||||||
| recommendation="Use 'key=value' format or provide valid JSON like '{\"key\": \"value\"}'." | ||||||
| ) from ex | ||||||
|
|
||||||
| result[k] = v | ||||||
| if setting_type == "SlotSettings": | ||||||
| slot_result[k] = True | ||||||
jcassanji-southworks marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
|
||||||
| for setting_name, value in result.items(): | ||||||
| app_settings.properties[setting_name] = value | ||||||
| client = web_client_factory(cmd.cli_ctx) | ||||||
|
|
||||||
|
|
||||||
| # TODO: Centauri currently return wrong payload for update appsettings, remove this once backend has the fix. | ||||||
| if is_centauri_functionapp(cmd, resource_group_name, name): | ||||||
| update_application_settings_polling(cmd, resource_group_name, name, app_settings, slot, client) | ||||||
| result = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'list_application_settings', slot) | ||||||
| else: | ||||||
| result = _generic_settings_operation(cmd.cli_ctx, resource_group_name, name, | ||||||
| 'update_application_settings', | ||||||
| app_settings, slot, client) | ||||||
|
|
||||||
| # Process slot configurations before updating application settings to ensure proper configuration order. | ||||||
jcassanji-southworks marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| app_settings_slot_cfg_names = [] | ||||||
| if slot_result: | ||||||
| slot_cfg_names = client.web_apps.list_slot_configuration_names(resource_group_name, name) | ||||||
|
|
@@ -578,6 +605,15 @@ def update_app_settings(cmd, resource_group_name, name, settings=None, slot=None | |||||
| app_settings_slot_cfg_names = slot_cfg_names.app_setting_names | ||||||
| client.web_apps.update_slot_configuration_names(resource_group_name, name, slot_cfg_names) | ||||||
|
|
||||||
| # TODO: Centauri currently return wrong payload for update appsettings, remove this once backend has the fix. | ||||||
| if is_centauri_functionapp(cmd, resource_group_name, name): | ||||||
| update_application_settings_polling(cmd, resource_group_name, name, app_settings, slot, client) | ||||||
| result = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'list_application_settings', slot) | ||||||
| else: | ||||||
| result = _generic_settings_operation(cmd.cli_ctx, resource_group_name, name, | ||||||
| 'update_application_settings', | ||||||
| app_settings, slot, client) | ||||||
|
|
||||||
| return _build_app_settings_output(result.properties, app_settings_slot_cfg_names, redact=True) | ||||||
|
|
||||||
|
|
||||||
|
|
@@ -597,7 +633,7 @@ def update_application_settings_polling(cmd, resource_group_name, name, app_sett | |||||
| time.sleep(5) | ||||||
| r = send_raw_request(cmd.cli_ctx, method='get', url=poll_url) | ||||||
| else: | ||||||
| raise CLIError(ex) | ||||||
| raise AzureResponseError(f"Failed to update application settings: {str(ex)}") from ex | ||||||
|
|
||||||
|
|
||||||
| def add_azure_storage_account(cmd, resource_group_name, name, custom_id, storage_type, account_name, | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.