mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge branch 'dev' into uniformize-actionmap-api
This commit is contained in:
commit
00b6af5dcb
47 changed files with 890 additions and 643 deletions
|
@ -53,6 +53,12 @@ root-tests:
|
|||
script:
|
||||
- python3 -m pytest tests
|
||||
|
||||
test-helpers:
|
||||
extends: .test-stage
|
||||
script:
|
||||
- cd tests
|
||||
- bash test_helpers.sh
|
||||
|
||||
test-apps:
|
||||
extends: .test-stage
|
||||
script:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
n_version=7.0.2
|
||||
n_version=7.1.0
|
||||
n_install_dir="/opt/node_n"
|
||||
node_version_path="$n_install_dir/n/versions/node"
|
||||
# N_PREFIX is the directory of n, it needs to be loaded as a environment variable.
|
||||
|
@ -17,7 +17,7 @@ ynh_install_n () {
|
|||
ynh_print_info --message="Installation of N - Node.js version management"
|
||||
# Build an app.src for n
|
||||
echo "SOURCE_URL=https://github.com/tj/n/archive/v${n_version}.tar.gz
|
||||
SOURCE_SUM=fa80a8685f0fb1b4187fc0a1228b44f0ea2f244e063fe8f443b8913ea595af89" > "$YNH_APP_BASEDIR/conf/n.src"
|
||||
SOURCE_SUM=20100f3bc56648cc414717fb7367fcf0e8229dc59a10b0530ccac90042ee0a74" > "$YNH_APP_BASEDIR/conf/n.src"
|
||||
# Download and extract n
|
||||
ynh_setup_source --dest_dir="$n_install_dir/git" --source_id=n
|
||||
# Install n
|
||||
|
|
|
@ -21,6 +21,9 @@ YNH_APP_BASEDIR=$([[ "$(basename $0)" =~ ^backup|restore$ ]] && echo '../setting
|
|||
# Requires YunoHost version 2.6.4 or higher.
|
||||
ynh_exit_properly () {
|
||||
local exit_code=$?
|
||||
|
||||
rm -rf "/var/cache/yunohost/download/"
|
||||
|
||||
if [ "$exit_code" -eq 0 ]; then
|
||||
exit 0 # Exit without error if the script ended correctly
|
||||
fi
|
||||
|
@ -60,9 +63,10 @@ ynh_abort_if_errors () {
|
|||
|
||||
# Download, check integrity, uncompress and patch the source from app.src
|
||||
#
|
||||
# usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id]
|
||||
# usage: ynh_setup_source --dest_dir=dest_dir [--source_id=source_id] [--keep="file1 file2"]
|
||||
# | arg: -d, --dest_dir= - Directory where to setup sources
|
||||
# | arg: -s, --source_id= - Name of the source, defaults to `app`
|
||||
# | arg: -k, --keep= - Space-separated list of files/folders that will be backup/restored in $dest_dir, such as a config file you don't want to overwrite. For example 'conf.json secrets.json logs/'
|
||||
#
|
||||
# This helper will read `conf/${source_id}.src`, download and install the sources.
|
||||
#
|
||||
|
@ -97,13 +101,15 @@ ynh_abort_if_errors () {
|
|||
# Requires YunoHost version 2.6.4 or higher.
|
||||
ynh_setup_source () {
|
||||
# Declare an array to define the options of this helper.
|
||||
local legacy_args=ds
|
||||
local -A args_array=( [d]=dest_dir= [s]=source_id= )
|
||||
local legacy_args=dsk
|
||||
local -A args_array=( [d]=dest_dir= [s]=source_id= [k]=keep= )
|
||||
local dest_dir
|
||||
local source_id
|
||||
local keep
|
||||
# Manage arguments with getopts
|
||||
ynh_handle_getopts_args "$@"
|
||||
source_id="${source_id:-app}" # If the argument is not given, source_id equals "app"
|
||||
source_id="${source_id:-app}"
|
||||
keep="${keep:-}"
|
||||
|
||||
local src_file_path="$YNH_APP_BASEDIR/conf/${source_id}.src"
|
||||
|
||||
|
@ -127,8 +133,13 @@ ynh_setup_source () {
|
|||
src_filename="${source_id}.${src_format}"
|
||||
fi
|
||||
|
||||
|
||||
# (Unused?) mecanism where one can have the file in a special local cache to not have to download it...
|
||||
local local_src="/opt/yunohost-apps-src/${YNH_APP_ID}/${src_filename}"
|
||||
|
||||
mkdir -p /var/cache/yunohost/download/${YNH_APP_ID}/
|
||||
src_filename="/var/cache/yunohost/download/${YNH_APP_ID}/${src_filename}"
|
||||
|
||||
if test -e "$local_src"
|
||||
then
|
||||
cp $local_src $src_filename
|
||||
|
@ -148,9 +159,32 @@ ynh_setup_source () {
|
|||
echo "${src_sum} ${src_filename}" | ${src_sumprg} --check --status \
|
||||
|| ynh_die --message="Corrupt source"
|
||||
|
||||
# Keep files to be backup/restored at the end of the helper
|
||||
# Assuming $dest_dir already exists
|
||||
rm -rf /var/cache/yunohost/files_to_keep_during_setup_source/
|
||||
if [ -n "$keep" ] && [ -e "$dest_dir" ]
|
||||
then
|
||||
local keep_dir=/var/cache/yunohost/files_to_keep_during_setup_source/${YNH_APP_ID}
|
||||
mkdir -p $keep_dir
|
||||
local stuff_to_keep
|
||||
for stuff_to_keep in $keep
|
||||
do
|
||||
if [ -e "$dest_dir/$stuff_to_keep" ]
|
||||
then
|
||||
mkdir --parents "$(dirname "$keep_dir/$stuff_to_keep")"
|
||||
cp --archive "$dest_dir/$stuff_to_keep" "$keep_dir/$stuff_to_keep"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Extract source into the app dir
|
||||
mkdir --parents "$dest_dir"
|
||||
|
||||
if [ -n "${final_path:-}" ] && [ "$dest_dir" == "$final_path" ]
|
||||
then
|
||||
_ynh_apply_default_permissions $dest_dir
|
||||
fi
|
||||
|
||||
if ! "$src_extract"
|
||||
then
|
||||
mv $src_filename $dest_dir
|
||||
|
@ -188,19 +222,41 @@ ynh_setup_source () {
|
|||
fi
|
||||
|
||||
# Apply patches
|
||||
if (( $(find $YNH_CWD/../sources/patches/ -type f -name "${source_id}-*.patch" 2> /dev/null | wc --lines) > "0" ))
|
||||
if [ -d "$YNH_APP_BASEDIR/sources/patches/" ]
|
||||
then
|
||||
(cd "$dest_dir"
|
||||
for p in $YNH_CWD/../sources/patches/${source_id}-*.patch
|
||||
do
|
||||
patch --strip=1 < $p
|
||||
done) || ynh_die --message="Unable to apply patches"
|
||||
local patches_folder=$(realpath $YNH_APP_BASEDIR/sources/patches/)
|
||||
if (( $(find $patches_folder -type f -name "${source_id}-*.patch" 2> /dev/null | wc --lines) > "0" ))
|
||||
then
|
||||
(cd "$dest_dir"
|
||||
for p in $patches_folder/${source_id}-*.patch
|
||||
do
|
||||
echo $p
|
||||
patch --strip=1 < $p
|
||||
done) || ynh_die --message="Unable to apply patches"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add supplementary files
|
||||
if test -e "$YNH_CWD/../sources/extra_files/${source_id}"; then
|
||||
cp --archive $YNH_CWD/../sources/extra_files/$source_id/. "$dest_dir"
|
||||
if test -e "$YNH_APP_BASEDIR/sources/extra_files/${source_id}"; then
|
||||
cp --archive $YNH_APP_BASEDIR/sources/extra_files/$source_id/. "$dest_dir"
|
||||
fi
|
||||
|
||||
# Keep files to be backup/restored at the end of the helper
|
||||
# Assuming $dest_dir already exists
|
||||
if [ -n "$keep" ]
|
||||
then
|
||||
local keep_dir=/var/cache/yunohost/files_to_keep_during_setup_source/${YNH_APP_ID}
|
||||
local stuff_to_keep
|
||||
for stuff_to_keep in $keep
|
||||
do
|
||||
if [ -e "$keep_dir/$stuff_to_keep" ]
|
||||
then
|
||||
mkdir --parents "$(dirname "$dest_dir/$stuff_to_keep")"
|
||||
cp --archive "$keep_dir/$stuff_to_keep" "$dest_dir/$stuff_to_keep"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
rm -rf /var/cache/yunohost/files_to_keep_during_setup_source/
|
||||
}
|
||||
|
||||
# Curl abstraction to help with POST requests to local pages (such as installation forms)
|
||||
|
@ -315,8 +371,17 @@ ynh_add_config () {
|
|||
|
||||
ynh_backup_if_checksum_is_different --file="$destination"
|
||||
|
||||
# Make sure to set the permissions before we copy the file
|
||||
# This is to cover a case where an attacker could have
|
||||
# created a file beforehand to have control over it
|
||||
# (cp won't overwrite ownership / modes by default...)
|
||||
touch $destination
|
||||
chown root:root $destination
|
||||
chmod 640 $destination
|
||||
|
||||
cp -f "$template_path" "$destination"
|
||||
chown root: "$destination"
|
||||
|
||||
_ynh_apply_default_permissions $destination
|
||||
|
||||
ynh_replace_vars --file="$destination"
|
||||
|
||||
|
@ -672,9 +737,43 @@ ynh_compare_current_package_version() {
|
|||
|
||||
# Check validity of the comparator
|
||||
if [[ ! $comparison =~ (lt|le|eq|ne|ge|gt) ]]; then
|
||||
ynh_die --message="Invialid comparator must be : lt, le, eq, ne, ge, gt"
|
||||
ynh_die --message="Invalid comparator must be : lt, le, eq, ne, ge, gt"
|
||||
fi
|
||||
|
||||
# Return the return value of dpkg --compare-versions
|
||||
dpkg --compare-versions $current_version $comparison $version
|
||||
}
|
||||
|
||||
# Check if we should enforce sane default permissions (= disable rwx for 'others')
|
||||
# on file/folders handled with ynh_setup_source and ynh_add_config
|
||||
#
|
||||
# [internal]
|
||||
#
|
||||
# Having a file others-readable or a folder others-executable(=enterable)
|
||||
# is a security risk comparable to "chmod 777"
|
||||
#
|
||||
# Configuration files may contain secrets. Or even just being able to enter a
|
||||
# folder may allow an attacker to do nasty stuff (maybe a file or subfolder has
|
||||
# some write permission enabled for 'other' and the attacker may edit the
|
||||
# content or create files as leverage for priviledge escalation ...)
|
||||
#
|
||||
# The sane default should be to set ownership to $app:$app.
|
||||
# In specific case, you may want to set the ownership to $app:www-data
|
||||
# for example if nginx needs access to static files.
|
||||
#
|
||||
_ynh_apply_default_permissions() {
|
||||
local target=$1
|
||||
|
||||
local ynh_requirement=$(jq -r '.requirements.yunohost' $YNH_APP_BASEDIR/manifest.json | tr -d '>= ')
|
||||
|
||||
if [ -z "$ynh_requirement" ] || [ "$ynh_requirement" == "null" ] || dpkg --compare-versions $ynh_requirement ge 4.2
|
||||
then
|
||||
chmod o-rwx $target
|
||||
chmod g-w $target
|
||||
chown -R root:root $target
|
||||
if ynh_system_user_exists $app
|
||||
then
|
||||
chown $app:$app $target
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit hook on subcommand error or unset variable
|
||||
set -eu
|
||||
|
||||
# Source YNH helpers
|
||||
source /usr/share/yunohost/helpers
|
||||
|
||||
# Backup destination
|
||||
backup_dir="${1}/conf/cron"
|
||||
|
||||
# Backup the configuration
|
||||
for f in $(ls -1B /etc/cron.d/yunohost* 2> /dev/null); do
|
||||
ynh_backup "$f" "${backup_dir}/${f##*/}"
|
||||
done
|
|
@ -50,6 +50,8 @@ do_init_regen() {
|
|||
chown root:root /etc/ssowat/conf.json.persistent
|
||||
|
||||
mkdir -p /var/cache/yunohost/repo
|
||||
chown root:root /var/cache/yunohost
|
||||
chmod 700 /var/cache/yunohost
|
||||
}
|
||||
|
||||
do_pre_regen() {
|
||||
|
@ -77,13 +79,27 @@ do_pre_regen() {
|
|||
cp services.yml /etc/yunohost/services.yml
|
||||
fi
|
||||
|
||||
mkdir -p $pending_dir/etc/cron.d/
|
||||
mkdir -p $pending_dir/etc/cron.daily/
|
||||
|
||||
# add cron job for diagnosis to be ran at 7h and 19h + a random delay between
|
||||
# 0 and 20min, meant to avoid every instances running their diagnosis at
|
||||
# exactly the same time, which may overload the diagnosis server.
|
||||
mkdir -p $pending_dir/etc/cron.d/
|
||||
cat > $pending_dir/etc/cron.d/yunohost-diagnosis << EOF
|
||||
SHELL=/bin/bash
|
||||
0 7,19 * * * root : YunoHost Automatic Diagnosis; sleep \$((RANDOM\\%1200)); yunohost diagnosis run --email > /dev/null 2>/dev/null || echo "Running the automatic diagnosis failed miserably"
|
||||
EOF
|
||||
|
||||
# Cron job that upgrade the app list everyday
|
||||
cat > $pending_dir/etc/cron.daily/yunohost-fetch-apps-catalog << EOF
|
||||
#!/bin/bash
|
||||
(sleep \$((RANDOM%3600)); yunohost tools update --apps > /dev/null) &
|
||||
EOF
|
||||
|
||||
# Cron job that renew lets encrypt certificates if there's any that needs renewal
|
||||
cat > $pending_dir/etc/cron.daily/yunohost-certificate-renew << EOF
|
||||
#!/bin/bash
|
||||
yunohost domain cert-renew --email
|
||||
EOF
|
||||
|
||||
# If we subscribed to a dyndns domain, add the corresponding cron
|
||||
|
@ -142,6 +158,14 @@ do_post_regen() {
|
|||
find /etc/yunohost/certs/ -type f -exec chmod 640 {} \;
|
||||
find /etc/yunohost/certs/ -type d -exec chmod 750 {} \;
|
||||
|
||||
find /etc/cron.*/yunohost-* -type f -exec chmod 755 {} \;
|
||||
find /etc/cron.d/yunohost-* -type f -exec chmod 644 {} \;
|
||||
find /etc/cron.*/yunohost-* -type f -exec chown root:root {} \;
|
||||
|
||||
chown root:root /var/cache/yunohost
|
||||
chmod 700 /var/cache/yunohost
|
||||
|
||||
|
||||
# Misc configuration / state files
|
||||
chown root:root $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)
|
||||
chmod 600 $(ls /etc/yunohost/{*.yml,*.yaml,*.json,mysql,psql} 2>/dev/null)
|
||||
|
|
|
@ -47,6 +47,12 @@ do_pre_regen() {
|
|||
|
||||
# install / update plain conf files
|
||||
cp plain/* "$nginx_conf_dir"
|
||||
# remove the panel overlay if this is specified in settings
|
||||
panel_overlay=$(yunohost settings get 'ssowat.panel_overlay.enabled')
|
||||
if [ "$panel_overlay" == "false" ] || [ "$panel_overlay" == "False" ]
|
||||
then
|
||||
echo "#" > "${nginx_conf_dir}/yunohost_panel.conf.inc"
|
||||
fi
|
||||
|
||||
# retrieve variables
|
||||
main_domain=$(cat /etc/yunohost/current_host)
|
||||
|
|
|
@ -77,7 +77,7 @@ class SystemResourcesDiagnoser(Diagnoser):
|
|||
|
||||
# Ignore /dev/loop stuff which are ~virtual partitions ? (e.g. mounted to /snap/)
|
||||
disk_partitions = [
|
||||
d for d in disk_partitions if not d.device.startswith("/dev/loop")
|
||||
d for d in disk_partitions if d.mountpoint in ["/", "/var"] or not d.device.startswith("/dev/loop")
|
||||
]
|
||||
|
||||
for disk_partition in disk_partitions:
|
||||
|
@ -139,7 +139,7 @@ class SystemResourcesDiagnoser(Diagnoser):
|
|||
status="ERROR",
|
||||
summary="diagnosis_rootfstotalspace_critical",
|
||||
)
|
||||
if main_space < 14 * GB:
|
||||
elif main_space < 14 * GB:
|
||||
yield dict(
|
||||
meta={"test": "rootfstotalspace"},
|
||||
data={"space": human_size(main_space)},
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
backup_dir="$1/conf/cron"
|
||||
|
||||
cp -a $backup_dir/. /etc/cron.d
|
||||
|
||||
# Restart just in case
|
||||
service cron restart
|
|
@ -6,6 +6,6 @@ location /yunohost/admin/ {
|
|||
default_type text/html;
|
||||
index index.html;
|
||||
|
||||
more_set_headers "Content-Security-Policy: upgrade-insecure-requests; default-src 'self'; connect-src 'self' https://raw.githubusercontent.com https://paste.yunohost.org wss://$host; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; object-src 'none';";
|
||||
more_set_headers "Content-Security-Policy: upgrade-insecure-requests; default-src 'self'; connect-src 'self' https://raw.githubusercontent.com https://paste.yunohost.org wss://$host; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; object-src 'none'; img-src 'self' data:;";
|
||||
more_set_headers "Content-Security-Policy-Report-Only:";
|
||||
}
|
||||
|
|
|
@ -54,7 +54,12 @@ smtpd_tls_loglevel=1
|
|||
|
||||
# -- TLS for outgoing connections
|
||||
# Use TLS if this is supported by the remote SMTP server, otherwise use plaintext.
|
||||
{% if relay_port == "465" %}
|
||||
smtp_tls_wrappermode = yes
|
||||
smtp_tls_security_level = encrypt
|
||||
{% else %}
|
||||
smtp_tls_security_level = may
|
||||
{% endif %}
|
||||
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
||||
smtp_tls_exclude_ciphers = aNULL, MD5, DES, ADH, RC4, 3DES
|
||||
smtp_tls_mandatory_ciphers= high
|
||||
|
|
37
debian/changelog
vendored
37
debian/changelog
vendored
|
@ -1,8 +1,39 @@
|
|||
yunohost (4.2) unstable; urgency=low
|
||||
yunohost (4.2.0) testing; urgency=low
|
||||
|
||||
- Placeholder for 4.2 to satisfy CI / debian build during dev
|
||||
- [mod] Python2 -> Python3 ([#1116](https://github.com/yunohost/yunohost/pull/1116), a97a9df3, 1387dff4, b53859db, f5ab4443, f9478b93, dc6033c3)
|
||||
- [mod] refactoring: Drop legacy-way of passing arguments in hook_exec, prevent exposing secrets in command line args ([#1096](https://github.com/yunohost/yunohost/pull/1096))
|
||||
- [mod] refactoring: use regen_conf instead of service_regen_conf in settings.py (9c11fd58)
|
||||
- [mod] refactoring: More consistent local CA management for simpler postinstall ([#1062](https://github.com/yunohost/yunohost/pull/1062))
|
||||
- [mod] refactoring: init folders during .deb install instead of regen conf ([#1063](https://github.com/yunohost/yunohost/pull/1063))
|
||||
- [mod] refactoring: init ldap before the postinstall ([#1064](https://github.com/yunohost/yunohost/pull/1064))
|
||||
- [mod] refactoring: simpler and more consistent logging initialization ([#1119](https://github.com/yunohost/yunohost/pull/1119), 0884a0c1)
|
||||
- [mod] code-quality: add CI job to auto-format code, fix linter errors ([#1142](https://github.com/yunohost/yunohost/pull/1142), [#1161](https://github.com/yunohost/yunohost/pull/1161), 97f26015, [#1162](https://github.com/yunohost/yunohost/pull/1162))
|
||||
- [mod] misc: Prevent the installation of apache2 ... ([#1148](https://github.com/yunohost/yunohost/pull/1148))
|
||||
- [mod] misc: Drop old cache rules for .ms files, not relevant anymore ([#1150](https://github.com/yunohost/yunohost/pull/1150))
|
||||
- [fix] misc: Abort postinstall if /etc/yunohost/apps ain't empty ([#1147](https://github.com/yunohost/yunohost/pull/1147))
|
||||
- [mod] misc: No need for mysql root password anymore ([#912](https://github.com/YunoHost/yunohost/pull/912))
|
||||
- [fix] app operations: wait for services to finish reloading (4a19a60b)
|
||||
- [enh] ux: Improve error semantic such that the webadmin can autoredirect to the proper log view ([#1077](https://github.com/yunohost/yunohost/pull/1077), [#1187](https://github.com/YunoHost/yunohost/pull/1187))
|
||||
- [mod] cli/api: Misc command and routes renaming / aliasing ([#1146](https://github.com/yunohost/yunohost/pull/1146))
|
||||
- [enh] cli: Add a new "yunohost app search" command ([#1070](https://github.com/yunohost/yunohost/pull/1070))
|
||||
- [enh] cli: Add '--remove-apps' (and '--force') options to "yunohost domain remove" ([#1125](https://github.com/yunohost/yunohost/pull/1125))
|
||||
- [enh] diagnosis: Report low total space for rootfs ([#1145](https://github.com/yunohost/yunohost/pull/1145))
|
||||
- [fix] upnp: Handle port closing ([#1154](https://github.com/yunohost/yunohost/pull/1154))
|
||||
- [fix] dyndns: clean old madness, improve update strategy, improve cron management, delete dyndns key upon domain removal ([#1149](https://github.com/yunohost/yunohost/pull/1149))
|
||||
- [enh] helpers: Adding composer helper ([#1090](https://github.com/yunohost/yunohost/pull/1090))
|
||||
- [enh] helpers: Upgrade n to v7.0.2 ([#1178](https://github.com/yunohost/yunohost/pull/1178))
|
||||
- [enh] helpers: Add multimedia helpers and hooks ([#1129](https://github.com/yunohost/yunohost/pull/1129), 47420c62)
|
||||
- [enh] helpers: Normalize conf template handling for nginx, php-fpm, systemd and fail2ban using ynh_add_config ([#1118](https://github.com/yunohost/yunohost/pull/1118))
|
||||
- [fix] helpers, doc: Update template for the new doc (grav) ([#1167](https://github.com/yunohost/yunohost/pull/1167), [#1168](https://github.com/yunohost/yunohost/pull/1168), 59d3e387)
|
||||
- [enh] helpers: Define YNH_APP_BASEDIR to be able to properly point to conf folder depending on the app script we're running ([#1172](https://github.com/yunohost/yunohost/pull/1172))
|
||||
- [enh] helpers: Use jq / output-as json to get info from yunohost commands instead of scraping with grep ([#1160](https://github.com/yunohost/yunohost/pull/1160))
|
||||
- [fix] helpers: Misc fixes/enh (b85d959d, db93b82b, ce04570b, 07f8d6d7)
|
||||
- [fix] helpers: download ynh_setup_source stuff in /var/cache/yunohost to prevent situations where it ends up in /etc/yunohost/apps/ (d98ec6ce)
|
||||
- [i18n] Translations updated for Catalan, Chinese (Simplified), Czech, Dutch, French, German, Italian, Occitan, Polish
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 20 Jan 2021 05:19:58 +0100
|
||||
Thanks to all contributors <3 ! (Bram, Christian W., Daniel, Dave, Éric G., Félix P., Flavio C., Kay0u, Krzysztof N., ljf, Mathieu M., Miloš K., MrMorals, Nils V.Z., penguin321, ppr, Quentí, Radek S, Scapharnaum, Sébastien M., xaloc33, yalh76, Yifei D.)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 25 Mar 2021 01:00:00 +0100
|
||||
|
||||
yunohost (4.1.7.4) stable; urgency=low
|
||||
|
||||
|
|
|
@ -327,7 +327,7 @@
|
|||
"regenconf_failed": "No s'ha pogut regenerar la configuració per la/les categoria/es : {categories}",
|
||||
"regenconf_pending_applying": "Aplicació de la configuració pendent per la categoria «{category}»...",
|
||||
"restore_already_installed_app": "Una aplicació amb la ID «{app:s}» ja està instal·lada",
|
||||
"restore_app_failed": "No s'ha pogut restaurar {app:s}",
|
||||
"app_restore_failed": "No s'ha pogut restaurar {app:s}: {error:s}",
|
||||
"restore_cleaning_failed": "No s'ha pogut netejar el directori temporal de restauració",
|
||||
"restore_complete": "Restauració completada",
|
||||
"restore_confirm_yunohost_installed": "Esteu segur de voler restaurar un sistema ja instal·lat? [{answers:s}]",
|
||||
|
|
|
@ -96,7 +96,7 @@
|
|||
"port_already_closed": "Der Port {port:d} wurde bereits für {ip_version:s} Verbindungen geschlossen",
|
||||
"port_already_opened": "Der Port {port:d} wird bereits von {ip_version:s} benutzt",
|
||||
"restore_already_installed_app": "Es ist bereits eine App mit der ID '{app:s}' installiet",
|
||||
"restore_app_failed": "App '{app:s}' konnte nicht wiederhergestellt werden",
|
||||
"app_restore_failed": "App '{app:s}' konnte nicht wiederhergestellt werden: {error:s}",
|
||||
"restore_cleaning_failed": "Das temporäre Wiederherstellungsverzeichnis konnte nicht geleert werden",
|
||||
"restore_complete": "Wiederherstellung abgeschlossen",
|
||||
"restore_confirm_yunohost_installed": "Möchtest du die Wiederherstellung wirklich starten? [{answers:s}]",
|
||||
|
@ -605,5 +605,16 @@
|
|||
"postinstall_low_rootfsspace": "Das Root-Filesystem hat insgesamt weniger als 10GB freien Speicherplatz zur Verfügung, was ziemlich besorgniserregend ist! Sie werden sehr bald keinen freien Speicherplatz mehr haben! Für das Root-Filesystem werden mindestens 16GB empfohlen. Wenn Sie YunoHost trotz dieser Warnung installieren wollen, wiederholen Sie den Befehl mit --force-diskspace",
|
||||
"regenconf_up_to_date": "Die Konfiguration ist bereits aktuell für die Kategorie '{category}'",
|
||||
"regenconf_now_managed_by_yunohost": "Die Konfigurationsdatei '{conf}' wird jetzt von YunoHost (Kategorie {category}) verwaltet.",
|
||||
"regenconf_updated": "Konfiguration aktualisiert für '{category}'"
|
||||
"regenconf_updated": "Konfiguration aktualisiert für '{category}'",
|
||||
"regenconf_pending_applying": "Wende die anstehende Konfiguration für die Kategorie {category} an...",
|
||||
"regenconf_failed": "Konnte die Konfiguration für die Kategorie(n) {categories} nicht neu erstellen",
|
||||
"regenconf_dry_pending_applying": "Überprüfe die anstehende Konfiguration, welche für die Kategorie {category}' aktualisiert worden wäre…",
|
||||
"regenconf_would_be_updated": "Die Konfiguration wäre für die Kategorie '{category}' aktualisiert worden",
|
||||
"restore_system_part_failed": "Die Systemteile '{part:s}' konnten nicht wiederhergestellt werden",
|
||||
"restore_removing_tmp_dir_failed": "Ein altes, temporäres Directory konnte nicht entfernt werden",
|
||||
"restore_not_enough_disk_space": "Nicht genug Speicher (Speicher: {free_space:d} B, benötigter Speicher: {needed_space:d} B, Sicherheitspuffer: {margin:d} B)",
|
||||
"restore_may_be_not_enough_disk_space": "Dein System scheint nicht genug Speicherplatz zu haben (frei: {free_space:d} B, benötigter Platz: {needed_space:d} B, Sicherheitspuffer: {margin:d} B)",
|
||||
"restore_extracting": "Packe die benötigten Dateien aus dem Archiv aus…",
|
||||
"restore_already_installed_apps": "Folgende Apps können nicht wiederhergestellt werden, weil sie schon installiert sind: {apps}",
|
||||
"regex_with_only_domain": "Du kannst regex nicht als Domain verwenden, sondern nur als Pfad"
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
"app_requirements_checking": "Checking required packages for {app}...",
|
||||
"app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}",
|
||||
"app_remove_after_failed_install": "Removing the app following the installation failure...",
|
||||
"app_restore_failed": "Could not restore {app:s}: {error:s}",
|
||||
"app_restore_script_failed": "An error occured inside the app restore script",
|
||||
"app_sources_fetch_failed": "Could not fetch sources files, is the URL correct?",
|
||||
"app_start_install": "Installing {app}...",
|
||||
"app_start_remove": "Removing {app}...",
|
||||
|
@ -323,6 +325,7 @@
|
|||
"global_settings_setting_security_postfix_compatibility": "Compatibility vs. security tradeoff for the Postfix server. Affects the ciphers (and other security-related aspects)",
|
||||
"global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discard it and save it in /etc/yunohost/settings-unknown.json",
|
||||
"global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration",
|
||||
"global_settings_setting_ssowat_panel_overlay_enabled": "Enable SSOwat panel overlay",
|
||||
"global_settings_setting_smtp_allow_ipv6": "Allow the use of IPv6 to receive and send mail",
|
||||
"global_settings_setting_smtp_relay_host": "SMTP relay host to use in order to send mail instead of this yunohost instance. Useful if you are in one of this situation: your 25 port is blocked by your ISP or VPS provider, you have a residential IP listed on DUHL, you are not able to configure reverse DNS or this server is not directly exposed on the internet and you want use an other one to send mails.",
|
||||
"global_settings_setting_smtp_relay_port": "SMTP relay port",
|
||||
|
@ -374,6 +377,7 @@
|
|||
"log_app_config_show_panel": "Show the config panel of the '{}' app",
|
||||
"log_app_config_apply": "Apply config to the '{}' app",
|
||||
"log_available_on_yunopaste": "This log is now available via {url}",
|
||||
"log_backup_create": "Create a backup archive",
|
||||
"log_backup_restore_system": "Restore system from a backup archive",
|
||||
"log_backup_restore_app": "Restore '{}' from a backup archive",
|
||||
"log_remove_on_failed_restore": "Remove '{}' after a failed restore from a backup archive",
|
||||
|
@ -419,12 +423,7 @@
|
|||
"migration_description_0017_postgresql_9p6_to_11": "Migrate databases from PostgreSQL 9.6 to 11",
|
||||
"migration_description_0018_xtable_to_nftable": "Migrate old network traffic rules to the new nftable system",
|
||||
"migration_description_0019_extend_permissions_features": "Extend/rework the app permission management system",
|
||||
"migration_0011_create_group": "Creating a group for each user...",
|
||||
"migration_0011_LDAP_update_failed": "Unable to update LDAP. Error: {error:s}",
|
||||
"migration_0011_migrate_permission": "Migrating permissions from apps settings to LDAP...",
|
||||
"migration_0011_update_LDAP_database": "Updating LDAP database...",
|
||||
"migration_0011_update_LDAP_schema": "Updating LDAP schema...",
|
||||
"migration_0011_failed_to_remove_stale_object": "Unable to remove stale object {dn}: {error}",
|
||||
"migration_update_LDAP_schema": "Updating LDAP schema...",
|
||||
"migration_0015_start" : "Starting migration to Buster",
|
||||
"migration_0015_patching_sources_list": "Patching the sources.lists...",
|
||||
"migration_0015_main_upgrade": "Starting main upgrade...",
|
||||
|
@ -527,7 +526,7 @@
|
|||
"regex_with_only_domain": "You can't use a regex for domain, only for path",
|
||||
"restore_already_installed_app": "An app with the ID '{app:s}' is already installed",
|
||||
"restore_already_installed_apps": "The following apps can't be restored because they are already installed: {apps}",
|
||||
"restore_app_failed": "Could not restore {app:s}",
|
||||
"restore_backup_too_old": "This backup archive can not be restored because it comes from a too-old YunoHost version.",
|
||||
"restore_cleaning_failed": "Could not clean up the temporary restoration directory",
|
||||
"restore_complete": "Restoration completed",
|
||||
"restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]",
|
||||
|
|
|
@ -479,7 +479,7 @@
|
|||
"service_restarted": "Servo '{service:s}' rekomencis",
|
||||
"pattern_username": "Devas esti minuskulaj literoj kaj minuskloj nur",
|
||||
"extracting": "Eltirante…",
|
||||
"restore_app_failed": "Ne povis restarigi la programon '{app:s}'",
|
||||
"app_restore_failed": "Ne povis restarigi la programon '{app:s}': {error:s}",
|
||||
"yunohost_configured": "YunoHost nun estas agordita",
|
||||
"certmanager_self_ca_conf_file_not_found": "Ne povis trovi agorddosieron por mem-subskriba aŭtoritato (dosiero: {file:s})",
|
||||
"log_app_remove": "Forigu la aplikon '{}'",
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
"port_already_closed": "El puerto {port:d} ya está cerrado para las conexiones {ip_version:s}",
|
||||
"port_already_opened": "El puerto {port:d} ya está abierto para las conexiones {ip_version:s}",
|
||||
"restore_already_installed_app": "Una aplicación con el ID «{app:s}» ya está instalada",
|
||||
"restore_app_failed": "No se pudo restaurar la aplicación «{app:s}»",
|
||||
"app_restore_failed": "No se pudo restaurar la aplicación «{app:s}»: {error:s}",
|
||||
"restore_cleaning_failed": "No se pudo limpiar el directorio temporal de restauración",
|
||||
"restore_complete": "Restaurada",
|
||||
"restore_confirm_yunohost_installed": "¿Realmente desea restaurar un sistema ya instalado? [{answers:s}]",
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"password_too_simple_1": "Pasahitzak gutxienez 8 karaktere izan behar ditu"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
"port_already_closed": "Le port {port:d} est déjà fermé pour les connexions {ip_version:s}",
|
||||
"port_already_opened": "Le port {port:d} est déjà ouvert pour les connexions {ip_version:s}",
|
||||
"restore_already_installed_app": "Une application est déjà installée avec l’identifiant '{app:s}'",
|
||||
"restore_app_failed": "Impossible de restaurer '{app:s}'",
|
||||
"app_restore_failed": "Impossible de restaurer '{app:s}': {error:s}",
|
||||
"restore_cleaning_failed": "Impossible de nettoyer le dossier temporaire de restauration",
|
||||
"restore_complete": "Restauration terminée",
|
||||
"restore_confirm_yunohost_installed": "Voulez-vous vraiment restaurer un système déjà installé ? [{answers:s}]",
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
"pattern_username": "Caratteri minuscoli alfanumerici o trattini bassi soli",
|
||||
"port_already_closed": "La porta {port:d} è già chiusa per le connessioni {ip_version:s}",
|
||||
"restore_already_installed_app": "Un'applicazione con l'ID '{app:s}' è già installata",
|
||||
"restore_app_failed": "Impossibile ripristinare l'applicazione '{app:s}'",
|
||||
"app_restore_failed": "Impossibile ripristinare l'applicazione '{app:s}': {error:s}",
|
||||
"restore_cleaning_failed": "Impossibile pulire la directory temporanea di ripristino",
|
||||
"restore_complete": "Ripristino completo",
|
||||
"restore_confirm_yunohost_installed": "Sei sicuro di volere ripristinare un sistema già installato? {answers:s}",
|
||||
|
@ -674,5 +674,9 @@
|
|||
"diagnosis_mail_blacklist_reason": "Il motivo della blacklist è: {reason}",
|
||||
"diagnosis_mail_blacklist_listed_by": "Il tuo IP o dominio <code>{item}</code> è nella blacklist {blacklist_name}",
|
||||
"diagnosis_backports_in_sources_list": "Sembra che apt (il package manager) sia configurato per utilizzare le backport del repository. A meno che tu non sappia quello che stai facendo, scoraggiamo fortemente di installare pacchetti tramite esse, perché ci sono alte probabilità di creare conflitti con il tuo sistema.",
|
||||
"diagnosis_basesystem_hardware_model": "Modello server: {model}"
|
||||
"diagnosis_basesystem_hardware_model": "Modello server: {model}",
|
||||
"postinstall_low_rootfsspace": "La radice del filesystem ha uno spazio totale inferiore ai 10 GB, ed è piuttosto preoccupante! Consumerai tutta la memoria molto velocemente! Raccomandiamo di avere almeno 16 GB per la radice del filesystem. Se vuoi installare YunoHost ignorando questo avviso, esegui nuovamente il postinstall con l'argomento --force-diskspace",
|
||||
"domain_remove_confirm_apps_removal": "Rimuovere questo dominio rimuoverà anche le seguenti applicazioni:\n{apps}\n\nSei sicuro di voler continuare? [{answers}]",
|
||||
"diagnosis_rootfstotalspace_critical": "La radice del filesystem ha un totale di solo {space}, ed è piuttosto preoccupante! Probabilmente consumerai tutta la memoria molto velocemente! Raccomandiamo di avere almeno 16 GB per la radice del filesystem.",
|
||||
"diagnosis_rootfstotalspace_warning": "La radice del filesystem ha un totale di solo {space}. Potrebbe non essere un problema, ma stai attento perché potresti consumare tutta la memoria velocemente... Raccomandiamo di avere almeno 16 GB per la radice del filesystem."
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
"pattern_password": "Wachtwoord moet tenminste 3 karakters lang zijn",
|
||||
"port_already_closed": "Poort {port:d} is al gesloten voor {ip_version:s} verbindingen",
|
||||
"port_already_opened": "Poort {port:d} is al open voor {ip_version:s} verbindingen",
|
||||
"restore_app_failed": "De app '{app:s}' kon niet worden terug gezet",
|
||||
"app_restore_failed": "De app '{app:s}' kon niet worden terug gezet: {error:s}",
|
||||
"restore_hook_unavailable": "De herstel-hook '{part:s}' is niet beschikbaar op dit systeem",
|
||||
"service_add_failed": "Kan service '{service:s}' niet toevoegen",
|
||||
"service_already_started": "Service '{service:s}' draait al",
|
||||
|
|
|
@ -170,7 +170,7 @@
|
|||
"port_already_closed": "Lo pòrt {port:d} es ja tampat per las connexions {ip_version:s}",
|
||||
"port_already_opened": "Lo pòrt {port:d} es ja dubèrt per las connexions {ip_version:s}",
|
||||
"restore_already_installed_app": "Una aplicacion es ja installada amb l’id « {app:s} »",
|
||||
"restore_app_failed": "Impossible de restaurar l’aplicacion « {app:s} »",
|
||||
"app_restore_failed": "Impossible de restaurar l’aplicacion « {app:s} »: {error:s}",
|
||||
"backup_ask_for_copying_if_needed": "Volètz far una salvagarda en utilizant {size:s} Mo temporàriament ? (Aqueste biais de far es emplegat perque unes fichièrs an pas pogut èsser preparats amb un metòde mai eficaç.)",
|
||||
"yunohost_not_installed": "YunoHost es pas installat o corrèctament installat. Mercés d’executar « yunohost tools postinstall »",
|
||||
"backup_output_directory_forbidden": "Causissètz un repertòri de destinacion deferent. Las salvagardas pòdon pas se realizar dins los repertòris bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives",
|
||||
|
@ -327,7 +327,7 @@
|
|||
"log_user_delete": "Levar l’utilizaire « {} »",
|
||||
"log_user_update": "Actualizar las informacions de l’utilizaire « {} »",
|
||||
"log_domain_main_domain": "Far venir « {} » lo domeni màger",
|
||||
"log_tools_migrations_migrate_forward": "Migrar",
|
||||
"log_tools_migrations_migrate_forward": "Executar las migracions",
|
||||
"log_tools_postinstall": "Realizar la post installacion del servidor YunoHost",
|
||||
"log_tools_upgrade": "Actualizacion dels paquets sistèma",
|
||||
"log_tools_shutdown": "Atudar lo servidor",
|
||||
|
@ -593,5 +593,19 @@
|
|||
"app_argument_password_no_default": "Error pendent l’analisi de l’argument del senhal « {name} » : l’argument de senhal pòt pas aver de valor per defaut per de rason de seguretat",
|
||||
"app_label_deprecated": "Aquesta comanda es estada renduda obsolèta. Mercés d'utilizar lo nòva \"yunohost user permission update\" per gerir letiquetada de l'aplication",
|
||||
"additional_urls_already_removed": "URL addicionala {url:s} es ja estada elimida per la permission «#permission:s»",
|
||||
"additional_urls_already_added": "URL addicionadal «{url:s}'» es ja estada aponduda per la permission «{permission:s}»"
|
||||
"additional_urls_already_added": "URL addicionadal «{url:s}'» es ja estada aponduda per la permission «{permission:s}»",
|
||||
"migration_0015_yunohost_upgrade": "Aviada de la mesa a jorn de YunoHost...",
|
||||
"migration_0015_main_upgrade": "Aviada de la mesa a nivèl generala...",
|
||||
"migration_0015_patching_sources_list": "Mesa a jorn del fichièr sources.lists...",
|
||||
"migration_0015_start": "Aviar la migracion cap a Buster",
|
||||
"migration_description_0017_postgresql_9p6_to_11": "Migrar las basas de donadas de PostgreSQL 9.6 cap a 11",
|
||||
"migration_description_0016_php70_to_php73_pools": "Migrar los fichièrs de configuracion php7.0 cap a php7.3",
|
||||
"migration_description_0015_migrate_to_buster": "Mesa a nivèl dels sistèmas Debian Buster e YunoHost 4.x",
|
||||
"migrating_legacy_permission_settings": "Migracion dels paramètres de permission ancians...",
|
||||
"log_app_config_apply": "Aplicar la configuracion a l’aplicacion « {} »",
|
||||
"log_app_config_show_panel": "Mostrar lo panèl de configuracion de l’aplicacion « {} »",
|
||||
"log_app_action_run": "Executar l’accion de l’aplicacion « {} »",
|
||||
"diagnosis_basesystem_hardware_model": "Lo modèl del servidor es {model}",
|
||||
"backup_archive_cant_retrieve_info_json": "Obtencion impossibla de las informacions de l’archiu « {archive} »... Se pòt pas recuperar lo fichièr info.json (o es pas un fichièr json valid).",
|
||||
"app_packaging_format_not_supported": "Se pòt pas installar aquesta aplicacion pr’amor que son format es pas pres en carga per vòstra version de YunoHost. Deuriatz considerar actualizar lo sistèma."
|
||||
}
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
{
|
||||
"password_too_simple_1": "Hasło musi mieć co najmniej 8 znaków"
|
||||
}
|
||||
"password_too_simple_1": "Hasło musi mieć co najmniej 8 znaków",
|
||||
"app_already_up_to_date": "{app:s} jest obecnie aktualna",
|
||||
"app_already_installed": "{app:s} jest już zainstalowane",
|
||||
"already_up_to_date": "Nic do zrobienia. Wszystko jest obecnie aktualne.",
|
||||
"admin_password_too_long": "Proszę wybrać hasło krótsze niż 127 znaków",
|
||||
"admin_password_changed": "Hasło administratora zostało zmienione",
|
||||
"admin_password_change_failed": "Nie można zmienić hasła",
|
||||
"admin_password": "Hasło administratora",
|
||||
"action_invalid": "Nieprawidłowa operacja '{action:s}'",
|
||||
"aborting": "Przerywanie."
|
||||
}
|
||||
|
|
|
@ -47,14 +47,12 @@ from moulinette.utils.filesystem import (
|
|||
write_to_file,
|
||||
write_to_json,
|
||||
write_to_yaml,
|
||||
chmod,
|
||||
chown,
|
||||
mkdir,
|
||||
)
|
||||
|
||||
from yunohost.service import service_status, _run_service_command
|
||||
from yunohost.utils import packages
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.log import is_unit_operation, OperationLogger
|
||||
|
||||
logger = getActionLogger("yunohost.app")
|
||||
|
@ -66,7 +64,6 @@ APP_TMP_FOLDER = INSTALL_TMP + "/from_file"
|
|||
|
||||
APPS_CATALOG_CACHE = "/var/cache/yunohost/repo"
|
||||
APPS_CATALOG_CONF = "/etc/yunohost/apps_catalog.yml"
|
||||
APPS_CATALOG_CRON_PATH = "/etc/cron.daily/yunohost-fetch-apps-catalog"
|
||||
APPS_CATALOG_API_VERSION = 2
|
||||
APPS_CATALOG_DEFAULT_URL = "https://app.yunohost.org/default"
|
||||
|
||||
|
@ -192,7 +189,7 @@ def app_info(app, full=False):
|
|||
from yunohost.permission import user_permission_list
|
||||
|
||||
if not _is_installed(app):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
|
||||
|
@ -257,8 +254,9 @@ def _app_upgradable(app_infos):
|
|||
return "url_required"
|
||||
|
||||
# Do not advertise upgrades for bad-quality apps
|
||||
level = app_in_catalog.get("level", -1)
|
||||
if (
|
||||
not app_in_catalog.get("level", -1) >= 5
|
||||
not (isinstance(level, int) and level >= 5)
|
||||
or app_in_catalog.get("state") != "working"
|
||||
):
|
||||
return "bad_quality"
|
||||
|
@ -321,7 +319,7 @@ def app_map(app=None, raw=False, user=None):
|
|||
|
||||
if app is not None:
|
||||
if not _is_installed(app):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
apps = [
|
||||
|
@ -421,14 +419,14 @@ def app_change_url(operation_logger, app, domain, path):
|
|||
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
|
||||
if not os.path.exists(
|
||||
os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url")
|
||||
):
|
||||
raise YunohostError("app_change_url_no_script", app_name=app)
|
||||
raise YunohostValidationError("app_change_url_no_script", app_name=app)
|
||||
|
||||
old_domain = app_setting(app, "domain")
|
||||
old_path = app_setting(app, "path")
|
||||
|
@ -438,7 +436,7 @@ def app_change_url(operation_logger, app, domain, path):
|
|||
domain, path = _normalize_domain_path(domain, path)
|
||||
|
||||
if (domain, path) == (old_domain, old_path):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_change_url_identical_domains", domain=domain, path=path
|
||||
)
|
||||
|
||||
|
@ -551,12 +549,12 @@ def app_upgrade(app=[], url=None, file=None, force=False):
|
|||
|
||||
# Abort if any of those app is in fact not installed..
|
||||
for app in [app_ for app_ in apps if not _is_installed(app_)]:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
|
||||
if len(apps) == 0:
|
||||
raise YunohostError("apps_already_up_to_date")
|
||||
raise YunohostValidationError("apps_already_up_to_date")
|
||||
if len(apps) > 1:
|
||||
logger.info(m18n.n("app_upgrade_several_apps", apps=", ".join(apps)))
|
||||
|
||||
|
@ -880,11 +878,11 @@ def app_install(
|
|||
confirm_install("thirdparty")
|
||||
manifest, extracted_app_folder = _extract_app_from_file(app)
|
||||
else:
|
||||
raise YunohostError("app_unknown")
|
||||
raise YunohostValidationError("app_unknown")
|
||||
|
||||
# Check ID
|
||||
if "id" not in manifest or "__" in manifest["id"]:
|
||||
raise YunohostError("app_id_invalid")
|
||||
raise YunohostValidationError("app_id_invalid")
|
||||
|
||||
app_id = manifest["id"]
|
||||
label = label if label else manifest["name"]
|
||||
|
@ -897,7 +895,7 @@ def app_install(
|
|||
instance_number = _installed_instance_number(app_id, last=True) + 1
|
||||
if instance_number > 1:
|
||||
if "multi_instance" not in manifest or not is_true(manifest["multi_instance"]):
|
||||
raise YunohostError("app_already_installed", app=app_id)
|
||||
raise YunohostValidationError("app_already_installed", app=app_id)
|
||||
|
||||
# Change app_id to the forked app id
|
||||
app_instance_name = app_id + "__" + str(instance_number)
|
||||
|
@ -1124,8 +1122,7 @@ def app_install(
|
|||
|
||||
raise YunohostError(
|
||||
failure_message_with_debug_instructions,
|
||||
raw_msg=True,
|
||||
log_ref=operation_logger.name,
|
||||
raw_msg=True
|
||||
)
|
||||
|
||||
# Clean hooks and add new ones
|
||||
|
@ -1209,7 +1206,7 @@ def app_remove(operation_logger, app):
|
|||
)
|
||||
|
||||
if not _is_installed(app):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
|
||||
|
@ -1372,10 +1369,10 @@ def app_makedefault(operation_logger, app, domain=None):
|
|||
domain = app_domain
|
||||
operation_logger.related_to.append(("domain", domain))
|
||||
elif domain not in domain_list()["domains"]:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
if "/" in app_map(raw=True)[domain]:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_make_default_location_already_used",
|
||||
app=app,
|
||||
domain=app_domain,
|
||||
|
@ -1578,7 +1575,7 @@ def app_register_url(app, domain, path):
|
|||
if _is_installed(app):
|
||||
settings = _get_app_settings(app)
|
||||
if "path" in settings.keys() and "domain" in settings.keys():
|
||||
raise YunohostError("app_already_installed_cant_change_url")
|
||||
raise YunohostValidationError("app_already_installed_cant_change_url")
|
||||
|
||||
# Check the url is available
|
||||
_assert_no_conflicting_apps(domain, path)
|
||||
|
@ -1694,7 +1691,7 @@ def app_change_label(app, new_label):
|
|||
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
logger.warning(m18n.n("app_label_deprecated"))
|
||||
|
@ -1730,7 +1727,7 @@ def app_action_run(operation_logger, app, action, args=None):
|
|||
actions = {x["id"]: x for x in actions}
|
||||
|
||||
if action not in actions:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"action '%s' not available for app '%s', available actions are: %s"
|
||||
% (action, app, ", ".join(actions.keys())),
|
||||
raw_msg=True,
|
||||
|
@ -1884,7 +1881,7 @@ def app_config_apply(operation_logger, app, args):
|
|||
|
||||
installed = _is_installed(app)
|
||||
if not installed:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
|
||||
|
@ -2199,7 +2196,7 @@ def _get_app_settings(app_id):
|
|||
|
||||
"""
|
||||
if not _is_installed(app_id):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_not_installed", app=app_id, all_apps=_get_all_installed_apps_id()
|
||||
)
|
||||
try:
|
||||
|
@ -2546,9 +2543,9 @@ def _fetch_app_from_git(app):
|
|||
app_id, _ = _parse_app_instance_name(app)
|
||||
|
||||
if app_id not in app_dict:
|
||||
raise YunohostError("app_unknown")
|
||||
raise YunohostValidationError("app_unknown")
|
||||
elif "git" not in app_dict[app_id]:
|
||||
raise YunohostError("app_unsupported_remote_type")
|
||||
raise YunohostValidationError("app_unsupported_remote_type")
|
||||
|
||||
app_info = app_dict[app_id]
|
||||
url = app_info["git"]["url"]
|
||||
|
@ -2684,7 +2681,7 @@ def _check_manifest_requirements(manifest, app_instance_name):
|
|||
|
||||
packaging_format = int(manifest.get("packaging_format", 0))
|
||||
if packaging_format not in [0, 1]:
|
||||
raise YunohostError("app_packaging_format_not_supported")
|
||||
raise YunohostValidationError("app_packaging_format_not_supported")
|
||||
|
||||
requirements = manifest.get("requirements", dict())
|
||||
|
||||
|
@ -2697,7 +2694,7 @@ def _check_manifest_requirements(manifest, app_instance_name):
|
|||
for pkgname, spec in requirements.items():
|
||||
if not packages.meets_version_specifier(pkgname, spec):
|
||||
version = packages.ynh_packages_version()[pkgname]["version"]
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_requirements_unmeet",
|
||||
pkgname=pkgname,
|
||||
version=version,
|
||||
|
@ -2796,7 +2793,7 @@ class YunoHostArgumentFormatParser(object):
|
|||
# we don't have an answer, check optional and default_value
|
||||
if question.value is None or question.value == "":
|
||||
if not question.optional and question.default is None:
|
||||
raise YunohostError("app_argument_required", name=question.name)
|
||||
raise YunohostValidationError("app_argument_required", name=question.name)
|
||||
else:
|
||||
question.value = (
|
||||
getattr(self, "default_value", None)
|
||||
|
@ -2816,7 +2813,7 @@ class YunoHostArgumentFormatParser(object):
|
|||
return (question.value, self.argument_type)
|
||||
|
||||
def _raise_invalid_answer(self, question):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_argument_choice_invalid",
|
||||
name=question.name,
|
||||
choices=", ".join(question.choices),
|
||||
|
@ -2854,13 +2851,13 @@ class PasswordArgumentParser(YunoHostArgumentFormatParser):
|
|||
)
|
||||
|
||||
if question.default is not None:
|
||||
raise YunohostError("app_argument_password_no_default", name=question.name)
|
||||
raise YunohostValidationError("app_argument_password_no_default", name=question.name)
|
||||
|
||||
return question
|
||||
|
||||
def _post_parse_value(self, question):
|
||||
if any(char in question.value for char in self.forbidden_chars):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"pattern_password_app", forbidden_chars=self.forbidden_chars
|
||||
)
|
||||
|
||||
|
@ -2913,7 +2910,7 @@ class BooleanArgumentParser(YunoHostArgumentFormatParser):
|
|||
if str(question.value).lower() in ["0", "no", "n", "false"]:
|
||||
return 0
|
||||
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_argument_choice_invalid",
|
||||
name=question.name,
|
||||
choices="yes, no, y, n, 1, 0",
|
||||
|
@ -2938,7 +2935,7 @@ class DomainArgumentParser(YunoHostArgumentFormatParser):
|
|||
return question
|
||||
|
||||
def _raise_invalid_answer(self, question):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_argument_invalid", name=question.name, error=m18n.n("domain_unknown")
|
||||
)
|
||||
|
||||
|
@ -2964,7 +2961,7 @@ class UserArgumentParser(YunoHostArgumentFormatParser):
|
|||
return question
|
||||
|
||||
def _raise_invalid_answer(self, question):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_argument_invalid",
|
||||
name=question.name,
|
||||
error=m18n.n("user_unknown", user=question.value),
|
||||
|
@ -2992,7 +2989,7 @@ class NumberArgumentParser(YunoHostArgumentFormatParser):
|
|||
if isinstance(question.value, str) and question.value.isdigit():
|
||||
return int(question.value)
|
||||
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_argument_invalid", name=question.name, error=m18n.n("invalid_number")
|
||||
)
|
||||
|
||||
|
@ -3123,7 +3120,7 @@ def _get_conflicting_apps(domain, path, ignore_app=None):
|
|||
|
||||
# Abort if domain is unknown
|
||||
if domain not in domain_list()["domains"]:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
# Fetch apps map
|
||||
apps_map = app_map(raw=True)
|
||||
|
@ -3162,9 +3159,9 @@ def _assert_no_conflicting_apps(domain, path, ignore_app=None, full_domain=False
|
|||
)
|
||||
|
||||
if full_domain:
|
||||
raise YunohostError("app_full_domain_unavailable", domain=domain)
|
||||
raise YunohostValidationError("app_full_domain_unavailable", domain=domain)
|
||||
else:
|
||||
raise YunohostError("app_location_unavailable", apps="\n".join(apps))
|
||||
raise YunohostValidationError("app_location_unavailable", apps="\n".join(apps))
|
||||
|
||||
|
||||
def _make_environment_for_app_script(app, args={}, args_prefix="APP_ARG_"):
|
||||
|
@ -3232,28 +3229,15 @@ def _parse_app_instance_name(app_instance_name):
|
|||
def _initialize_apps_catalog_system():
|
||||
"""
|
||||
This function is meant to intialize the apps_catalog system with YunoHost's default app catalog.
|
||||
|
||||
It also creates the cron job that will update the list every day
|
||||
"""
|
||||
|
||||
default_apps_catalog_list = [{"id": "default", "url": APPS_CATALOG_DEFAULT_URL}]
|
||||
|
||||
cron_job = []
|
||||
cron_job.append("#!/bin/bash")
|
||||
# We add a random delay between 0 and 60 min to avoid every instance fetching
|
||||
# the apps catalog at the same time every night
|
||||
cron_job.append("(sleep $((RANDOM%3600));")
|
||||
cron_job.append("yunohost tools update --apps > /dev/null) &")
|
||||
try:
|
||||
logger.debug(
|
||||
"Initializing apps catalog system with YunoHost's default app list"
|
||||
)
|
||||
write_to_yaml(APPS_CATALOG_CONF, default_apps_catalog_list)
|
||||
|
||||
logger.debug("Installing apps catalog fetch daily cron job")
|
||||
write_to_file(APPS_CATALOG_CRON_PATH, "\n".join(cron_job))
|
||||
chown(APPS_CATALOG_CRON_PATH, uid="root", gid="root")
|
||||
chmod(APPS_CATALOG_CRON_PATH, 0o755)
|
||||
except Exception as e:
|
||||
raise YunohostError(
|
||||
"Could not initialize the apps catalog system... : %s" % str(e)
|
||||
|
@ -3467,17 +3451,18 @@ def _assert_system_is_sane_for_app(manifest, when):
|
|||
|
||||
# Wait if a service is reloading
|
||||
test_nb = 0
|
||||
while test_nb < 10:
|
||||
while test_nb < 16:
|
||||
if not any(s for s in services if service_status(s)["status"] == "reloading"):
|
||||
break
|
||||
time.sleep(0.5)
|
||||
test_nb+=1
|
||||
|
||||
# List services currently down and raise an exception if any are found
|
||||
faulty_services = [s for s in services if service_status(s)["status"] != "running"]
|
||||
services_status = {s:service_status(s) for s in services}
|
||||
faulty_services = [f"{s} ({status['status']})" for s, status in services_status.items() if status['status'] != "running"]
|
||||
if faulty_services:
|
||||
if when == "pre":
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"app_action_cannot_be_ran_because_required_services_down",
|
||||
services=", ".join(faulty_services),
|
||||
)
|
||||
|
@ -3488,7 +3473,7 @@ def _assert_system_is_sane_for_app(manifest, when):
|
|||
|
||||
if packages.dpkg_is_broken():
|
||||
if when == "pre":
|
||||
raise YunohostError("dpkg_is_broken")
|
||||
raise YunohostValidationError("dpkg_is_broken")
|
||||
elif when == "post":
|
||||
raise YunohostError("this_action_broke_dpkg")
|
||||
|
||||
|
@ -3667,7 +3652,7 @@ def _patch_legacy_helpers(app_folder):
|
|||
# couldn't patch the deprecated helper in the previous lines. In
|
||||
# that case, abort the install or whichever step is performed
|
||||
if helper in content and infos["important"]:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"This app is likely pretty old and uses deprecated / outdated helpers that can't be migrated easily. It can't be installed anymore.",
|
||||
raw_msg=True,
|
||||
)
|
||||
|
|
|
@ -36,6 +36,7 @@ from datetime import datetime
|
|||
from glob import glob
|
||||
from collections import OrderedDict
|
||||
from functools import reduce
|
||||
from packaging import version
|
||||
|
||||
from moulinette import msignals, m18n, msettings
|
||||
from moulinette.utils import filesystem
|
||||
|
@ -60,10 +61,10 @@ from yunohost.hook import (
|
|||
hook_exec,
|
||||
CUSTOM_HOOK_FOLDER,
|
||||
)
|
||||
from yunohost.tools import tools_postinstall
|
||||
from yunohost.tools import tools_postinstall, _tools_migrations_run_after_system_restore, _tools_migrations_run_before_app_restore
|
||||
from yunohost.regenconf import regen_conf
|
||||
from yunohost.log import OperationLogger
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.log import OperationLogger, is_unit_operation
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.utils.packages import ynh_packages_version
|
||||
from yunohost.settings import settings_get
|
||||
|
||||
|
@ -348,7 +349,7 @@ class BackupManager:
|
|||
|
||||
# Try to recursively unmount stuff (from a previously failed backup ?)
|
||||
if not _recursive_umount(self.work_dir):
|
||||
raise YunohostError("backup_output_directory_not_empty")
|
||||
raise YunohostValidationError("backup_output_directory_not_empty")
|
||||
else:
|
||||
# If umount succeeded, remove the directory (we checked that
|
||||
# we're in /home/yunohost.backup/tmp so that should be okay...
|
||||
|
@ -858,6 +859,9 @@ class RestoreManager:
|
|||
# FIXME this way to get the info is not compatible with copy or custom
|
||||
# backup methods
|
||||
self.info = backup_info(name, with_details=True)
|
||||
if not self.info["from_yunohost_version"] or version.parse(self.info["from_yunohost_version"]) < version.parse("3.8.0"):
|
||||
raise YunohostValidationError("restore_backup_too_old")
|
||||
|
||||
self.archive_path = self.info["path"]
|
||||
self.name = name
|
||||
self.method = BackupMethod.create(method, self)
|
||||
|
@ -1027,7 +1031,7 @@ class RestoreManager:
|
|||
already_installed = [app for app in to_be_restored if _is_installed(app)]
|
||||
if already_installed != []:
|
||||
if already_installed == to_be_restored:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"restore_already_installed_apps", apps=", ".join(already_installed)
|
||||
)
|
||||
else:
|
||||
|
@ -1133,14 +1137,14 @@ class RestoreManager:
|
|||
return True
|
||||
elif free_space > needed_space:
|
||||
# TODO Add --force options to avoid the error raising
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"restore_may_be_not_enough_disk_space",
|
||||
free_space=free_space,
|
||||
needed_space=needed_space,
|
||||
margin=margin,
|
||||
)
|
||||
else:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"restore_not_enough_disk_space",
|
||||
free_space=free_space,
|
||||
needed_space=needed_space,
|
||||
|
@ -1215,7 +1219,6 @@ class RestoreManager:
|
|||
if system_targets == []:
|
||||
return
|
||||
|
||||
from yunohost.user import user_group_list
|
||||
from yunohost.permission import (
|
||||
permission_create,
|
||||
permission_delete,
|
||||
|
@ -1278,25 +1281,15 @@ class RestoreManager:
|
|||
|
||||
regen_conf()
|
||||
|
||||
# Check that at least a group exists (all_users) to know if we need to
|
||||
# do the migration 0011 : setup group and permission
|
||||
#
|
||||
# Legacy code
|
||||
if "all_users" not in user_group_list()["groups"].keys():
|
||||
from yunohost.utils.legacy import SetupGroupPermissions
|
||||
_tools_migrations_run_after_system_restore(backup_version=self.info["from_yunohost_version"])
|
||||
|
||||
# Update LDAP schema restart slapd
|
||||
logger.info(m18n.n("migration_0011_update_LDAP_schema"))
|
||||
regen_conf(names=["slapd"], force=True)
|
||||
SetupGroupPermissions.migrate_LDAP_db()
|
||||
|
||||
# Remove all permission for all app which is still in the LDAP
|
||||
# Remove all permission for all app still in the LDAP
|
||||
for permission_name in user_permission_list(ignore_system_perms=True)[
|
||||
"permissions"
|
||||
].keys():
|
||||
permission_delete(permission_name, force=True, sync_perm=False)
|
||||
|
||||
# Restore permission for the app which is installed
|
||||
# Restore permission for apps installed
|
||||
for permission_name, permission_infos in old_apps_permission.items():
|
||||
app_name, perm_name = permission_name.split(".")
|
||||
if _is_installed(app_name):
|
||||
|
@ -1347,7 +1340,6 @@ class RestoreManager:
|
|||
name should be already install)
|
||||
"""
|
||||
from yunohost.user import user_group_list
|
||||
from yunohost.app import app_setting
|
||||
from yunohost.permission import (
|
||||
permission_create,
|
||||
permission_delete,
|
||||
|
@ -1400,7 +1392,6 @@ class RestoreManager:
|
|||
self.targets.set_result("apps", app_instance_name, "Warning")
|
||||
return
|
||||
|
||||
logger.debug(m18n.n("restore_running_app_script", app=app_instance_name))
|
||||
try:
|
||||
# Restore app settings
|
||||
app_settings_new_path = os.path.join(
|
||||
|
@ -1409,7 +1400,7 @@ class RestoreManager:
|
|||
app_scripts_new_path = os.path.join(app_settings_new_path, "scripts")
|
||||
shutil.copytree(app_settings_in_archive, app_settings_new_path)
|
||||
filesystem.chmod(app_settings_new_path, 0o400, 0o400, True)
|
||||
filesystem.chown(app_scripts_new_path, "admin", None, True)
|
||||
filesystem.chown(app_scripts_new_path, "root", None, True)
|
||||
|
||||
# Copy the app scripts to a writable temporary folder
|
||||
# FIXME : use 'install -Dm555' or something similar to what's done
|
||||
|
@ -1417,146 +1408,156 @@ class RestoreManager:
|
|||
tmp_folder_for_app_restore = tempfile.mkdtemp(prefix="restore")
|
||||
copytree(app_scripts_in_archive, tmp_folder_for_app_restore)
|
||||
filesystem.chmod(tmp_folder_for_app_restore, 0o550, 0o550, True)
|
||||
filesystem.chown(tmp_folder_for_app_restore, "admin", None, True)
|
||||
filesystem.chown(tmp_folder_for_app_restore, "root", None, True)
|
||||
restore_script = os.path.join(tmp_folder_for_app_restore, "restore")
|
||||
|
||||
# Restore permissions
|
||||
if os.path.isfile("%s/permissions.yml" % app_settings_new_path):
|
||||
if not os.path.isfile("%s/permissions.yml" % app_settings_new_path):
|
||||
raise YunohostError("Didnt find a permssions.yml for the app !?", raw_msg=True)
|
||||
|
||||
permissions = read_yaml("%s/permissions.yml" % app_settings_new_path)
|
||||
existing_groups = user_group_list()["groups"]
|
||||
permissions = read_yaml("%s/permissions.yml" % app_settings_new_path)
|
||||
existing_groups = user_group_list()["groups"]
|
||||
|
||||
for permission_name, permission_infos in permissions.items():
|
||||
for permission_name, permission_infos in permissions.items():
|
||||
|
||||
if "allowed" not in permission_infos:
|
||||
logger.warning(
|
||||
"'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself."
|
||||
% (permission_name, app_instance_name)
|
||||
)
|
||||
should_be_allowed = ["all_users"]
|
||||
else:
|
||||
should_be_allowed = [
|
||||
g
|
||||
for g in permission_infos["allowed"]
|
||||
if g in existing_groups
|
||||
]
|
||||
|
||||
perm_name = permission_name.split(".")[1]
|
||||
permission_create(
|
||||
permission_name,
|
||||
allowed=should_be_allowed,
|
||||
url=permission_infos.get("url"),
|
||||
additional_urls=permission_infos.get("additional_urls"),
|
||||
auth_header=permission_infos.get("auth_header"),
|
||||
label=permission_infos.get("label")
|
||||
if perm_name == "main"
|
||||
else permission_infos.get("sublabel"),
|
||||
show_tile=permission_infos.get("show_tile", True),
|
||||
protected=permission_infos.get("protected", False),
|
||||
sync_perm=False,
|
||||
if "allowed" not in permission_infos:
|
||||
logger.warning(
|
||||
"'allowed' key corresponding to allowed groups for permission %s not found when restoring app %s … You might have to reconfigure permissions yourself."
|
||||
% (permission_name, app_instance_name)
|
||||
)
|
||||
should_be_allowed = ["all_users"]
|
||||
else:
|
||||
should_be_allowed = [
|
||||
g
|
||||
for g in permission_infos["allowed"]
|
||||
if g in existing_groups
|
||||
]
|
||||
|
||||
permission_sync_to_user()
|
||||
perm_name = permission_name.split(".")[1]
|
||||
permission_create(
|
||||
permission_name,
|
||||
allowed=should_be_allowed,
|
||||
url=permission_infos.get("url"),
|
||||
additional_urls=permission_infos.get("additional_urls"),
|
||||
auth_header=permission_infos.get("auth_header"),
|
||||
label=permission_infos.get("label")
|
||||
if perm_name == "main"
|
||||
else permission_infos.get("sublabel"),
|
||||
show_tile=permission_infos.get("show_tile", True),
|
||||
protected=permission_infos.get("protected", False),
|
||||
sync_perm=False,
|
||||
)
|
||||
|
||||
os.remove("%s/permissions.yml" % app_settings_new_path)
|
||||
else:
|
||||
# Otherwise, we need to migrate the legacy permissions of this
|
||||
# app (included in its settings.yml)
|
||||
from yunohost.utils.legacy import SetupGroupPermissions
|
||||
permission_sync_to_user()
|
||||
|
||||
SetupGroupPermissions.migrate_app_permission(app=app_instance_name)
|
||||
os.remove("%s/permissions.yml" % app_settings_new_path)
|
||||
|
||||
# Migrate old settings
|
||||
legacy_permission_settings = [
|
||||
"skipped_uris",
|
||||
"unprotected_uris",
|
||||
"protected_uris",
|
||||
"skipped_regex",
|
||||
"unprotected_regex",
|
||||
"protected_regex",
|
||||
]
|
||||
if any(
|
||||
app_setting(app_instance_name, setting) is not None
|
||||
for setting in legacy_permission_settings
|
||||
):
|
||||
from yunohost.utils.legacy import migrate_legacy_permission_settings
|
||||
|
||||
migrate_legacy_permission_settings(app=app_instance_name)
|
||||
|
||||
# Prepare env. var. to pass to script
|
||||
env_dict = _make_environment_for_app_script(app_instance_name)
|
||||
env_dict.update(
|
||||
{
|
||||
"YNH_BACKUP_DIR": self.work_dir,
|
||||
"YNH_BACKUP_CSV": os.path.join(self.work_dir, "backup.csv"),
|
||||
"YNH_APP_BACKUP_DIR": os.path.join(
|
||||
self.work_dir, "apps", app_instance_name, "backup"
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
operation_logger.extra["env"] = env_dict
|
||||
operation_logger.flush()
|
||||
|
||||
# Execute app restore script
|
||||
hook_exec(
|
||||
restore_script,
|
||||
chdir=app_backup_in_archive,
|
||||
raise_on_error=True,
|
||||
env=env_dict,
|
||||
)[0]
|
||||
_tools_migrations_run_before_app_restore(backup_version=self.info["from_yunohost_version"], app_id=app_instance_name)
|
||||
except Exception:
|
||||
msg = m18n.n("restore_app_failed", app=app_instance_name)
|
||||
import traceback
|
||||
error = m18n.n("unexpected_error", error="\n" + traceback.format_exc())
|
||||
msg = m18n.n("app_restore_failed", app=app_instance_name, error=error)
|
||||
logger.error(msg)
|
||||
operation_logger.error(msg)
|
||||
|
||||
if msettings.get("interface") != "api":
|
||||
dump_app_log_extract_for_debugging(operation_logger)
|
||||
|
||||
self.targets.set_result("apps", app_instance_name, "Error")
|
||||
|
||||
remove_script = os.path.join(app_scripts_in_archive, "remove")
|
||||
|
||||
# Setup environment for remove script
|
||||
env_dict_remove = _make_environment_for_app_script(app_instance_name)
|
||||
|
||||
operation_logger = OperationLogger(
|
||||
"remove_on_failed_restore",
|
||||
[("app", app_instance_name)],
|
||||
env=env_dict_remove,
|
||||
)
|
||||
operation_logger.start()
|
||||
|
||||
# Execute remove script
|
||||
if hook_exec(remove_script, env=env_dict_remove)[0] != 0:
|
||||
msg = m18n.n("app_not_properly_removed", app=app_instance_name)
|
||||
logger.warning(msg)
|
||||
operation_logger.error(msg)
|
||||
else:
|
||||
operation_logger.success()
|
||||
|
||||
# Cleaning app directory
|
||||
# Cleanup
|
||||
shutil.rmtree(app_settings_new_path, ignore_errors=True)
|
||||
shutil.rmtree(tmp_folder_for_app_restore, ignore_errors=True)
|
||||
|
||||
# Remove all permission in LDAP for this app
|
||||
for permission_name in user_permission_list()["permissions"].keys():
|
||||
if permission_name.startswith(app_instance_name + "."):
|
||||
permission_delete(permission_name, force=True)
|
||||
return
|
||||
|
||||
# TODO Cleaning app hooks
|
||||
else:
|
||||
self.targets.set_result("apps", app_instance_name, "Success")
|
||||
operation_logger.success()
|
||||
logger.debug(m18n.n("restore_running_app_script", app=app_instance_name))
|
||||
|
||||
# Prepare env. var. to pass to script
|
||||
env_dict = _make_environment_for_app_script(app_instance_name)
|
||||
env_dict.update(
|
||||
{
|
||||
"YNH_BACKUP_DIR": self.work_dir,
|
||||
"YNH_BACKUP_CSV": os.path.join(self.work_dir, "backup.csv"),
|
||||
"YNH_APP_BACKUP_DIR": os.path.join(
|
||||
self.work_dir, "apps", app_instance_name, "backup"
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
operation_logger.extra["env"] = env_dict
|
||||
operation_logger.flush()
|
||||
|
||||
# Execute the app install script
|
||||
restore_failed = True
|
||||
try:
|
||||
restore_retcode = hook_exec(
|
||||
restore_script,
|
||||
chdir=app_backup_in_archive,
|
||||
env=env_dict,
|
||||
)[0]
|
||||
# "Common" app restore failure : the script failed and returned exit code != 0
|
||||
restore_failed = True if restore_retcode != 0 else False
|
||||
if restore_failed:
|
||||
error = m18n.n("app_restore_script_failed")
|
||||
logger.error(m18n.n("app_restore_failed", app=app_instance_name, error=error))
|
||||
failure_message_with_debug_instructions = operation_logger.error(error)
|
||||
if msettings.get("interface") != "api":
|
||||
dump_app_log_extract_for_debugging(operation_logger)
|
||||
# Script got manually interrupted ... N.B. : KeyboardInterrupt does not inherit from Exception
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
error = m18n.n("operation_interrupted")
|
||||
logger.error(m18n.n("app_restore_failed", app=app_instance_name, error=error))
|
||||
failure_message_with_debug_instructions = operation_logger.error(error)
|
||||
# Something wrong happened in Yunohost's code (most probably hook_exec)
|
||||
except Exception:
|
||||
import traceback
|
||||
error = m18n.n("unexpected_error", error="\n" + traceback.format_exc())
|
||||
logger.error(m18n.n("app_restore_failed", app=app_instance_name, error=error))
|
||||
failure_message_with_debug_instructions = operation_logger.error(error)
|
||||
finally:
|
||||
# Cleaning temporary scripts directory
|
||||
shutil.rmtree(tmp_folder_for_app_restore, ignore_errors=True)
|
||||
|
||||
if not restore_failed:
|
||||
self.targets.set_result("apps", app_instance_name, "Success")
|
||||
operation_logger.success()
|
||||
else:
|
||||
|
||||
self.targets.set_result("apps", app_instance_name, "Error")
|
||||
|
||||
remove_script = os.path.join(app_scripts_in_archive, "remove")
|
||||
|
||||
# Setup environment for remove script
|
||||
env_dict_remove = _make_environment_for_app_script(app_instance_name)
|
||||
|
||||
remove_operation_logger = OperationLogger(
|
||||
"remove_on_failed_restore",
|
||||
[("app", app_instance_name)],
|
||||
env=env_dict_remove,
|
||||
)
|
||||
remove_operation_logger.start()
|
||||
|
||||
# Execute remove script
|
||||
if hook_exec(remove_script, env=env_dict_remove)[0] != 0:
|
||||
msg = m18n.n("app_not_properly_removed", app=app_instance_name)
|
||||
logger.warning(msg)
|
||||
remove_operation_logger.error(msg)
|
||||
else:
|
||||
remove_operation_logger.success()
|
||||
|
||||
# Cleaning app directory
|
||||
shutil.rmtree(app_settings_new_path, ignore_errors=True)
|
||||
|
||||
# Remove all permission in LDAP for this app
|
||||
for permission_name in user_permission_list()["permissions"].keys():
|
||||
if permission_name.startswith(app_instance_name + "."):
|
||||
permission_delete(permission_name, force=True)
|
||||
|
||||
# TODO Cleaning app hooks
|
||||
|
||||
logger.error(failure_message_with_debug_instructions)
|
||||
|
||||
#
|
||||
# Backup methods #
|
||||
#
|
||||
|
||||
|
||||
class BackupMethod(object):
|
||||
|
||||
"""
|
||||
|
@ -1729,7 +1730,7 @@ class BackupMethod(object):
|
|||
free_space,
|
||||
backup_size,
|
||||
)
|
||||
raise YunohostError("not_enough_disk_space", path=self.repo)
|
||||
raise YunohostValidationError("not_enough_disk_space", path=self.repo)
|
||||
|
||||
def _organize_files(self):
|
||||
"""
|
||||
|
@ -2163,7 +2164,9 @@ class CustomBackupMethod(BackupMethod):
|
|||
#
|
||||
|
||||
|
||||
@is_unit_operation()
|
||||
def backup_create(
|
||||
operation_logger,
|
||||
name=None, description=None, methods=[], output_directory=None, system=[], apps=[]
|
||||
):
|
||||
"""
|
||||
|
@ -2186,7 +2189,7 @@ def backup_create(
|
|||
|
||||
# Validate there is no archive with the same name
|
||||
if name and name in backup_list()["archives"]:
|
||||
raise YunohostError("backup_archive_name_exists")
|
||||
raise YunohostValidationError("backup_archive_name_exists")
|
||||
|
||||
# By default we backup using the tar method
|
||||
if not methods:
|
||||
|
@ -2201,14 +2204,14 @@ def backup_create(
|
|||
r"^/(|(bin|boot|dev|etc|lib|root|run|sbin|sys|usr|var)(|/.*))$",
|
||||
output_directory,
|
||||
):
|
||||
raise YunohostError("backup_output_directory_forbidden")
|
||||
raise YunohostValidationError("backup_output_directory_forbidden")
|
||||
|
||||
if "copy" in methods:
|
||||
if not output_directory:
|
||||
raise YunohostError("backup_output_directory_required")
|
||||
raise YunohostValidationError("backup_output_directory_required")
|
||||
# Check that output directory is empty
|
||||
elif os.path.isdir(output_directory) and os.listdir(output_directory):
|
||||
raise YunohostError("backup_output_directory_not_empty")
|
||||
raise YunohostValidationError("backup_output_directory_not_empty")
|
||||
|
||||
# If no --system or --apps given, backup everything
|
||||
if system is None and apps is None:
|
||||
|
@ -2219,6 +2222,8 @@ def backup_create(
|
|||
# Intialize #
|
||||
#
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
# Create yunohost archives directory if it does not exists
|
||||
_create_archive_dir()
|
||||
|
||||
|
@ -2233,6 +2238,10 @@ def backup_create(
|
|||
backup_manager.set_system_targets(system)
|
||||
backup_manager.set_apps_targets(apps)
|
||||
|
||||
for app in backup_manager.targets.list("apps", exclude=["Skipped"]):
|
||||
operation_logger.related_to.append(("app", app))
|
||||
operation_logger.flush()
|
||||
|
||||
#
|
||||
# Collect files and put them in the archive #
|
||||
#
|
||||
|
@ -2245,6 +2254,7 @@ def backup_create(
|
|||
backup_manager.backup()
|
||||
|
||||
logger.success(m18n.n("backup_created"))
|
||||
operation_logger.success()
|
||||
|
||||
return {
|
||||
"name": backup_manager.name,
|
||||
|
@ -2277,6 +2287,11 @@ def backup_restore(name, system=[], apps=[], force=False):
|
|||
# Initialize #
|
||||
#
|
||||
|
||||
if name.endswith(".tar.gz"):
|
||||
name = name[:-len(".tar.gz")]
|
||||
elif name.endswith(".tar"):
|
||||
name = name[:-len(".tar")]
|
||||
|
||||
restore_manager = RestoreManager(name)
|
||||
|
||||
restore_manager.set_system_targets(system)
|
||||
|
@ -2381,7 +2396,7 @@ def backup_download(name):
|
|||
if not os.path.lexists(archive_file):
|
||||
archive_file += ".gz"
|
||||
if not os.path.lexists(archive_file):
|
||||
raise YunohostError("backup_archive_name_unknown", name=name)
|
||||
raise YunohostValidationError("backup_archive_name_unknown", name=name)
|
||||
|
||||
# If symlink, retrieve the real path
|
||||
if os.path.islink(archive_file):
|
||||
|
@ -2389,7 +2404,7 @@ def backup_download(name):
|
|||
|
||||
# Raise exception if link is broken (e.g. on unmounted external storage)
|
||||
if not os.path.exists(archive_file):
|
||||
raise YunohostError("backup_archive_broken_link", path=archive_file)
|
||||
raise YunohostValidationError("backup_archive_broken_link", path=archive_file)
|
||||
|
||||
# We return a raw bottle HTTPresponse (instead of serializable data like
|
||||
# list/dict, ...), which is gonna be picked and used directly by moulinette
|
||||
|
@ -2409,13 +2424,19 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
human_readable -- Print sizes in human readable format
|
||||
|
||||
"""
|
||||
|
||||
if name.endswith(".tar.gz"):
|
||||
name = name[:-len(".tar.gz")]
|
||||
elif name.endswith(".tar"):
|
||||
name = name[:-len(".tar")]
|
||||
|
||||
archive_file = "%s/%s.tar" % (ARCHIVES_PATH, name)
|
||||
|
||||
# Check file exist (even if it's a broken symlink)
|
||||
if not os.path.lexists(archive_file):
|
||||
archive_file += ".gz"
|
||||
if not os.path.lexists(archive_file):
|
||||
raise YunohostError("backup_archive_name_unknown", name=name)
|
||||
raise YunohostValidationError("backup_archive_name_unknown", name=name)
|
||||
|
||||
# If symlink, retrieve the real path
|
||||
if os.path.islink(archive_file):
|
||||
|
@ -2423,7 +2444,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
|
||||
# Raise exception if link is broken (e.g. on unmounted external storage)
|
||||
if not os.path.exists(archive_file):
|
||||
raise YunohostError("backup_archive_broken_link", path=archive_file)
|
||||
raise YunohostValidationError("backup_archive_broken_link", path=archive_file)
|
||||
|
||||
info_file = "%s/%s.info.json" % (ARCHIVES_PATH, name)
|
||||
|
||||
|
@ -2435,7 +2456,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
|
||||
try:
|
||||
files_in_archive = tar.getnames()
|
||||
except IOError as e:
|
||||
except (IOError, EOFError) as e:
|
||||
raise YunohostError(
|
||||
"backup_archive_corrupted", archive=archive_file, error=str(e)
|
||||
)
|
||||
|
@ -2519,6 +2540,7 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
|
||||
result["apps"] = info["apps"]
|
||||
result["system"] = info[system_key]
|
||||
result["from_yunohost_version"] = info.get("from_yunohost_version")
|
||||
return result
|
||||
|
||||
|
||||
|
@ -2531,7 +2553,7 @@ def backup_delete(name):
|
|||
|
||||
"""
|
||||
if name not in backup_list()["archives"]:
|
||||
raise YunohostError("backup_archive_name_unknown", name=name)
|
||||
raise YunohostValidationError("backup_archive_name_unknown", name=name)
|
||||
|
||||
hook_callback("pre_backup_delete", args=[name])
|
||||
|
||||
|
@ -2548,6 +2570,8 @@ def backup_delete(name):
|
|||
files_to_delete.append(actual_archive)
|
||||
|
||||
for backup_file in files_to_delete:
|
||||
if not os.path.exists(backup_file):
|
||||
continue
|
||||
try:
|
||||
os.remove(backup_file)
|
||||
except Exception:
|
||||
|
|
|
@ -37,7 +37,7 @@ from moulinette.utils.log import getActionLogger
|
|||
from moulinette.utils.filesystem import read_file
|
||||
|
||||
from yunohost.vendor.acme_tiny.acme_tiny import get_crt as sign_certificate
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.utils.network import get_public_ip
|
||||
|
||||
from yunohost.diagnosis import Diagnoser
|
||||
|
@ -90,7 +90,7 @@ def certificate_status(domain_list, full=False):
|
|||
for domain in domain_list:
|
||||
# Is it in Yunohost domain list?
|
||||
if domain not in yunohost_domains_list:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
certificates = {}
|
||||
|
||||
|
@ -166,7 +166,7 @@ def _certificate_install_selfsigned(domain_list, force=False):
|
|||
status = _get_status(domain)
|
||||
|
||||
if status["summary"]["code"] in ("good", "great"):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"certmanager_attempt_to_replace_valid_cert", domain=domain
|
||||
)
|
||||
|
||||
|
@ -197,6 +197,8 @@ def _certificate_install_selfsigned(domain_list, force=False):
|
|||
|
||||
out, _ = p.communicate()
|
||||
|
||||
out = out.decode("utf-8")
|
||||
|
||||
if p.returncode != 0:
|
||||
logger.warning(out)
|
||||
raise YunohostError("domain_cert_gen_failed")
|
||||
|
@ -267,12 +269,12 @@ def _certificate_install_letsencrypt(
|
|||
for domain in domain_list:
|
||||
yunohost_domains_list = yunohost.domain.domain_list()["domains"]
|
||||
if domain not in yunohost_domains_list:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
# Is it self-signed?
|
||||
status = _get_status(domain)
|
||||
if not force and status["CA_type"]["code"] != "self-signed":
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"certmanager_domain_cert_not_selfsigned", domain=domain
|
||||
)
|
||||
|
||||
|
@ -315,8 +317,6 @@ def _certificate_install_letsencrypt(
|
|||
% domain
|
||||
)
|
||||
else:
|
||||
_install_cron(no_checks=no_checks)
|
||||
|
||||
logger.success(m18n.n("certmanager_cert_install_success", domain=domain))
|
||||
|
||||
operation_logger.success()
|
||||
|
@ -370,25 +370,25 @@ def certificate_renew(
|
|||
|
||||
# Is it in Yunohost dmomain list?
|
||||
if domain not in yunohost.domain.domain_list()["domains"]:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
status = _get_status(domain)
|
||||
|
||||
# Does it expire soon?
|
||||
if status["validity"] > VALIDITY_LIMIT and not force:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"certmanager_attempt_to_renew_valid_cert", domain=domain
|
||||
)
|
||||
|
||||
# Does it have a Let's Encrypt cert?
|
||||
if status["CA_type"]["code"] != "lets-encrypt":
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"certmanager_attempt_to_renew_nonLE_cert", domain=domain
|
||||
)
|
||||
|
||||
# Check ACME challenge configured for given domain
|
||||
if not _check_acme_challenge_configuration(domain):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"certmanager_acme_not_configured_for_domain", domain=domain
|
||||
)
|
||||
|
||||
|
@ -455,32 +455,6 @@ def certificate_renew(
|
|||
# Back-end stuff #
|
||||
#
|
||||
|
||||
|
||||
def _install_cron(no_checks=False):
|
||||
cron_job_file = "/etc/cron.daily/yunohost-certificate-renew"
|
||||
|
||||
# we need to check if "--no-checks" isn't already put inside the existing
|
||||
# crontab, if it's the case it's probably because another domain needed it
|
||||
# at some point so we keep it
|
||||
if not no_checks and os.path.exists(cron_job_file):
|
||||
with open(cron_job_file, "r") as f:
|
||||
# no the best test in the world but except if we uses a shell
|
||||
# script parser I'm not expected a much more better way to do that
|
||||
no_checks = "--no-checks" in f.read()
|
||||
|
||||
command = "yunohost domain cert-renew --email\n"
|
||||
|
||||
if no_checks:
|
||||
# handle trailing "\n with ":-1"
|
||||
command = command[:-1] + " --no-checks\n"
|
||||
|
||||
with open(cron_job_file, "w") as f:
|
||||
f.write("#!/bin/bash\n")
|
||||
f.write(command)
|
||||
|
||||
_set_permissions(cron_job_file, "root", "root", 0o755)
|
||||
|
||||
|
||||
def _email_renewing_failed(domain, exception_message, stack=""):
|
||||
from_ = "certmanager@%s (Certificate Manager)" % domain
|
||||
to_ = "root"
|
||||
|
@ -898,20 +872,20 @@ def _check_domain_is_ready_for_ACME(domain):
|
|||
)
|
||||
|
||||
if not dnsrecords or not httpreachable:
|
||||
raise YunohostError("certmanager_domain_not_diagnosed_yet", domain=domain)
|
||||
raise YunohostValidationError("certmanager_domain_not_diagnosed_yet", domain=domain)
|
||||
|
||||
# Check if IP from DNS matches public IP
|
||||
if not dnsrecords.get("status") in [
|
||||
"SUCCESS",
|
||||
"WARNING",
|
||||
]: # Warning is for missing IPv6 record which ain't critical for ACME
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"certmanager_domain_dns_ip_differs_from_public_ip", domain=domain
|
||||
)
|
||||
|
||||
# Check if domain seems to be accessible through HTTP?
|
||||
if not httpreachable.get("status") == "SUCCESS":
|
||||
raise YunohostError("certmanager_domain_http_not_working", domain=domain)
|
||||
raise YunohostValidationError("certmanager_domain_http_not_working", domain=domain)
|
||||
|
||||
|
||||
# FIXME / TODO : ideally this should not be needed. There should be a proper
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import subprocess
|
||||
|
||||
from moulinette import m18n
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
||||
from yunohost.tools import Migration
|
||||
|
@ -23,7 +23,7 @@ class MyMigration(Migration):
|
|||
return
|
||||
|
||||
if not self.package_is_installed("postgresql-11"):
|
||||
raise YunohostError("migration_0017_postgresql_11_not_installed")
|
||||
raise YunohostValidationError("migration_0017_postgresql_11_not_installed")
|
||||
|
||||
# Make sure there's a 9.6 cluster
|
||||
try:
|
||||
|
@ -37,7 +37,7 @@ class MyMigration(Migration):
|
|||
if not space_used_by_directory(
|
||||
"/var/lib/postgresql/9.6"
|
||||
) > free_space_in_directory("/var/lib/postgresql"):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"migration_0017_not_enough_space", path="/var/lib/postgresql/"
|
||||
)
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class MyMigration(Migration):
|
|||
)
|
||||
|
||||
# Update LDAP schema restart slapd
|
||||
logger.info(m18n.n("migration_0011_update_LDAP_schema"))
|
||||
logger.info(m18n.n("migration_update_LDAP_schema"))
|
||||
regen_conf(names=["slapd"], force=True)
|
||||
|
||||
logger.info(m18n.n("migration_0019_add_new_attributes_in_ldap"))
|
||||
|
@ -78,6 +78,32 @@ class MyMigration(Migration):
|
|||
|
||||
ldap.update("cn=%s,ou=permission" % permission, update)
|
||||
|
||||
introduced_in_version = "4.1"
|
||||
|
||||
def run_after_system_restore(self):
|
||||
# Update LDAP database
|
||||
self.add_new_ldap_attributes()
|
||||
|
||||
def run_before_system_restore(self, app_id):
|
||||
from yunohost.app import app_setting
|
||||
from yunohost.utils.legacy import migrate_legacy_permission_settings
|
||||
|
||||
# Migrate old settings
|
||||
legacy_permission_settings = [
|
||||
"skipped_uris",
|
||||
"unprotected_uris",
|
||||
"protected_uris",
|
||||
"skipped_regex",
|
||||
"unprotected_regex",
|
||||
"protected_regex",
|
||||
]
|
||||
if any(
|
||||
app_setting(app_id, setting) is not None
|
||||
for setting in legacy_permission_settings
|
||||
):
|
||||
migrate_legacy_permission_settings(app=app_id)
|
||||
|
||||
|
||||
def run(self):
|
||||
|
||||
# FIXME : what do we really want to do here ...
|
||||
|
|
|
@ -37,7 +37,7 @@ from moulinette.utils.filesystem import (
|
|||
write_to_yaml,
|
||||
)
|
||||
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.hook import hook_list, hook_exec
|
||||
|
||||
logger = log.getActionLogger("yunohost.diagnosis")
|
||||
|
@ -59,11 +59,11 @@ def diagnosis_get(category, item):
|
|||
all_categories_names = [c for c, _ in all_categories]
|
||||
|
||||
if category not in all_categories_names:
|
||||
raise YunohostError("diagnosis_unknown_categories", categories=category)
|
||||
raise YunohostValidationError("diagnosis_unknown_categories", categories=category)
|
||||
|
||||
if isinstance(item, list):
|
||||
if any("=" not in criteria for criteria in item):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"Criterias should be of the form key=value (e.g. domain=yolo.test)"
|
||||
)
|
||||
|
||||
|
@ -91,7 +91,7 @@ def diagnosis_show(
|
|||
else:
|
||||
unknown_categories = [c for c in categories if c not in all_categories_names]
|
||||
if unknown_categories:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"diagnosis_unknown_categories", categories=", ".join(unknown_categories)
|
||||
)
|
||||
|
||||
|
@ -181,7 +181,7 @@ def diagnosis_run(
|
|||
else:
|
||||
unknown_categories = [c for c in categories if c not in all_categories_names]
|
||||
if unknown_categories:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"diagnosis_unknown_categories", categories=", ".join(unknown_categories)
|
||||
)
|
||||
|
||||
|
@ -278,14 +278,14 @@ def _diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
|
|||
|
||||
# Sanity checks for the provided arguments
|
||||
if len(filter_) == 0:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"You should provide at least one criteria being the diagnosis category to ignore"
|
||||
)
|
||||
category = filter_[0]
|
||||
if category not in all_categories_names:
|
||||
raise YunohostError("%s is not a diagnosis category" % category)
|
||||
raise YunohostValidationError("%s is not a diagnosis category" % category)
|
||||
if any("=" not in criteria for criteria in filter_[1:]):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"Criterias should be of the form key=value (e.g. domain=yolo.test)"
|
||||
)
|
||||
|
||||
|
@ -339,7 +339,7 @@ def _diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
|
|||
configuration["ignore_filters"][category] = []
|
||||
|
||||
if criterias not in configuration["ignore_filters"][category]:
|
||||
raise YunohostError("This filter does not exists.")
|
||||
raise YunohostValidationError("This filter does not exists.")
|
||||
|
||||
configuration["ignore_filters"][category].remove(criterias)
|
||||
_diagnosis_write_configuration(configuration)
|
||||
|
|
|
@ -28,7 +28,7 @@ import re
|
|||
|
||||
from moulinette import m18n, msettings, msignals
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import write_to_file
|
||||
|
||||
|
@ -101,16 +101,14 @@ def domain_add(operation_logger, domain, dyndns=False):
|
|||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
if domain.startswith("xmpp-upload."):
|
||||
raise YunohostError("domain_cannot_add_xmpp_upload")
|
||||
raise YunohostValidationError("domain_cannot_add_xmpp_upload")
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
try:
|
||||
ldap.validate_uniqueness({"virtualdomain": domain})
|
||||
except MoulinetteError:
|
||||
raise YunohostError("domain_exists")
|
||||
|
||||
operation_logger.start()
|
||||
raise YunohostValidationError("domain_exists")
|
||||
|
||||
# Lower domain to avoid some edge cases issues
|
||||
# See: https://forum.yunohost.org/t/invalid-domain-causes-diagnosis-web-to-fail-fr-on-demand/11765
|
||||
|
@ -119,17 +117,21 @@ def domain_add(operation_logger, domain, dyndns=False):
|
|||
# DynDNS domain
|
||||
if dyndns:
|
||||
|
||||
from yunohost.dyndns import dyndns_subscribe, _dyndns_provides, _guess_current_dyndns_domain
|
||||
from yunohost.dyndns import _dyndns_provides, _guess_current_dyndns_domain
|
||||
|
||||
# Do not allow to subscribe to multiple dyndns domains...
|
||||
if _guess_current_dyndns_domain("dyndns.yunohost.org") != (None, None):
|
||||
raise YunohostError('domain_dyndns_already_subscribed')
|
||||
raise YunohostValidationError('domain_dyndns_already_subscribed')
|
||||
|
||||
# Check that this domain can effectively be provided by
|
||||
# dyndns.yunohost.org. (i.e. is it a nohost.me / noho.st)
|
||||
if not _dyndns_provides("dyndns.yunohost.org", domain):
|
||||
raise YunohostError("domain_dyndns_root_unknown")
|
||||
raise YunohostValidationError("domain_dyndns_root_unknown")
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
if dyndns:
|
||||
from yunohost.dyndns import dyndns_subscribe
|
||||
# Actually subscribe
|
||||
dyndns_subscribe(domain=domain)
|
||||
|
||||
|
@ -197,7 +199,7 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
|
|||
# we don't want to check the domain exists because the ldap add may have
|
||||
# failed
|
||||
if not force and domain not in domain_list()['domains']:
|
||||
raise YunohostError('domain_name_unknown', domain=domain)
|
||||
raise YunohostValidationError('domain_name_unknown', domain=domain)
|
||||
|
||||
# Check domain is not the main domain
|
||||
if domain == _get_maindomain():
|
||||
|
@ -205,13 +207,13 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
|
|||
other_domains.remove(domain)
|
||||
|
||||
if other_domains:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"domain_cannot_remove_main",
|
||||
domain=domain,
|
||||
other_domains="\n * " + ("\n * ".join(other_domains)),
|
||||
)
|
||||
else:
|
||||
raise YunohostError("domain_cannot_remove_main_add_new_one", domain=domain)
|
||||
raise YunohostValidationError("domain_cannot_remove_main_add_new_one", domain=domain)
|
||||
|
||||
# Check if apps are installed on the domain
|
||||
apps_on_that_domain = []
|
||||
|
@ -234,9 +236,10 @@ def domain_remove(operation_logger, domain, remove_apps=False, force=False):
|
|||
for app, _ in apps_on_that_domain:
|
||||
app_remove(app)
|
||||
else:
|
||||
raise YunohostError('domain_uninstall_app_first', apps="\n".join([x[1] for x in apps_on_that_domain]))
|
||||
raise YunohostValidationError('domain_uninstall_app_first', apps="\n".join([x[1] for x in apps_on_that_domain]))
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
try:
|
||||
ldap.remove("virtualdomain=" + domain + ",ou=domains")
|
||||
|
@ -288,7 +291,7 @@ def domain_dns_conf(domain, ttl=None):
|
|||
"""
|
||||
|
||||
if domain not in domain_list()["domains"]:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
ttl = 3600 if ttl is None else ttl
|
||||
|
||||
|
@ -345,7 +348,7 @@ def domain_main_domain(operation_logger, new_main_domain=None):
|
|||
|
||||
# Check domain exists
|
||||
if new_main_domain not in domain_list()["domains"]:
|
||||
raise YunohostError("domain_name_unknown", domain=new_main_domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=new_main_domain)
|
||||
|
||||
operation_logger.related_to.append(("domain", new_main_domain))
|
||||
operation_logger.start()
|
||||
|
|
|
@ -36,7 +36,7 @@ from moulinette.utils.log import getActionLogger
|
|||
from moulinette.utils.filesystem import write_to_file, read_file
|
||||
from moulinette.utils.network import download_json
|
||||
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.domain import _get_maindomain, _build_dns_conf
|
||||
from yunohost.utils.network import get_public_ip, dig
|
||||
from yunohost.log import is_unit_operation
|
||||
|
@ -124,7 +124,7 @@ def dyndns_subscribe(
|
|||
"""
|
||||
|
||||
if _guess_current_dyndns_domain(subscribe_host) != (None, None):
|
||||
raise YunohostError('domain_dyndns_already_subscribed')
|
||||
raise YunohostValidationError('domain_dyndns_already_subscribed')
|
||||
|
||||
if domain is None:
|
||||
domain = _get_maindomain()
|
||||
|
@ -132,13 +132,13 @@ def dyndns_subscribe(
|
|||
|
||||
# Verify if domain is provided by subscribe_host
|
||||
if not _dyndns_provides(subscribe_host, domain):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"dyndns_domain_not_provided", domain=domain, provider=subscribe_host
|
||||
)
|
||||
|
||||
# Verify if domain is available
|
||||
if not _dyndns_available(subscribe_host, domain):
|
||||
raise YunohostError("dyndns_unavailable", domain=domain)
|
||||
raise YunohostValidationError("dyndns_unavailable", domain=domain)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -231,7 +231,7 @@ def dyndns_update(
|
|||
(domain, key) = _guess_current_dyndns_domain(dyn_host)
|
||||
|
||||
if domain is None:
|
||||
raise YunohostError('dyndns_no_domain_registered')
|
||||
raise YunohostValidationError('dyndns_no_domain_registered')
|
||||
|
||||
# If key is not given, pick the first file we find with the domain given
|
||||
else:
|
||||
|
@ -239,7 +239,7 @@ def dyndns_update(
|
|||
keys = glob.glob("/etc/yunohost/dyndns/K{0}.+*.private".format(domain))
|
||||
|
||||
if not keys:
|
||||
raise YunohostError("dyndns_key_not_found")
|
||||
raise YunohostValidationError("dyndns_key_not_found")
|
||||
|
||||
key = keys[0]
|
||||
|
||||
|
@ -260,7 +260,7 @@ def dyndns_update(
|
|||
ok, result = dig(dyn_host, "A")
|
||||
dyn_host_ip = result[0] if ok == "ok" and len(result) else None
|
||||
if not dyn_host_ip:
|
||||
raise YunohostError("Failed to resolve %s" % dyn_host)
|
||||
raise YunohostError("Failed to resolve %s" % dyn_host, raw_msg=True)
|
||||
|
||||
ok, result = dig(domain, rdtype, resolvers=[dyn_host_ip])
|
||||
if ok == "ok":
|
||||
|
|
|
@ -28,7 +28,7 @@ import yaml
|
|||
import miniupnpc
|
||||
|
||||
from moulinette import m18n
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from moulinette.utils import process
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.text import prependlines
|
||||
|
@ -188,18 +188,19 @@ def firewall_list(raw=False, by_ip_version=False, list_forwarded=False):
|
|||
for i in ["ipv4", "ipv6"]:
|
||||
f = firewall[i]
|
||||
# Combine TCP and UDP ports
|
||||
ports[i] = sorted(set(f["TCP"]) | set(f["UDP"]))
|
||||
ports[i] = sorted(set(f["TCP"]) | set(f["UDP"]), key=lambda p: int(p.split(':')[0]) if isinstance(p, str) else p)
|
||||
|
||||
if not by_ip_version:
|
||||
# Combine IPv4 and IPv6 ports
|
||||
ports = sorted(set(ports["ipv4"]) | set(ports["ipv6"]))
|
||||
ports = sorted(set(ports["ipv4"]) | set(ports["ipv6"]), key=lambda p: int(p.split(':')[0]) if isinstance(p, str) else p)
|
||||
|
||||
# Format returned dict
|
||||
ret = {"opened_ports": ports}
|
||||
if list_forwarded:
|
||||
# Combine TCP and UDP forwarded ports
|
||||
ret["forwarded_ports"] = sorted(
|
||||
set(firewall["uPnP"]["TCP"]) | set(firewall["uPnP"]["UDP"])
|
||||
set(firewall["uPnP"]["TCP"]) | set(firewall["uPnP"]["UDP"]),
|
||||
key=lambda p: int(p.split(':')[0]) if isinstance(p, str) else p
|
||||
)
|
||||
return ret
|
||||
|
||||
|
@ -366,7 +367,7 @@ def firewall_upnp(action="status", no_refresh=False):
|
|||
if action == "status":
|
||||
no_refresh = True
|
||||
else:
|
||||
raise YunohostError("action_invalid", action=action)
|
||||
raise YunohostValidationError("action_invalid", action=action)
|
||||
|
||||
# Refresh port mapping using UPnP
|
||||
if not no_refresh:
|
||||
|
|
|
@ -32,7 +32,7 @@ from glob import iglob
|
|||
from importlib import import_module
|
||||
|
||||
from moulinette import m18n, msettings
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from moulinette.utils import log
|
||||
from moulinette.utils.filesystem import read_json
|
||||
|
||||
|
@ -117,7 +117,7 @@ def hook_info(action, name):
|
|||
)
|
||||
|
||||
if not hooks:
|
||||
raise YunohostError("hook_name_unknown", name=name)
|
||||
raise YunohostValidationError("hook_name_unknown", name=name)
|
||||
return {
|
||||
"action": action,
|
||||
"name": name,
|
||||
|
@ -186,7 +186,7 @@ def hook_list(action, list_by="name", show_info=False):
|
|||
d.add(name)
|
||||
|
||||
else:
|
||||
raise YunohostError("hook_list_by_invalid")
|
||||
raise YunohostValidationError("hook_list_by_invalid")
|
||||
|
||||
def _append_folder(d, folder):
|
||||
# Iterate over and add hook from a folder
|
||||
|
@ -273,7 +273,7 @@ def hook_callback(
|
|||
try:
|
||||
hl = hooks_names[n]
|
||||
except KeyError:
|
||||
raise YunohostError("hook_name_unknown", n)
|
||||
raise YunohostValidationError("hook_name_unknown", n)
|
||||
# Iterate over hooks with this name
|
||||
for h in hl:
|
||||
# Update hooks dict
|
||||
|
|
|
@ -35,7 +35,7 @@ from logging import FileHandler, getLogger, Formatter
|
|||
|
||||
from moulinette import m18n, msettings
|
||||
from moulinette.core import MoulinetteError
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.utils.packages import get_ynh_package_version
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_file, read_yaml
|
||||
|
@ -191,7 +191,7 @@ def log_show(
|
|||
log_path = base_path + LOG_FILE_EXT
|
||||
|
||||
if not os.path.exists(md_path) and not os.path.exists(log_path):
|
||||
raise YunohostError("log_does_exists", log=path)
|
||||
raise YunohostValidationError("log_does_exists", log=path)
|
||||
|
||||
infos = {}
|
||||
|
||||
|
@ -414,7 +414,7 @@ class RedactingFormatter(Formatter):
|
|||
# This matches stuff like db_pwd=the_secret or admin_password=other_secret
|
||||
# (the secret part being at least 3 chars to avoid catching some lines like just "db_pwd=")
|
||||
# Some names like "key" or "manifest_key" are ignored, used in helpers like ynh_app_setting_set or ynh_read_manifest
|
||||
match = re.search(r'(pwd|pass|password|secret\w*|\w+key|token)=(\S{3,})$', record.strip())
|
||||
match = re.search(r'(pwd|pass|password|passphrase|secret\w*|\w+key|token)=(\S{3,})$', record.strip())
|
||||
if match and match.group(2) not in self.data_to_redact and match.group(1) not in ["key", "manifest_key"]:
|
||||
self.data_to_redact.append(match.group(2))
|
||||
except Exception as e:
|
||||
|
@ -631,10 +631,19 @@ class OperationLogger(object):
|
|||
"""
|
||||
Close properly the unit operation
|
||||
"""
|
||||
|
||||
# When the error happen's in the is_unit_operation try/except,
|
||||
# we want to inject the log ref in the exception, such that it may be
|
||||
# transmitted to the webadmin which can then redirect to the appropriate
|
||||
# log page
|
||||
if self.started_at and isinstance(error, Exception) and not isinstance(error, YunohostValidationError):
|
||||
error.log_ref = self.name
|
||||
|
||||
if self.ended_at is not None or self.started_at is None:
|
||||
return
|
||||
if error is not None and not isinstance(error, str):
|
||||
error = str(error)
|
||||
|
||||
self.ended_at = datetime.utcnow()
|
||||
self._error = error
|
||||
self._success = error is None
|
||||
|
|
|
@ -31,7 +31,7 @@ import random
|
|||
|
||||
from moulinette import m18n
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.log import is_unit_operation
|
||||
|
||||
logger = getActionLogger("yunohost.user")
|
||||
|
@ -175,14 +175,14 @@ def user_permission_update(
|
|||
|
||||
# Refuse to add "visitors" to mail, xmpp ... they require an account to make sense.
|
||||
if add and "visitors" in add and permission.split(".")[0] in SYSTEM_PERMS:
|
||||
raise YunohostError("permission_require_account", permission=permission)
|
||||
raise YunohostValidationError("permission_require_account", permission=permission)
|
||||
|
||||
# Refuse to add "visitors" to protected permission
|
||||
if (
|
||||
(add and "visitors" in add and existing_permission["protected"])
|
||||
or (remove and "visitors" in remove and existing_permission["protected"])
|
||||
) and not force:
|
||||
raise YunohostError("permission_protected", permission=permission)
|
||||
raise YunohostValidationError("permission_protected", permission=permission)
|
||||
|
||||
# Fetch currently allowed groups for this permission
|
||||
|
||||
|
@ -198,7 +198,7 @@ def user_permission_update(
|
|||
groups_to_add = [add] if not isinstance(add, list) else add
|
||||
for group in groups_to_add:
|
||||
if group not in all_existing_groups:
|
||||
raise YunohostError("group_unknown", group=group)
|
||||
raise YunohostValidationError("group_unknown", group=group)
|
||||
if group in current_allowed_groups:
|
||||
logger.warning(
|
||||
m18n.n(
|
||||
|
@ -326,7 +326,7 @@ def user_permission_info(permission):
|
|||
permission, None
|
||||
)
|
||||
if existing_permission is None:
|
||||
raise YunohostError("permission_not_found", permission=permission)
|
||||
raise YunohostValidationError("permission_not_found", permission=permission)
|
||||
|
||||
return existing_permission
|
||||
|
||||
|
@ -391,7 +391,7 @@ def permission_create(
|
|||
if ldap.get_conflict(
|
||||
{"cn": permission}, base_dn="ou=permission,dc=yunohost,dc=org"
|
||||
):
|
||||
raise YunohostError("permission_already_exist", permission=permission)
|
||||
raise YunohostValidationError("permission_already_exist", permission=permission)
|
||||
|
||||
# Get random GID
|
||||
all_gid = {x.gr_gid for x in grp.getgrall()}
|
||||
|
@ -427,7 +427,7 @@ def permission_create(
|
|||
all_existing_groups = user_group_list()["groups"].keys()
|
||||
for group in allowed or []:
|
||||
if group not in all_existing_groups:
|
||||
raise YunohostError("group_unknown", group=group)
|
||||
raise YunohostValidationError("group_unknown", group=group)
|
||||
|
||||
operation_logger.related_to.append(("app", permission.split(".")[0]))
|
||||
operation_logger.start()
|
||||
|
@ -594,7 +594,7 @@ def permission_delete(operation_logger, permission, force=False, sync_perm=True)
|
|||
permission = permission + ".main"
|
||||
|
||||
if permission.endswith(".main") and not force:
|
||||
raise YunohostError("permission_cannot_remove_main")
|
||||
raise YunohostValidationError("permission_cannot_remove_main")
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
|
@ -861,7 +861,7 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
|
|||
try:
|
||||
re.compile(regex)
|
||||
except Exception:
|
||||
raise YunohostError("invalid_regex", regex=regex)
|
||||
raise YunohostValidationError("invalid_regex", regex=regex)
|
||||
|
||||
if url.startswith("re:"):
|
||||
|
||||
|
@ -874,12 +874,12 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
|
|||
# regex with domain
|
||||
|
||||
if "/" not in url:
|
||||
raise YunohostError("regex_with_only_domain")
|
||||
raise YunohostValidationError("regex_with_only_domain")
|
||||
domain, path = url[3:].split("/", 1)
|
||||
path = "/" + path
|
||||
|
||||
if domain.replace("%", "").replace("\\", "") not in domains:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
validate_regex(path)
|
||||
|
||||
|
@ -914,7 +914,7 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
|
|||
sanitized_url = domain + path
|
||||
|
||||
if domain not in domains:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
_assert_no_conflicting_apps(domain, path, ignore_app=app)
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ from glob import glob
|
|||
from datetime import datetime
|
||||
|
||||
from moulinette import m18n
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from moulinette.utils.process import check_output
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_file, append_to_file, write_to_file
|
||||
|
@ -145,7 +145,7 @@ def service_remove(name):
|
|||
services = _get_services()
|
||||
|
||||
if name not in services:
|
||||
raise YunohostError("service_unknown", service=name)
|
||||
raise YunohostValidationError("service_unknown", service=name)
|
||||
|
||||
del services[name]
|
||||
try:
|
||||
|
@ -325,7 +325,7 @@ def service_status(names=[]):
|
|||
# Validate service names requested
|
||||
for name in names:
|
||||
if name not in services.keys():
|
||||
raise YunohostError("service_unknown", service=name)
|
||||
raise YunohostValidationError("service_unknown", service=name)
|
||||
|
||||
# Filter only requested servivces
|
||||
services = {k: v for k, v in services.items() if k in names}
|
||||
|
@ -465,6 +465,7 @@ def _get_and_format_service_status(service, infos):
|
|||
if p.returncode == 0:
|
||||
output["configuration"] = "valid"
|
||||
else:
|
||||
out = out.decode()
|
||||
output["configuration"] = "broken"
|
||||
output["configuration-details"] = out.strip().split("\n")
|
||||
|
||||
|
@ -484,7 +485,7 @@ def service_log(name, number=50):
|
|||
number = int(number)
|
||||
|
||||
if name not in services.keys():
|
||||
raise YunohostError("service_unknown", service=name)
|
||||
raise YunohostValidationError("service_unknown", service=name)
|
||||
|
||||
log_list = services[name].get("log", [])
|
||||
|
||||
|
@ -545,7 +546,7 @@ def service_regen_conf(
|
|||
|
||||
for name in names:
|
||||
if name not in services.keys():
|
||||
raise YunohostError("service_unknown", service=name)
|
||||
raise YunohostValidationError("service_unknown", service=name)
|
||||
|
||||
if names is []:
|
||||
names = list(services.keys())
|
||||
|
@ -568,7 +569,7 @@ def _run_service_command(action, service):
|
|||
"""
|
||||
services = _get_services()
|
||||
if service not in services.keys():
|
||||
raise YunohostError("service_unknown", service=service)
|
||||
raise YunohostValidationError("service_unknown", service=service)
|
||||
|
||||
possible_actions = [
|
||||
"start",
|
||||
|
|
|
@ -6,7 +6,7 @@ from datetime import datetime
|
|||
from collections import OrderedDict
|
||||
|
||||
from moulinette import m18n
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from yunohost.regenconf import regen_conf
|
||||
|
||||
|
@ -94,10 +94,10 @@ DEFAULTS = OrderedDict(
|
|||
("smtp.relay.user", {"type": "string", "default": ""}),
|
||||
("smtp.relay.password", {"type": "string", "default": ""}),
|
||||
("backup.compress_tar_archives", {"type": "bool", "default": False}),
|
||||
("ssowat.panel_overlay.enabled", {"type": "bool", "default": True}),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def settings_get(key, full=False):
|
||||
"""
|
||||
Get an entry value in the settings
|
||||
|
@ -109,7 +109,7 @@ def settings_get(key, full=False):
|
|||
settings = _get_settings()
|
||||
|
||||
if key not in settings:
|
||||
raise YunohostError("global_settings_key_doesnt_exists", settings_key=key)
|
||||
raise YunohostValidationError("global_settings_key_doesnt_exists", settings_key=key)
|
||||
|
||||
if full:
|
||||
return settings[key]
|
||||
|
@ -137,7 +137,7 @@ def settings_set(key, value):
|
|||
settings = _get_settings()
|
||||
|
||||
if key not in settings:
|
||||
raise YunohostError("global_settings_key_doesnt_exists", settings_key=key)
|
||||
raise YunohostValidationError("global_settings_key_doesnt_exists", settings_key=key)
|
||||
|
||||
key_type = settings[key]["type"]
|
||||
|
||||
|
@ -146,7 +146,7 @@ def settings_set(key, value):
|
|||
if boolean_value[0]:
|
||||
value = boolean_value[1]
|
||||
else:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"global_settings_bad_type_for_setting",
|
||||
setting=key,
|
||||
received_type=type(value).__name__,
|
||||
|
@ -158,14 +158,14 @@ def settings_set(key, value):
|
|||
try:
|
||||
value = int(value)
|
||||
except Exception:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"global_settings_bad_type_for_setting",
|
||||
setting=key,
|
||||
received_type=type(value).__name__,
|
||||
expected_type=key_type,
|
||||
)
|
||||
else:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"global_settings_bad_type_for_setting",
|
||||
setting=key,
|
||||
received_type=type(value).__name__,
|
||||
|
@ -173,7 +173,7 @@ def settings_set(key, value):
|
|||
)
|
||||
elif key_type == "string":
|
||||
if not isinstance(value, str):
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"global_settings_bad_type_for_setting",
|
||||
setting=key,
|
||||
received_type=type(value).__name__,
|
||||
|
@ -181,14 +181,14 @@ def settings_set(key, value):
|
|||
)
|
||||
elif key_type == "enum":
|
||||
if value not in settings[key]["choices"]:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"global_settings_bad_choice_for_enum",
|
||||
setting=key,
|
||||
choice=str(value),
|
||||
available_choices=", ".join(settings[key]["choices"]),
|
||||
)
|
||||
else:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"global_settings_unknown_type", setting=key, unknown_type=key_type
|
||||
)
|
||||
|
||||
|
@ -214,7 +214,7 @@ def settings_reset(key):
|
|||
settings = _get_settings()
|
||||
|
||||
if key not in settings:
|
||||
raise YunohostError("global_settings_key_doesnt_exists", settings_key=key)
|
||||
raise YunohostValidationError("global_settings_key_doesnt_exists", settings_key=key)
|
||||
|
||||
settings[key]["value"] = settings[key]["default"]
|
||||
_save_settings(settings)
|
||||
|
@ -304,7 +304,7 @@ def _get_settings():
|
|||
)
|
||||
unknown_settings[key] = value
|
||||
except Exception as e:
|
||||
raise YunohostError("global_settings_cant_open_settings", reason=e)
|
||||
raise YunohostValidationError("global_settings_cant_open_settings", reason=e)
|
||||
|
||||
if unknown_settings:
|
||||
try:
|
||||
|
@ -376,7 +376,7 @@ def trigger_post_change_hook(setting_name, old_value, new_value):
|
|||
#
|
||||
# ===========================================
|
||||
|
||||
|
||||
@post_change_hook("ssowat.panel_overlay.enabled")
|
||||
@post_change_hook("security.nginx.compatibility")
|
||||
def reconfigure_nginx(setting_name, old_value, new_value):
|
||||
if old_value != new_value:
|
||||
|
|
|
@ -5,7 +5,7 @@ import os
|
|||
import pwd
|
||||
import subprocess
|
||||
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostValidationError
|
||||
from moulinette.utils.filesystem import read_file, write_to_file, chown, chmod, mkdir
|
||||
|
||||
SSHD_CONFIG_PATH = "/etc/ssh/sshd_config"
|
||||
|
@ -21,7 +21,7 @@ def user_ssh_allow(username):
|
|||
# TODO it would be good to support different kind of shells
|
||||
|
||||
if not _get_user_for_ssh(username):
|
||||
raise YunohostError("user_unknown", user=username)
|
||||
raise YunohostValidationError("user_unknown", user=username)
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
|
@ -43,7 +43,7 @@ def user_ssh_disallow(username):
|
|||
# TODO it would be good to support different kind of shells
|
||||
|
||||
if not _get_user_for_ssh(username):
|
||||
raise YunohostError("user_unknown", user=username)
|
||||
raise YunohostValidationError("user_unknown", user=username)
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
|
|
|
@ -19,13 +19,11 @@ from yunohost.app import (
|
|||
logger,
|
||||
APPS_CATALOG_CACHE,
|
||||
APPS_CATALOG_CONF,
|
||||
APPS_CATALOG_CRON_PATH,
|
||||
APPS_CATALOG_API_VERSION,
|
||||
APPS_CATALOG_DEFAULT_URL,
|
||||
)
|
||||
|
||||
APPS_CATALOG_DEFAULT_URL_FULL = _actual_apps_catalog_api_url(APPS_CATALOG_DEFAULT_URL)
|
||||
CRON_FOLDER, CRON_NAME = APPS_CATALOG_CRON_PATH.rsplit("/", 1)
|
||||
|
||||
DUMMY_APP_CATALOG = """{
|
||||
"apps": {
|
||||
|
@ -50,10 +48,6 @@ def setup_function(function):
|
|||
# Clear apps catalog cache
|
||||
shutil.rmtree(APPS_CATALOG_CACHE, ignore_errors=True)
|
||||
|
||||
# Clear apps_catalog cron
|
||||
if os.path.exists(APPS_CATALOG_CRON_PATH):
|
||||
os.remove(APPS_CATALOG_CRON_PATH)
|
||||
|
||||
# Clear apps_catalog conf
|
||||
if os.path.exists(APPS_CATALOG_CONF):
|
||||
os.remove(APPS_CATALOG_CONF)
|
||||
|
@ -67,11 +61,6 @@ def teardown_function(function):
|
|||
shutil.rmtree(APPS_CATALOG_CACHE, ignore_errors=True)
|
||||
|
||||
|
||||
def cron_job_is_there():
|
||||
r = os.system("run-parts -v --test %s | grep %s" % (CRON_FOLDER, CRON_NAME))
|
||||
return r == 0
|
||||
|
||||
|
||||
#
|
||||
# ################################################
|
||||
#
|
||||
|
@ -83,17 +72,12 @@ def test_apps_catalog_init(mocker):
|
|||
assert not glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
# Conf doesn't exist yet
|
||||
assert not os.path.exists(APPS_CATALOG_CONF)
|
||||
# Conf doesn't exist yet
|
||||
assert not os.path.exists(APPS_CATALOG_CRON_PATH)
|
||||
|
||||
# Initialize ...
|
||||
mocker.spy(m18n, "n")
|
||||
_initialize_apps_catalog_system()
|
||||
m18n.n.assert_any_call("apps_catalog_init_success")
|
||||
|
||||
# Then there's a cron enabled
|
||||
assert cron_job_is_there()
|
||||
|
||||
# And a conf with at least one list
|
||||
assert os.path.exists(APPS_CATALOG_CONF)
|
||||
apps_catalog_list = _read_apps_catalog_list()
|
||||
|
|
|
@ -47,8 +47,8 @@ def setup_function(function):
|
|||
for m in function.__dict__.get("pytestmark", [])
|
||||
}
|
||||
|
||||
if "with_wordpress_archive_from_2p4" in markers:
|
||||
add_archive_wordpress_from_2p4()
|
||||
if "with_wordpress_archive_from_3p8" in markers:
|
||||
add_archive_wordpress_from_3p8()
|
||||
assert len(backup_list()["archives"]) == 1
|
||||
|
||||
if "with_legacy_app_installed" in markers:
|
||||
|
@ -70,8 +70,8 @@ def setup_function(function):
|
|||
)
|
||||
assert app_is_installed("backup_recommended_app")
|
||||
|
||||
if "with_system_archive_from_2p4" in markers:
|
||||
add_archive_system_from_2p4()
|
||||
if "with_system_archive_from_3p8" in markers:
|
||||
add_archive_system_from_3p8()
|
||||
assert len(backup_list()["archives"]) == 1
|
||||
|
||||
if "with_permission_app_installed" in markers:
|
||||
|
@ -107,7 +107,8 @@ def teardown_function(function):
|
|||
|
||||
if "with_custom_domain" in markers:
|
||||
domain = markers["with_custom_domain"]["args"][0]
|
||||
domain_remove(domain)
|
||||
if domain != maindomain:
|
||||
domain_remove(domain)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -147,7 +148,7 @@ def backup_test_dependencies_are_met():
|
|||
|
||||
# Dummy test apps (or backup archives)
|
||||
assert os.path.exists(
|
||||
os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4")
|
||||
os.path.join(get_test_apps_dir(), "backup_wordpress_from_3p8")
|
||||
)
|
||||
assert os.path.exists(os.path.join(get_test_apps_dir(), "legacy_app_ynh"))
|
||||
assert os.path.exists(
|
||||
|
@ -216,39 +217,25 @@ def install_app(app, path, additionnal_args=""):
|
|||
)
|
||||
|
||||
|
||||
def add_archive_wordpress_from_2p4():
|
||||
def add_archive_wordpress_from_3p8():
|
||||
|
||||
os.system("mkdir -p /home/yunohost.backup/archives")
|
||||
|
||||
os.system(
|
||||
"cp "
|
||||
+ os.path.join(
|
||||
get_test_apps_dir(), "backup_wordpress_from_2p4/backup.info.json"
|
||||
)
|
||||
+ " /home/yunohost.backup/archives/backup_wordpress_from_2p4.info.json"
|
||||
)
|
||||
|
||||
os.system(
|
||||
"cp "
|
||||
+ os.path.join(get_test_apps_dir(), "backup_wordpress_from_2p4/backup.tar.gz")
|
||||
+ " /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz"
|
||||
+ os.path.join(get_test_apps_dir(), "backup_wordpress_from_3p8/backup.tar.gz")
|
||||
+ " /home/yunohost.backup/archives/backup_wordpress_from_3p8.tar.gz"
|
||||
)
|
||||
|
||||
|
||||
def add_archive_system_from_2p4():
|
||||
def add_archive_system_from_3p8():
|
||||
|
||||
os.system("mkdir -p /home/yunohost.backup/archives")
|
||||
|
||||
os.system(
|
||||
"cp "
|
||||
+ os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.info.json")
|
||||
+ " /home/yunohost.backup/archives/backup_system_from_2p4.info.json"
|
||||
)
|
||||
|
||||
os.system(
|
||||
"cp "
|
||||
+ os.path.join(get_test_apps_dir(), "backup_system_from_2p4/backup.tar.gz")
|
||||
+ " /home/yunohost.backup/archives/backup_system_from_2p4.tar.gz"
|
||||
+ os.path.join(get_test_apps_dir(), "backup_system_from_3p8/backup.tar.gz")
|
||||
+ " /home/yunohost.backup/archives/backup_system_from_3p8.tar.gz"
|
||||
)
|
||||
|
||||
|
||||
|
@ -314,12 +301,12 @@ def test_backup_and_restore_all_sys(mocker):
|
|||
|
||||
|
||||
#
|
||||
# System restore from 2.4 #
|
||||
# System restore from 3.8 #
|
||||
#
|
||||
|
||||
|
||||
@pytest.mark.with_system_archive_from_2p4
|
||||
def test_restore_system_from_Ynh2p4(monkeypatch, mocker):
|
||||
@pytest.mark.with_system_archive_from_3p8
|
||||
def test_restore_system_from_Ynh3p8(monkeypatch, mocker):
|
||||
|
||||
# Backup current system
|
||||
with message(mocker, "backup_created"):
|
||||
|
@ -327,7 +314,7 @@ def test_restore_system_from_Ynh2p4(monkeypatch, mocker):
|
|||
archives = backup_list()["archives"]
|
||||
assert len(archives) == 2
|
||||
|
||||
# Restore system archive from 2.4
|
||||
# Restore system archive from 3.8
|
||||
try:
|
||||
with message(mocker, "restore_complete"):
|
||||
backup_restore(
|
||||
|
@ -464,9 +451,9 @@ def test_backup_using_copy_method(mocker):
|
|||
#
|
||||
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
@pytest.mark.with_wordpress_archive_from_3p8
|
||||
@pytest.mark.with_custom_domain("yolo.test")
|
||||
def test_restore_app_wordpress_from_Ynh2p4(mocker):
|
||||
def test_restore_app_wordpress_from_Ynh3p8(mocker):
|
||||
|
||||
with message(mocker, "restore_complete"):
|
||||
backup_restore(
|
||||
|
@ -474,19 +461,19 @@ def test_restore_app_wordpress_from_Ynh2p4(mocker):
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
@pytest.mark.with_wordpress_archive_from_3p8
|
||||
@pytest.mark.with_custom_domain("yolo.test")
|
||||
def test_restore_app_script_failure_handling(monkeypatch, mocker):
|
||||
def custom_hook_exec(name, *args, **kwargs):
|
||||
if os.path.basename(name).startswith("restore"):
|
||||
monkeypatch.undo()
|
||||
raise Exception
|
||||
return (1, None)
|
||||
|
||||
monkeypatch.setattr("yunohost.backup.hook_exec", custom_hook_exec)
|
||||
|
||||
assert not _is_installed("wordpress")
|
||||
|
||||
with message(mocker, "restore_app_failed", app="wordpress"):
|
||||
with message(mocker, "app_restore_script_failed"):
|
||||
with raiseYunohostError(mocker, "restore_nothings_done"):
|
||||
backup_restore(
|
||||
system=None, name=backup_list()["archives"][0], apps=["wordpress"]
|
||||
|
@ -495,7 +482,7 @@ def test_restore_app_script_failure_handling(monkeypatch, mocker):
|
|||
assert not _is_installed("wordpress")
|
||||
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
@pytest.mark.with_wordpress_archive_from_3p8
|
||||
def test_restore_app_not_enough_free_space(monkeypatch, mocker):
|
||||
def custom_free_space_in_directory(dirpath):
|
||||
return 0
|
||||
|
@ -514,7 +501,7 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker):
|
|||
assert not _is_installed("wordpress")
|
||||
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
@pytest.mark.with_wordpress_archive_from_3p8
|
||||
def test_restore_app_not_in_backup(mocker):
|
||||
|
||||
assert not _is_installed("wordpress")
|
||||
|
@ -530,7 +517,7 @@ def test_restore_app_not_in_backup(mocker):
|
|||
assert not _is_installed("yoloswag")
|
||||
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
@pytest.mark.with_wordpress_archive_from_3p8
|
||||
@pytest.mark.with_custom_domain("yolo.test")
|
||||
def test_restore_app_already_installed(mocker):
|
||||
|
||||
|
@ -648,18 +635,18 @@ def test_restore_archive_with_no_json(mocker):
|
|||
backup_restore(name="badbackup", force=True)
|
||||
|
||||
|
||||
@pytest.mark.with_wordpress_archive_from_2p4
|
||||
@pytest.mark.with_wordpress_archive_from_3p8
|
||||
def test_restore_archive_with_bad_archive(mocker):
|
||||
|
||||
# Break the archive
|
||||
os.system(
|
||||
"head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz > /home/yunohost.backup/archives/backup_wordpress_from_2p4.tar.gz"
|
||||
"head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_3p8.tar.gz > /home/yunohost.backup/archives/backup_wordpress_from_3p8_bad.tar.gz"
|
||||
)
|
||||
|
||||
assert "backup_wordpress_from_2p4" in backup_list()["archives"]
|
||||
assert "backup_wordpress_from_3p8_bad" in backup_list()["archives"]
|
||||
|
||||
with raiseYunohostError(mocker, "backup_archive_open_failed"):
|
||||
backup_restore(name="backup_wordpress_from_2p4", force=True)
|
||||
with raiseYunohostError(mocker, "backup_archive_corrupted"):
|
||||
backup_restore(name="backup_wordpress_from_3p8_bad", force=True)
|
||||
|
||||
clean_tmp_backup_directory()
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import yaml
|
|||
import subprocess
|
||||
import pwd
|
||||
from importlib import import_module
|
||||
from packaging import version
|
||||
|
||||
from moulinette import msignals, m18n
|
||||
from moulinette.utils.log import getActionLogger
|
||||
|
@ -51,7 +52,7 @@ from yunohost.utils.packages import (
|
|||
_list_upgradable_apt_packages,
|
||||
ynh_packages_version,
|
||||
)
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.log import is_unit_operation, OperationLogger
|
||||
|
||||
# FIXME this is a duplicate from apps.py
|
||||
|
@ -156,7 +157,7 @@ def tools_adminpw(new_password, check_strength=True):
|
|||
# UNIX seems to not like password longer than 127 chars ...
|
||||
# e.g. SSH login gets broken (or even 'su admin' when entering the password)
|
||||
if len(new_password) >= 127:
|
||||
raise YunohostError("admin_password_too_long")
|
||||
raise YunohostValidationError("admin_password_too_long")
|
||||
|
||||
new_hash = _hash_user_password(new_password)
|
||||
|
||||
|
@ -285,10 +286,10 @@ def tools_postinstall(
|
|||
|
||||
# Do some checks at first
|
||||
if os.path.isfile("/etc/yunohost/installed"):
|
||||
raise YunohostError("yunohost_already_installed")
|
||||
raise YunohostValidationError("yunohost_already_installed")
|
||||
|
||||
if os.path.isdir("/etc/yunohost/apps") and os.listdir("/etc/yunohost/apps") != []:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"It looks like you're trying to re-postinstall a system that was already working previously ... If you recently had some bug or issues with your installation, please first discuss with the team on how to fix the situation instead of savagely re-running the postinstall ...",
|
||||
raw_msg=True,
|
||||
)
|
||||
|
@ -301,7 +302,7 @@ def tools_postinstall(
|
|||
)
|
||||
GB = 1024 ** 3
|
||||
if not force_diskspace and main_space < 10 * GB:
|
||||
raise YunohostError("postinstall_low_rootfsspace")
|
||||
raise YunohostValidationError("postinstall_low_rootfsspace")
|
||||
|
||||
# Check password
|
||||
if not force_password:
|
||||
|
@ -331,14 +332,14 @@ def tools_postinstall(
|
|||
dyndns = True
|
||||
# If not, abort the postinstall
|
||||
else:
|
||||
raise YunohostError("dyndns_unavailable", domain=domain)
|
||||
raise YunohostValidationError("dyndns_unavailable", domain=domain)
|
||||
else:
|
||||
dyndns = False
|
||||
else:
|
||||
dyndns = False
|
||||
|
||||
if os.system("iptables -V >/dev/null 2>/dev/null") != 0:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"iptables/nftables does not seems to be working on your setup. You may be in a container or your kernel does have the proper modules loaded. Sometimes, rebooting the machine may solve the issue.",
|
||||
raw_msg=True,
|
||||
)
|
||||
|
@ -537,11 +538,11 @@ def tools_upgrade(
|
|||
from yunohost.utils import packages
|
||||
|
||||
if packages.dpkg_is_broken():
|
||||
raise YunohostError("dpkg_is_broken")
|
||||
raise YunohostValidationError("dpkg_is_broken")
|
||||
|
||||
# Check for obvious conflict with other dpkg/apt commands already running in parallel
|
||||
if not packages.dpkg_lock_available():
|
||||
raise YunohostError("dpkg_lock_not_available")
|
||||
raise YunohostValidationError("dpkg_lock_not_available")
|
||||
|
||||
# Legacy options management (--system, --apps)
|
||||
if target is None:
|
||||
|
@ -549,10 +550,10 @@ def tools_upgrade(
|
|||
logger.warning("Using 'yunohost tools upgrade' with --apps / --system is deprecated, just write 'yunohost tools upgrade apps' or 'system' (no -- prefix anymore)")
|
||||
|
||||
if (system, apps) == (True, True):
|
||||
raise YunohostError("tools_upgrade_cant_both")
|
||||
raise YunohostValidationError("tools_upgrade_cant_both")
|
||||
|
||||
if (system, apps) == (False, False):
|
||||
raise YunohostError("tools_upgrade_at_least_one")
|
||||
raise YunohostValidationError("tools_upgrade_at_least_one")
|
||||
|
||||
target = "apps" if apps else "system"
|
||||
|
||||
|
@ -840,7 +841,7 @@ def tools_migrations_list(pending=False, done=False):
|
|||
|
||||
# Check for option conflict
|
||||
if pending and done:
|
||||
raise YunohostError("migrations_list_conflict_pending_done")
|
||||
raise YunohostValidationError("migrations_list_conflict_pending_done")
|
||||
|
||||
# Get all migrations
|
||||
migrations = _get_migrations_list()
|
||||
|
@ -890,17 +891,17 @@ def tools_migrations_run(
|
|||
if m.id == target or m.name == target or m.id.split("_")[0] == target:
|
||||
return m
|
||||
|
||||
raise YunohostError("migrations_no_such_migration", id=target)
|
||||
raise YunohostValidationError("migrations_no_such_migration", id=target)
|
||||
|
||||
# auto, skip and force are exclusive options
|
||||
if auto + skip + force_rerun > 1:
|
||||
raise YunohostError("migrations_exclusive_options")
|
||||
raise YunohostValidationError("migrations_exclusive_options")
|
||||
|
||||
# If no target specified
|
||||
if not targets:
|
||||
# skip, revert or force require explicit targets
|
||||
if skip or force_rerun:
|
||||
raise YunohostError("migrations_must_provide_explicit_targets")
|
||||
raise YunohostValidationError("migrations_must_provide_explicit_targets")
|
||||
|
||||
# Otherwise, targets are all pending migrations
|
||||
targets = [m for m in all_migrations if m.state == "pending"]
|
||||
|
@ -912,11 +913,11 @@ def tools_migrations_run(
|
|||
pending = [t.id for t in targets if t.state == "pending"]
|
||||
|
||||
if skip and done:
|
||||
raise YunohostError("migrations_not_pending_cant_skip", ids=", ".join(done))
|
||||
raise YunohostValidationError("migrations_not_pending_cant_skip", ids=", ".join(done))
|
||||
if force_rerun and pending:
|
||||
raise YunohostError("migrations_pending_cant_rerun", ids=", ".join(pending))
|
||||
raise YunohostValidationError("migrations_pending_cant_rerun", ids=", ".join(pending))
|
||||
if not (skip or force_rerun) and done:
|
||||
raise YunohostError("migrations_already_ran", ids=", ".join(done))
|
||||
raise YunohostValidationError("migrations_already_ran", ids=", ".join(done))
|
||||
|
||||
# So, is there actually something to do ?
|
||||
if not targets:
|
||||
|
@ -1116,6 +1117,44 @@ def _skip_all_migrations():
|
|||
write_to_yaml(MIGRATIONS_STATE_PATH, new_states)
|
||||
|
||||
|
||||
def _tools_migrations_run_after_system_restore(backup_version):
|
||||
|
||||
all_migrations = _get_migrations_list()
|
||||
|
||||
for migration in all_migrations:
|
||||
if hasattr(migration, "introduced_in_version") \
|
||||
and version.parse(migration.introduced_in_version) > version.parse(backup_version) \
|
||||
and hasattr(migration, "run_after_system_restore"):
|
||||
try:
|
||||
logger.info(m18n.n("migrations_running_forward", id=migration.id))
|
||||
migration.run_after_system_restore()
|
||||
except Exception as e:
|
||||
msg = m18n.n(
|
||||
"migrations_migration_has_failed", exception=e, id=migration.id
|
||||
)
|
||||
logger.error(msg, exc_info=1)
|
||||
raise
|
||||
|
||||
|
||||
def _tools_migrations_run_before_app_restore(backup_version, app_id):
|
||||
|
||||
all_migrations = _get_migrations_list()
|
||||
|
||||
for migration in all_migrations:
|
||||
if hasattr(migration, "introduced_in_version") \
|
||||
and version.parse(migration.introduced_in_version) > version.parse(backup_version) \
|
||||
and hasattr(migration, "run_before_app_restore"):
|
||||
try:
|
||||
logger.info(m18n.n("migrations_running_forward", id=migration.id))
|
||||
migration.run_before_app_restore(app_id)
|
||||
except Exception as e:
|
||||
msg = m18n.n(
|
||||
"migrations_migration_has_failed", exception=e, id=migration.id
|
||||
)
|
||||
logger.error(msg, exc_info=1)
|
||||
raise
|
||||
|
||||
|
||||
class Migration(object):
|
||||
|
||||
# Those are to be implemented by daughter classes
|
||||
|
|
|
@ -37,7 +37,7 @@ from moulinette import msignals, msettings, m18n
|
|||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.process import check_output
|
||||
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostError, YunohostValidationError
|
||||
from yunohost.service import service_status
|
||||
from yunohost.log import is_unit_operation
|
||||
|
||||
|
@ -125,7 +125,7 @@ def user_create(
|
|||
# Validate domain used for email address/xmpp account
|
||||
if domain is None:
|
||||
if msettings.get("interface") == "api":
|
||||
raise YunohostError("Invalide usage, specify domain argument")
|
||||
raise YunohostValidationError("Invalid usage, you should specify a domain argument")
|
||||
else:
|
||||
# On affiche les differents domaines possibles
|
||||
msignals.display(m18n.n("domains_available"))
|
||||
|
@ -141,24 +141,24 @@ def user_create(
|
|||
|
||||
# Check that the domain exists
|
||||
if domain not in domain_list()["domains"]:
|
||||
raise YunohostError("domain_name_unknown", domain=domain)
|
||||
raise YunohostValidationError("domain_name_unknown", domain=domain)
|
||||
|
||||
mail = username + "@" + domain
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
if username in user_list()["users"]:
|
||||
raise YunohostError("user_already_exists", user=username)
|
||||
raise YunohostValidationError("user_already_exists", user=username)
|
||||
|
||||
# Validate uniqueness of username and mail in LDAP
|
||||
try:
|
||||
ldap.validate_uniqueness({"uid": username, "mail": mail, "cn": username})
|
||||
except Exception as e:
|
||||
raise YunohostError("user_creation_failed", user=username, error=e)
|
||||
raise YunohostValidationError("user_creation_failed", user=username, error=e)
|
||||
|
||||
# Validate uniqueness of username in system users
|
||||
all_existing_usernames = {x.pw_name for x in pwd.getpwall()}
|
||||
if username in all_existing_usernames:
|
||||
raise YunohostError("system_username_exists")
|
||||
raise YunohostValidationError("system_username_exists")
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
aliases = [
|
||||
|
@ -170,7 +170,7 @@ def user_create(
|
|||
]
|
||||
|
||||
if mail in aliases:
|
||||
raise YunohostError("mail_unavailable")
|
||||
raise YunohostValidationError("mail_unavailable")
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -264,7 +264,7 @@ def user_delete(operation_logger, username, purge=False):
|
|||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
if username not in user_list()["users"]:
|
||||
raise YunohostError("user_unknown", user=username)
|
||||
raise YunohostValidationError("user_unknown", user=username)
|
||||
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -347,7 +347,7 @@ def user_update(
|
|||
attrs=attrs_to_fetch,
|
||||
)
|
||||
if not result:
|
||||
raise YunohostError("user_unknown", user=username)
|
||||
raise YunohostValidationError("user_unknown", user=username)
|
||||
user = result[0]
|
||||
env_dict = {"YNH_USER_USERNAME": username}
|
||||
|
||||
|
@ -396,13 +396,13 @@ def user_update(
|
|||
try:
|
||||
ldap.validate_uniqueness({"mail": mail})
|
||||
except Exception as e:
|
||||
raise YunohostError("user_update_failed", user=username, error=e)
|
||||
raise YunohostValidationError("user_update_failed", user=username, error=e)
|
||||
if mail[mail.find("@") + 1 :] not in domains:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"mail_domain_unknown", domain=mail[mail.find("@") + 1 :]
|
||||
)
|
||||
if mail in aliases:
|
||||
raise YunohostError("mail_unavailable")
|
||||
raise YunohostValidationError("mail_unavailable")
|
||||
|
||||
del user["mail"][0]
|
||||
new_attr_dict["mail"] = [mail] + user["mail"]
|
||||
|
@ -414,9 +414,9 @@ def user_update(
|
|||
try:
|
||||
ldap.validate_uniqueness({"mail": mail})
|
||||
except Exception as e:
|
||||
raise YunohostError("user_update_failed", user=username, error=e)
|
||||
raise YunohostValidationError("user_update_failed", user=username, error=e)
|
||||
if mail[mail.find("@") + 1 :] not in domains:
|
||||
raise YunohostError(
|
||||
raise YunohostValidationError(
|
||||
"mail_domain_unknown", domain=mail[mail.find("@") + 1 :]
|
||||
)
|
||||
user["mail"].append(mail)
|
||||
|
@ -429,7 +429,7 @@ def user_update(
|
|||
if len(user["mail"]) > 1 and mail in user["mail"][1:]:
|
||||
user["mail"].remove(mail)
|
||||
else:
|
||||
raise YunohostError("mail_alias_remove_failed", mail=mail)
|
||||
raise YunohostValidationError("mail_alias_remove_failed", mail=mail)
|
||||
new_attr_dict["mail"] = user["mail"]
|
||||
|
||||
if "mail" in new_attr_dict:
|
||||
|
@ -451,7 +451,7 @@ def user_update(
|
|||
if len(user["maildrop"]) > 1 and mail in user["maildrop"][1:]:
|
||||
user["maildrop"].remove(mail)
|
||||
else:
|
||||
raise YunohostError("mail_forward_remove_failed", mail=mail)
|
||||
raise YunohostValidationError("mail_forward_remove_failed", mail=mail)
|
||||
new_attr_dict["maildrop"] = user["maildrop"]
|
||||
|
||||
if "maildrop" in new_attr_dict:
|
||||
|
@ -500,7 +500,7 @@ def user_info(username):
|
|||
if result:
|
||||
user = result[0]
|
||||
else:
|
||||
raise YunohostError("user_unknown", user=username)
|
||||
raise YunohostValidationError("user_unknown", user=username)
|
||||
|
||||
result_dict = {
|
||||
"username": user["uid"][0],
|
||||
|
@ -638,7 +638,7 @@ def user_group_create(
|
|||
{"cn": groupname}, base_dn="ou=groups,dc=yunohost,dc=org"
|
||||
)
|
||||
if conflict:
|
||||
raise YunohostError("group_already_exist", group=groupname)
|
||||
raise YunohostValidationError("group_already_exist", group=groupname)
|
||||
|
||||
# Validate uniqueness of groupname in system group
|
||||
all_existing_groupnames = {x.gr_name for x in grp.getgrall()}
|
||||
|
@ -651,7 +651,7 @@ def user_group_create(
|
|||
"sed --in-place '/^%s:/d' /etc/group" % groupname, shell=True
|
||||
)
|
||||
else:
|
||||
raise YunohostError("group_already_exist_on_system", group=groupname)
|
||||
raise YunohostValidationError("group_already_exist_on_system", group=groupname)
|
||||
|
||||
if not gid:
|
||||
# Get random GID
|
||||
|
@ -705,7 +705,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
|
|||
|
||||
existing_groups = list(user_group_list()["groups"].keys())
|
||||
if groupname not in existing_groups:
|
||||
raise YunohostError("group_unknown", group=groupname)
|
||||
raise YunohostValidationError("group_unknown", group=groupname)
|
||||
|
||||
# Refuse to delete primary groups of a user (e.g. group 'sam' related to user 'sam')
|
||||
# without the force option...
|
||||
|
@ -714,7 +714,7 @@ def user_group_delete(operation_logger, groupname, force=False, sync_perm=True):
|
|||
existing_users = list(user_list()["users"].keys())
|
||||
undeletable_groups = existing_users + ["all_users", "visitors"]
|
||||
if groupname in undeletable_groups and not force:
|
||||
raise YunohostError("group_cannot_be_deleted", group=groupname)
|
||||
raise YunohostValidationError("group_cannot_be_deleted", group=groupname)
|
||||
|
||||
operation_logger.start()
|
||||
ldap = _get_ldap_interface()
|
||||
|
@ -756,11 +756,11 @@ def user_group_update(
|
|||
# We also can't edit "all_users" without the force option because that's a special group...
|
||||
if not force:
|
||||
if groupname == "all_users":
|
||||
raise YunohostError("group_cannot_edit_all_users")
|
||||
raise YunohostValidationError("group_cannot_edit_all_users")
|
||||
elif groupname == "visitors":
|
||||
raise YunohostError("group_cannot_edit_visitors")
|
||||
raise YunohostValidationError("group_cannot_edit_visitors")
|
||||
elif groupname in existing_users:
|
||||
raise YunohostError("group_cannot_edit_primary_group", group=groupname)
|
||||
raise YunohostValidationError("group_cannot_edit_primary_group", group=groupname)
|
||||
|
||||
# We extract the uid for each member of the group to keep a simple flat list of members
|
||||
current_group = user_group_info(groupname)["members"]
|
||||
|
@ -771,7 +771,7 @@ def user_group_update(
|
|||
|
||||
for user in users_to_add:
|
||||
if user not in existing_users:
|
||||
raise YunohostError("user_unknown", user=user)
|
||||
raise YunohostValidationError("user_unknown", user=user)
|
||||
|
||||
if user in current_group:
|
||||
logger.warning(
|
||||
|
@ -843,7 +843,7 @@ def user_group_info(groupname):
|
|||
)
|
||||
|
||||
if not result:
|
||||
raise YunohostError("group_unknown", group=groupname)
|
||||
raise YunohostValidationError("group_unknown", group=groupname)
|
||||
|
||||
infos = result[0]
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ from moulinette import m18n
|
|||
|
||||
class YunohostError(MoulinetteError):
|
||||
|
||||
http_code = 500
|
||||
|
||||
"""
|
||||
Yunohost base exception
|
||||
|
||||
|
@ -49,3 +51,8 @@ class YunohostError(MoulinetteError):
|
|||
return super(YunohostError, self).content()
|
||||
else:
|
||||
return {"error": self.strerror, "log_ref": self.log_ref}
|
||||
|
||||
|
||||
class YunohostValidationError(YunohostError):
|
||||
|
||||
http_code = 400
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import os
|
||||
from moulinette import m18n
|
||||
from yunohost.utils.error import YunohostError
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import write_to_json, read_yaml
|
||||
|
||||
from yunohost.user import user_list, user_group_create, user_group_update
|
||||
from yunohost.user import user_list
|
||||
from yunohost.app import (
|
||||
app_setting,
|
||||
_installed_apps,
|
||||
_get_app_settings,
|
||||
_set_app_settings,
|
||||
|
@ -19,149 +17,6 @@ from yunohost.permission import (
|
|||
|
||||
logger = getActionLogger("yunohost.legacy")
|
||||
|
||||
|
||||
class SetupGroupPermissions:
|
||||
@staticmethod
|
||||
def remove_if_exists(target):
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
try:
|
||||
objects = ldap.search(target + ",dc=yunohost,dc=org")
|
||||
# ldap search will raise an exception if no corresponding object is found >.> ...
|
||||
except Exception:
|
||||
logger.debug("%s does not exist, no need to delete it" % target)
|
||||
return
|
||||
|
||||
objects.reverse()
|
||||
for o in objects:
|
||||
for dn in o["dn"]:
|
||||
dn = dn.replace(",dc=yunohost,dc=org", "")
|
||||
logger.debug("Deleting old object %s ..." % dn)
|
||||
try:
|
||||
ldap.remove(dn)
|
||||
except Exception as e:
|
||||
raise YunohostError(
|
||||
"migration_0011_failed_to_remove_stale_object", dn=dn, error=e
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def migrate_LDAP_db():
|
||||
|
||||
logger.info(m18n.n("migration_0011_update_LDAP_database"))
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
ldap_map = read_yaml(
|
||||
"/usr/share/yunohost/yunohost-config/moulinette/ldap_scheme.yml"
|
||||
)
|
||||
|
||||
try:
|
||||
SetupGroupPermissions.remove_if_exists("ou=permission")
|
||||
SetupGroupPermissions.remove_if_exists("ou=groups")
|
||||
|
||||
attr_dict = ldap_map["parents"]["ou=permission"]
|
||||
ldap.add("ou=permission", attr_dict)
|
||||
|
||||
attr_dict = ldap_map["parents"]["ou=groups"]
|
||||
ldap.add("ou=groups", attr_dict)
|
||||
|
||||
attr_dict = ldap_map["children"]["cn=all_users,ou=groups"]
|
||||
ldap.add("cn=all_users,ou=groups", attr_dict)
|
||||
|
||||
attr_dict = ldap_map["children"]["cn=visitors,ou=groups"]
|
||||
ldap.add("cn=visitors,ou=groups", attr_dict)
|
||||
|
||||
for rdn, attr_dict in ldap_map["depends_children"].items():
|
||||
ldap.add(rdn, attr_dict)
|
||||
except Exception as e:
|
||||
raise YunohostError("migration_0011_LDAP_update_failed", error=e)
|
||||
|
||||
logger.info(m18n.n("migration_0011_create_group"))
|
||||
|
||||
# Create a group for each yunohost user
|
||||
user_list = ldap.search(
|
||||
"ou=users,dc=yunohost,dc=org",
|
||||
"(&(objectclass=person)(!(uid=root))(!(uid=nobody)))",
|
||||
["uid", "uidNumber"],
|
||||
)
|
||||
for user_info in user_list:
|
||||
username = user_info["uid"][0]
|
||||
ldap.update(
|
||||
"uid=%s,ou=users" % username,
|
||||
{
|
||||
"objectClass": [
|
||||
"mailAccount",
|
||||
"inetOrgPerson",
|
||||
"posixAccount",
|
||||
"userPermissionYnh",
|
||||
]
|
||||
},
|
||||
)
|
||||
user_group_create(
|
||||
username,
|
||||
gid=user_info["uidNumber"][0],
|
||||
primary_group=True,
|
||||
sync_perm=False,
|
||||
)
|
||||
user_group_update(
|
||||
groupname="all_users", add=username, force=True, sync_perm=False
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def migrate_app_permission(app=None):
|
||||
logger.info(m18n.n("migration_0011_migrate_permission"))
|
||||
|
||||
apps = _installed_apps()
|
||||
|
||||
if app:
|
||||
if app not in apps:
|
||||
logger.error(
|
||||
"Can't migrate permission for app %s because it ain't installed..."
|
||||
% app
|
||||
)
|
||||
apps = []
|
||||
else:
|
||||
apps = [app]
|
||||
|
||||
for app in apps:
|
||||
permission = app_setting(app, "allowed_users")
|
||||
path = app_setting(app, "path")
|
||||
domain = app_setting(app, "domain")
|
||||
|
||||
url = "/" if domain and path else None
|
||||
if permission:
|
||||
known_users = list(user_list()["users"].keys())
|
||||
allowed = [
|
||||
user for user in permission.split(",") if user in known_users
|
||||
]
|
||||
else:
|
||||
allowed = ["all_users"]
|
||||
permission_create(
|
||||
app + ".main",
|
||||
url=url,
|
||||
allowed=allowed,
|
||||
show_tile=True,
|
||||
protected=False,
|
||||
sync_perm=False,
|
||||
)
|
||||
|
||||
app_setting(app, "allowed_users", delete=True)
|
||||
|
||||
# Migrate classic public app still using the legacy unprotected_uris
|
||||
if (
|
||||
app_setting(app, "unprotected_uris") == "/"
|
||||
or app_setting(app, "skipped_uris") == "/"
|
||||
):
|
||||
user_permission_update(app + ".main", add="visitors", sync_perm=False)
|
||||
|
||||
permission_sync_to_user()
|
||||
|
||||
|
||||
LEGACY_PERMISSION_LABEL = {
|
||||
("nextcloud", "skipped"): "api", # .well-known
|
||||
("libreto", "skipped"): "pad access", # /[^/]+
|
||||
|
|
|
@ -90,11 +90,11 @@ class PasswordValidator(object):
|
|||
# on top (at least not the moulinette ones)
|
||||
# because the moulinette needs to be correctly initialized
|
||||
# as well as modules available in python's path.
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.utils.error import YunohostValidationError
|
||||
|
||||
status, msg = self.validation_summary(password)
|
||||
if status == "error":
|
||||
raise YunohostError(msg)
|
||||
raise YunohostValidationError(msg)
|
||||
|
||||
def validation_summary(self, password):
|
||||
"""
|
||||
|
|
81
tests/test_helpers.d/ynhtest_setup_source.sh
Normal file
81
tests/test_helpers.d/ynhtest_setup_source.sh
Normal file
|
@ -0,0 +1,81 @@
|
|||
_make_dummy_src() {
|
||||
echo "test coucou"
|
||||
if [ ! -e $HTTPSERVER_DIR/dummy.tar.gz ]
|
||||
then
|
||||
pushd "$HTTPSERVER_DIR"
|
||||
mkdir dummy
|
||||
pushd dummy
|
||||
echo "Lorem Ipsum" > index.html
|
||||
echo '{"foo": "bar"}' > conf.json
|
||||
mkdir assets
|
||||
echo '.some.css { }' > assets/main.css
|
||||
echo 'var some="js";' > assets/main.js
|
||||
popd
|
||||
tar -czf dummy.tar.gz dummy
|
||||
popd
|
||||
fi
|
||||
echo "SOURCE_URL=http://127.0.0.1:$HTTPSERVER_PORT/dummy.tar.gz"
|
||||
echo "SOURCE_SUM=$(sha256sum $HTTPSERVER_DIR/dummy.tar.gz | awk '{print $1}')"
|
||||
}
|
||||
|
||||
ynhtest_setup_source_nominal() {
|
||||
final_path="$(mktemp -d -p $VAR_WWW)"
|
||||
_make_dummy_src > ../conf/dummy.src
|
||||
|
||||
ynh_setup_source --dest_dir="$final_path" --source_id="dummy"
|
||||
|
||||
test -e "$final_path"
|
||||
test -e "$final_path/index.html"
|
||||
}
|
||||
|
||||
ynhtest_setup_source_nominal_upgrade() {
|
||||
final_path="$(mktemp -d -p $VAR_WWW)"
|
||||
_make_dummy_src > ../conf/dummy.src
|
||||
|
||||
ynh_setup_source --dest_dir="$final_path" --source_id="dummy"
|
||||
|
||||
test "$(cat $final_path/index.html)" == "Lorem Ipsum"
|
||||
|
||||
# Except index.html to get overwritten during next ynh_setup_source
|
||||
echo "IEditedYou!" > $final_path/index.html
|
||||
test "$(cat $final_path/index.html)" == "IEditedYou!"
|
||||
|
||||
ynh_setup_source --dest_dir="$final_path" --source_id="dummy"
|
||||
|
||||
test "$(cat $final_path/index.html)" == "Lorem Ipsum"
|
||||
}
|
||||
|
||||
|
||||
ynhtest_setup_source_with_keep() {
|
||||
final_path="$(mktemp -d -p $VAR_WWW)"
|
||||
_make_dummy_src > ../conf/dummy.src
|
||||
|
||||
echo "IEditedYou!" > $final_path/index.html
|
||||
echo "IEditedYou!" > $final_path/test.txt
|
||||
|
||||
ynh_setup_source --dest_dir="$final_path" --source_id="dummy" --keep="index.html test.txt"
|
||||
|
||||
test -e "$final_path"
|
||||
test -e "$final_path/index.html"
|
||||
test -e "$final_path/test.txt"
|
||||
test "$(cat $final_path/index.html)" == "IEditedYou!"
|
||||
test "$(cat $final_path/test.txt)" == "IEditedYou!"
|
||||
}
|
||||
|
||||
ynhtest_setup_source_with_patch() {
|
||||
final_path="$(mktemp -d -p $VAR_WWW)"
|
||||
_make_dummy_src > ../conf/dummy.src
|
||||
|
||||
mkdir -p ../sources/patches
|
||||
cat > ../sources/patches/dummy-index.html.patch << EOF
|
||||
--- a/index.html
|
||||
+++ b/index.html
|
||||
@@ -1 +1,1 @@
|
||||
-Lorem Ipsum
|
||||
+Lorem Ipsum dolor sit amet
|
||||
EOF
|
||||
|
||||
ynh_setup_source --dest_dir="$final_path" --source_id="dummy"
|
||||
|
||||
test "$(cat $final_path/index.html)" == "Lorem Ipsum dolor sit amet"
|
||||
}
|
78
tests/test_helpers.sh
Normal file
78
tests/test_helpers.sh
Normal file
|
@ -0,0 +1,78 @@
|
|||
#!/bin/bash
|
||||
|
||||
readonly NORMAL=$(printf '\033[0m')
|
||||
readonly BOLD=$(printf '\033[1m')
|
||||
readonly RED=$(printf '\033[31m')
|
||||
readonly GREEN=$(printf '\033[32m')
|
||||
readonly ORANGE=$(printf '\033[33m')
|
||||
|
||||
function log_test()
|
||||
{
|
||||
echo -n "${BOLD}$1${NORMAL} ... "
|
||||
}
|
||||
|
||||
function log_passed()
|
||||
{
|
||||
echo "${BOLD}${GREEN}✔ Passed${NORMAL}"
|
||||
}
|
||||
|
||||
function log_failed()
|
||||
{
|
||||
echo "${BOLD}${RED}✘ Failed${NORMAL}"
|
||||
}
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
[ -n "$HTTPSERVER" ] && kill "$HTTPSERVER"
|
||||
[ -d "$HTTPSERVER_DIR" ] && rm -rf "$HTTPSERVER_DIR"
|
||||
[ -d "$VAR_WWW" ] && rm -rf "$VAR_WWW"
|
||||
}
|
||||
trap cleanup EXIT SIGINT
|
||||
|
||||
# =========================================================
|
||||
|
||||
# Dummy http server, to serve archives for ynh_setup_source
|
||||
HTTPSERVER_DIR=$(mktemp -d)
|
||||
HTTPSERVER_PORT=1312
|
||||
pushd "$HTTPSERVER_DIR" >/dev/null
|
||||
python -m SimpleHTTPServer $HTTPSERVER_PORT &>/dev/null &
|
||||
HTTPSERVER="$!"
|
||||
popd >/dev/null
|
||||
|
||||
VAR_WWW=$(mktemp -d)/var/www
|
||||
mkdir -p $VAR_WWW
|
||||
# =========================================================
|
||||
|
||||
source /usr/share/yunohost/helpers
|
||||
for TEST_SUITE in $(ls test_helpers.d/*)
|
||||
do
|
||||
source $TEST_SUITE
|
||||
done
|
||||
|
||||
# Hack to list all known function, keep only those starting by ynhtest_
|
||||
TESTS=$(declare -F | grep ' ynhtest_' | awk '{print $3}')
|
||||
|
||||
global_result=0
|
||||
|
||||
for TEST in $TESTS
|
||||
do
|
||||
log_test $TEST
|
||||
cd $(mktemp -d)
|
||||
(app=ynhtest
|
||||
YNH_APP_ID=$app
|
||||
mkdir conf
|
||||
mkdir scripts
|
||||
cd scripts
|
||||
set -eux
|
||||
$TEST
|
||||
) > ./test.log 2>&1
|
||||
|
||||
if [[ $? == 0 ]]
|
||||
then
|
||||
set +x; log_passed;
|
||||
else
|
||||
set +x; echo -e "\n----------"; cat ./test.log; echo -e "----------"; log_failed; global_result=1;
|
||||
fi
|
||||
done
|
||||
|
||||
exit $global_result
|
|
@ -25,10 +25,12 @@ def find_expected_string_keys():
|
|||
# Try to find :
|
||||
# m18n.n( "foo"
|
||||
# YunohostError("foo"
|
||||
# YunohostValidationError("foo"
|
||||
# # i18n: foo
|
||||
p1 = re.compile(r"m18n\.n\(\n*\s*[\"\'](\w+)[\"\']")
|
||||
p2 = re.compile(r"YunohostError\(\n*\s*[\'\"](\w+)[\'\"]")
|
||||
p3 = re.compile(r"# i18n: [\'\"]?(\w+)[\'\"]?")
|
||||
p3 = re.compile(r"YunohostValidationError\(\n*\s*[\'\"](\w+)[\'\"]")
|
||||
p4 = re.compile(r"# i18n: [\'\"]?(\w+)[\'\"]?")
|
||||
|
||||
python_files = glob.glob("src/yunohost/*.py")
|
||||
python_files.extend(glob.glob("src/yunohost/utils/*.py"))
|
||||
|
@ -47,6 +49,10 @@ def find_expected_string_keys():
|
|||
continue
|
||||
yield m
|
||||
for m in p3.findall(content):
|
||||
if m.endswith("_"):
|
||||
continue
|
||||
yield m
|
||||
for m in p4.findall(content):
|
||||
yield m
|
||||
|
||||
# For each diagnosis, try to find strings like "diagnosis_stuff_foo" (c.f. diagnosis summaries)
|
||||
|
|
Loading…
Add table
Reference in a new issue