-
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 21 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
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_simple_key_value_setting(s, dest): | ||
| """Parse simple key=value settings format.""" | ||
| 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 | ||
|
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. Slot settings will be added only to |
||
| return True | ||
| except ValueError: | ||
| pass # Fall back to JSON parsing if split fails | ||
| return False | ||
|
|
||
|
|
||
| def _parse_json_setting(s, dest, result, slot_result, setting_type): | ||
| """Parse JSON format settings.""" | ||
jcassanji-southworks marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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" (from line 525 loop) | ||
jcassanji-southworks marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # 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 _parse_fallback_key_value_setting(s, result): | ||
|
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. This function does (almost?) the same thing as _parse_simple_key_value_setting in line 514. It would never succeed if _parse_simple_key_value_setting fails. |
||
| """Parse key=value as fallback when JSON parsing fails.""" | ||
| try: | ||
| setting_name, value = 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[setting_name] = value | ||
|
|
||
|
|
||
| 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 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: | ||
| 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) | ||
| # Try simple key=value parsing first | ||
| if _parse_simple_key_value_setting(s, dest): | ||
| continue | ||
|
|
||
| # Try JSON parsing | ||
| if _parse_json_setting(s, dest, result, slot_result, setting_type): | ||
| continue | ||
|
|
||
| # Fallback to key=value parsing with error handling | ||
| _parse_fallback_key_value_setting(s, result) | ||
|
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. This is only called if _parse_simple_key_value_setting in line 605 returns false. But then this call will also not succeed, except if there is a problem with slot_result, as this function takes result as a parameter instead of slot_result for a slot setting. |
||
|
|
||
| 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. | ||
| 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.