From a938b0f9b6d24c2d698cce6819f76ff6f00a626c Mon Sep 17 00:00:00 2001 From: axolotle Date: Tue, 18 Apr 2023 20:15:25 +0200 Subject: [PATCH] configpanel: update _apply --- src/app.py | 14 +++++++--- src/domain.py | 56 +++++++++++++++++----------------------- src/settings.py | 42 ++++++++++++++++-------------- src/utils/configpanel.py | 26 +++++++++++++------ 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/src/app.py b/src/app.py index 5336dd18e..9f55e39ef 100644 --- a/src/app.py +++ b/src/app.py @@ -29,7 +29,7 @@ import subprocess import tempfile import copy from collections import OrderedDict -from typing import TYPE_CHECKING, List, Tuple, Dict, Any, Iterator, Optional +from typing import TYPE_CHECKING, List, Tuple, Dict, Any, Iterator, Optional, Union from packaging import version from moulinette import Moulinette, m18n @@ -75,7 +75,10 @@ from yunohost.app_catalog import ( # noqa ) if TYPE_CHECKING: + from pydantic.typing import AbstractSetIntStr, MappingIntStrAny + from yunohost.utils.configpanel import ConfigPanelModel, RawSettings + from yunohost.utils.form import FormModel logger = getActionLogger("yunohost.app") @@ -1884,8 +1887,13 @@ class AppConfigPanel(ConfigPanel): def _get_raw_settings(self, config: "ConfigPanelModel") -> "RawSettings": return self._call_config_script("show") - def _apply(self): - env = {key: str(value) for key, value in self.new_values.items()} + def _apply( + self, + form: "FormModel", + previous_settings: dict[str, Any], + exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None, + ): + env = {key: str(value) for key, value in form.dict().items()} return_content = self._call_config_script("apply", env=env) # If the script returned validation error diff --git a/src/domain.py b/src/domain.py index 292a25b0b..f69d1f709 100644 --- a/src/domain.py +++ b/src/domain.py @@ -18,7 +18,7 @@ # import os import time -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Any, List, Optional, Union from collections import OrderedDict from moulinette import m18n, Moulinette @@ -39,7 +39,10 @@ from yunohost.utils.error import YunohostError, YunohostValidationError from yunohost.log import is_unit_operation if TYPE_CHECKING: + from pydantic.typing import AbstractSetIntStr, MappingIntStrAny + from yunohost.utils.configpanel import RawConfig + from yunohost.utils.form import FormModel logger = getActionLogger("yunohost.domain") @@ -597,53 +600,42 @@ class DomainConfigPanel(ConfigPanel): return raw_config - def _apply(self): - if ( - "default_app" in self.future_values - and self.future_values["default_app"] != self.values["default_app"] - ): + def _apply( + self, + form: "FormModel", + previous_settings: dict[str, Any], + exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None, + ): + next_settings = { + k: v for k, v in form.dict().items() if previous_settings.get(k) != v + } + + if "default_app" in next_settings: from yunohost.app import app_ssowatconf, app_map if "/" in app_map(raw=True).get(self.entity, {}): raise YunohostValidationError( "app_make_default_location_already_used", - app=self.future_values["default_app"], + app=next_settings["default_app"], domain=self.entity, other_app=app_map(raw=True)[self.entity]["/"]["id"], ) - super()._apply() + super()._apply(form, previous_settings) # Reload ssowat if default app changed - if ( - "default_app" in self.future_values - and self.future_values["default_app"] != self.values["default_app"] - ): + if "default_app" in next_settings: app_ssowatconf() - stuff_to_regen_conf = [] - if ( - "xmpp" in self.future_values - and self.future_values["xmpp"] != self.values["xmpp"] - ): - stuff_to_regen_conf.append("nginx") - stuff_to_regen_conf.append("metronome") + stuff_to_regen_conf = set() + if "xmpp" in next_settings: + stuff_to_regen_conf.update({"nginx", "metronome"}) - if ( - "mail_in" in self.future_values - and self.future_values["mail_in"] != self.values["mail_in"] - ) or ( - "mail_out" in self.future_values - and self.future_values["mail_out"] != self.values["mail_out"] - ): - if "nginx" not in stuff_to_regen_conf: - stuff_to_regen_conf.append("nginx") - stuff_to_regen_conf.append("postfix") - stuff_to_regen_conf.append("dovecot") - stuff_to_regen_conf.append("rspamd") + if "mail_in" in next_settings or "mail_out" in next_settings: + stuff_to_regen_conf.update({"nginx", "postfix", "dovecot", "rspamd"}) if stuff_to_regen_conf: - regen_conf(names=stuff_to_regen_conf) + regen_conf(names=list(stuff_to_regen_conf)) def domain_action_run(domain, action, args=None): diff --git a/src/settings.py b/src/settings.py index 730ad4d00..1b45d3493 100644 --- a/src/settings.py +++ b/src/settings.py @@ -31,12 +31,15 @@ from yunohost.log import is_unit_operation from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings if TYPE_CHECKING: + from pydantic.typing import AbstractSetIntStr, MappingIntStrAny + from yunohost.utils.configpanel import ( ConfigPanelGetMode, ConfigPanelModel, RawConfig, RawSettings, ) + from yunohost.utils.form import FormModel logger = getActionLogger("yunohost.settings") @@ -217,19 +220,15 @@ class SettingsConfigPanel(ConfigPanel): return raw_settings - def _apply(self): - root_password = self.new_values.pop("root_password", None) - root_password_confirm = self.new_values.pop("root_password_confirm", None) - passwordless_sudo = self.new_values.pop("passwordless_sudo", None) - - self.values = { - k: v for k, v in self.values.items() if k not in self.virtual_settings - } - self.new_values = { - k: v for k, v in self.new_values.items() if k not in self.virtual_settings - } - - assert all(v not in self.future_values for v in self.virtual_settings) + def _apply( + self, + form: "FormModel", + previous_settings: dict[str, Any], + exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None, + ): + root_password = form.get("root_password", None) + root_password_confirm = form.get("root_password_confirm", None) + passwordless_sudo = form.get("passwordless_sudo", None) if root_password and root_password.strip(): if root_password != root_password_confirm: @@ -248,15 +247,20 @@ class SettingsConfigPanel(ConfigPanel): {"sudoOption": ["!authenticate"] if passwordless_sudo else []}, ) - super()._apply() - - settings = { - k: v for k, v in self.future_values.items() if self.values.get(k) != v + # First save settings except virtual + default ones + super()._apply(form, previous_settings, exclude=self.virtual_settings) + next_settings = { + k: v + for k, v in form.dict(exclude=self.virtual_settings).items() + if previous_settings.get(k) != v } - for setting_name, value in settings.items(): + + for setting_name, value in next_settings.items(): try: + # FIXME not sure to understand why we need the previous value if + # updated_settings has already been filtered trigger_post_change_hook( - setting_name, self.values.get(setting_name), value + setting_name, previous_settings.get(setting_name), value ) except Exception as e: logger.error(f"Post-change hook for setting failed : {e}") diff --git a/src/utils/configpanel.py b/src/utils/configpanel.py index f4260d8af..4ada07b9b 100644 --- a/src/utils/configpanel.py +++ b/src/utils/configpanel.py @@ -47,6 +47,7 @@ from yunohost.utils.i18n import _value_for_locale if TYPE_CHECKING: from pydantic.fields import ModelField + from pydantic.typing import AbstractSetIntStr, MappingIntStrAny from yunohost.utils.form import FormModel, Hooks @@ -681,21 +682,30 @@ class ConfigPanel: return settings - def _apply(self): + def _apply( + self, + form: "FormModel", + previous_settings: dict[str, Any], + exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None, + ) -> dict[str, Any]: + """ + Save settings in yaml file. + If `save_mode` is `"diff"` (which is the default), only values that are + different from their default value will be saved. + """ logger.info("Saving the new configuration...") + dir_path = os.path.dirname(os.path.realpath(self.save_path)) if not os.path.exists(dir_path): mkdir(dir_path, mode=0o700) - values_to_save = self.future_values - if self.save_mode == "diff": - defaults = self._get_default_values() - values_to_save = { - k: v for k, v in values_to_save.items() if defaults.get(k) != v - } + exclude_defaults = self.save_mode == "diff" + settings = form.dict(exclude_defaults=exclude_defaults, exclude=exclude) # type: ignore # Save the settings to the .yaml file - write_to_yaml(self.save_path, values_to_save) + write_to_yaml(self.save_path, settings) + + return settings def _reload_services(self): from yunohost.service import service_reload_or_restart