configpanel: update _apply

This commit is contained in:
axolotle 2023-04-18 20:15:25 +02:00
parent 7a60703ef5
commit 5f9ea58313
4 changed files with 81 additions and 88 deletions

View file

@ -27,7 +27,7 @@ import subprocess
import tempfile import tempfile
import copy import copy
from collections import OrderedDict 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 packaging import version
from logging import getLogger from logging import getLogger
from pathlib import Path from pathlib import Path
@ -73,7 +73,10 @@ from yunohost.app_catalog import ( # noqa
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
from yunohost.utils.configpanel import ConfigPanelModel, RawSettings from yunohost.utils.configpanel import ConfigPanelModel, RawSettings
from yunohost.utils.form import FormModel
logger = getLogger("yunohost.app") logger = getLogger("yunohost.app")
@ -1809,8 +1812,13 @@ class AppConfigPanel(ConfigPanel):
def _get_raw_settings(self, config: "ConfigPanelModel") -> "RawSettings": def _get_raw_settings(self, config: "ConfigPanelModel") -> "RawSettings":
return self._call_config_script("show") return self._call_config_script("show")
def _apply(self): def _apply(
env = {key: str(value) for key, value in self.new_values.items()} 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) return_content = self._call_config_script("apply", env=env)
# If the script returned validation error # If the script returned validation error

View file

@ -19,7 +19,7 @@
import os import os
import time import time
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, List, Optional from typing import TYPE_CHECKING, Any, List, Optional, Union
from collections import OrderedDict from collections import OrderedDict
from logging import getLogger from logging import getLogger
@ -48,7 +48,10 @@ from yunohost.utils.dns import is_yunohost_dyndns_domain
from yunohost.log import is_unit_operation from yunohost.log import is_unit_operation
if TYPE_CHECKING: if TYPE_CHECKING:
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
from yunohost.utils.configpanel import RawConfig from yunohost.utils.configpanel import RawConfig
from yunohost.utils.form import FormModel
logger = getLogger("yunohost.domain") logger = getLogger("yunohost.domain")
@ -669,7 +672,7 @@ class DomainConfigPanel(ConfigPanel):
# Portal settings are only available on "topest" domains # Portal settings are only available on "topest" domains
if _get_parent_domain_of(self.entity, topest=True) is not None: if _get_parent_domain_of(self.entity, topest=True) is not None:
del toml["feature"]["portal"] del raw_config["feature"]["portal"]
# Optimize wether or not to load the DNS section, # Optimize wether or not to load the DNS section,
# e.g. we don't want to trigger the whole _get_registary_config_section # e.g. we don't want to trigger the whole _get_registary_config_section
@ -712,17 +715,23 @@ class DomainConfigPanel(ConfigPanel):
return raw_config return raw_config
def _apply(self): def _apply(
if ( self,
"default_app" in self.future_values form: "FormModel",
and self.future_values["default_app"] != self.values["default_app"] 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 from yunohost.app import app_ssowatconf, app_map
if "/" in app_map(raw=True).get(self.entity, {}): if "/" in app_map(raw=True).get(self.entity, {}):
raise YunohostValidationError( raise YunohostValidationError(
"app_make_default_location_already_used", "app_make_default_location_already_used",
app=self.future_values["default_app"], app=next_settings["default_app"],
domain=self.entity, domain=self.entity,
other_app=app_map(raw=True)[self.entity]["/"]["id"], other_app=app_map(raw=True)[self.entity]["/"]["id"],
) )
@ -735,8 +744,7 @@ class DomainConfigPanel(ConfigPanel):
"portal_theme", "portal_theme",
] ]
if _get_parent_domain_of(self.entity, topest=True) is None and any( if _get_parent_domain_of(self.entity, topest=True) is None and any(
option in self.future_values option in next_settings
and self.new_values[option] != self.values.get(option)
for option in portal_options for option in portal_options
): ):
from yunohost.portal import PORTAL_SETTINGS_DIR from yunohost.portal import PORTAL_SETTINGS_DIR
@ -744,9 +752,8 @@ class DomainConfigPanel(ConfigPanel):
# Portal options are also saved in a `domain.portal.yml` file # Portal options are also saved in a `domain.portal.yml` file
# that can be read by the portal API. # that can be read by the portal API.
# FIXME remove those from the config panel saved values? # FIXME remove those from the config panel saved values?
portal_values = {
option: self.future_values[option] for option in portal_options portal_values = form.dict(include=portal_options)
}
portal_settings_path = Path(f"{PORTAL_SETTINGS_DIR}/{self.entity}.json") portal_settings_path = Path(f"{PORTAL_SETTINGS_DIR}/{self.entity}.json")
portal_settings = {"apps": {}} portal_settings = {"apps": {}}
@ -760,38 +767,21 @@ class DomainConfigPanel(ConfigPanel):
str(portal_settings_path), portal_settings, sort_keys=True, indent=4 str(portal_settings_path), portal_settings, sort_keys=True, indent=4
) )
super()._apply() super()._apply(form, previous_settings)
# Reload ssowat if default app changed # Reload ssowat if default app changed
if ( if "default_app" in next_settings:
"default_app" in self.future_values
and self.future_values["default_app"] != self.values["default_app"]
):
app_ssowatconf() app_ssowatconf()
stuff_to_regen_conf = [] stuff_to_regen_conf = set()
if ( if "xmpp" in next_settings:
"xmpp" in self.future_values stuff_to_regen_conf.update({"nginx", "metronome"})
and self.future_values["xmpp"] != self.values["xmpp"]
):
stuff_to_regen_conf.append("nginx")
stuff_to_regen_conf.append("metronome")
if ( if "mail_in" in next_settings or "mail_out" in next_settings:
"mail_in" in self.future_values stuff_to_regen_conf.update({"nginx", "postfix", "dovecot", "rspamd"})
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 stuff_to_regen_conf: 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): def domain_action_run(domain, action, args=None):

View file

@ -31,12 +31,15 @@ from yunohost.log import is_unit_operation
from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings from yunohost.utils.legacy import translate_legacy_settings_to_configpanel_settings
if TYPE_CHECKING: if TYPE_CHECKING:
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
from yunohost.utils.configpanel import ( from yunohost.utils.configpanel import (
ConfigPanelGetMode, ConfigPanelGetMode,
ConfigPanelModel, ConfigPanelModel,
RawConfig, RawConfig,
RawSettings, RawSettings,
) )
from yunohost.utils.form import FormModel
logger = getLogger("yunohost.settings") logger = getLogger("yunohost.settings")
@ -217,19 +220,15 @@ class SettingsConfigPanel(ConfigPanel):
return raw_settings return raw_settings
def _apply(self): def _apply(
root_password = self.new_values.pop("root_password", None) self,
root_password_confirm = self.new_values.pop("root_password_confirm", None) form: "FormModel",
passwordless_sudo = self.new_values.pop("passwordless_sudo", None) previous_settings: dict[str, Any],
exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None] = None,
self.values = { ):
k: v for k, v in self.values.items() if k not in self.virtual_settings root_password = form.get("root_password", None)
} root_password_confirm = form.get("root_password_confirm", None)
self.new_values = { passwordless_sudo = form.get("passwordless_sudo", None)
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)
if root_password and root_password.strip(): if root_password and root_password.strip():
if root_password != root_password_confirm: if root_password != root_password_confirm:
@ -248,15 +247,20 @@ class SettingsConfigPanel(ConfigPanel):
{"sudoOption": ["!authenticate"] if passwordless_sudo else []}, {"sudoOption": ["!authenticate"] if passwordless_sudo else []},
) )
super()._apply() # First save settings except virtual + default ones
super()._apply(form, previous_settings, exclude=self.virtual_settings)
settings = { next_settings = {
k: v for k, v in self.future_values.items() if self.values.get(k) != v 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: try:
# FIXME not sure to understand why we need the previous value if
# updated_settings has already been filtered
trigger_post_change_hook( trigger_post_change_hook(
setting_name, self.values.get(setting_name), value setting_name, previous_settings.get(setting_name), value
) )
except Exception as e: except Exception as e:
logger.error(f"Post-change hook for setting failed : {e}") logger.error(f"Post-change hook for setting failed : {e}")

View file

@ -47,6 +47,8 @@ from yunohost.utils.i18n import _value_for_locale
if TYPE_CHECKING: if TYPE_CHECKING:
from pydantic.fields import ModelField from pydantic.fields import ModelField
from pydantic.typing import AbstractSetIntStr, MappingIntStrAny
from yunohost.utils.form import FormModel, Hooks from yunohost.utils.form import FormModel, Hooks
logger = getLogger("yunohost.configpanel") logger = getLogger("yunohost.configpanel")
@ -678,43 +680,32 @@ class ConfigPanel:
hooks=hooks, hooks=hooks,
) )
<<<<<<< HEAD
# Check and ask unanswered questions
prefilled_answers = self.args.copy()
prefilled_answers.update(self.new_values)
questions = ask_questions_and_parse_answers(
{question["id"]: question for question in section["options"]},
prefilled_answers=prefilled_answers,
current_values=self.values,
hooks=self.hooks,
)
self.new_values.update(
{
question.id: question.value
for question in questions
if not question.readonly and question.value is not None
}
)
=======
return settings return settings
>>>>>>> be777b928 (configpanel: update _ask)
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...") logger.info("Saving the new configuration...")
dir_path = os.path.dirname(os.path.realpath(self.save_path)) dir_path = os.path.dirname(os.path.realpath(self.save_path))
if not os.path.exists(dir_path): if not os.path.exists(dir_path):
mkdir(dir_path, mode=0o700) mkdir(dir_path, mode=0o700)
values_to_save = self.future_values exclude_defaults = self.save_mode == "diff"
if self.save_mode == "diff": settings = form.dict(exclude_defaults=exclude_defaults, exclude=exclude) # type: ignore
defaults = self._get_default_values()
values_to_save = {
k: v for k, v in values_to_save.items() if defaults.get(k) != v
}
# Save the settings to the .yaml file # 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): def _reload_services(self):
from yunohost.service import service_reload_or_restart from yunohost.service import service_reload_or_restart