mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
update checks for app requirements as generator
This commit is contained in:
parent
727bef92e5
commit
e202df4793
1 changed files with 106 additions and 54 deletions
140
src/app.py
140
src/app.py
|
@ -29,7 +29,7 @@ import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import copy
|
import copy
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import List, Tuple, Dict, Any
|
from typing import List, Tuple, Dict, Any, Iterator
|
||||||
from packaging import version
|
from packaging import version
|
||||||
|
|
||||||
from moulinette import Moulinette, m18n
|
from moulinette import Moulinette, m18n
|
||||||
|
@ -587,7 +587,11 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
|
||||||
upgrade_type = "UPGRADE_FULL"
|
upgrade_type = "UPGRADE_FULL"
|
||||||
|
|
||||||
# Check requirements
|
# Check requirements
|
||||||
_check_manifest_requirements(manifest, action="upgrade")
|
for _, check, values, err in _check_manifest_requirements(
|
||||||
|
manifest, action="upgrade"
|
||||||
|
):
|
||||||
|
if not check:
|
||||||
|
raise YunohostValidationError(err, **values)
|
||||||
|
|
||||||
if manifest["packaging_format"] >= 2:
|
if manifest["packaging_format"] >= 2:
|
||||||
if no_safety_backup:
|
if no_safety_backup:
|
||||||
|
@ -788,6 +792,18 @@ def app_manifest(app):
|
||||||
raw_questions = manifest.get("install", {}).values()
|
raw_questions = manifest.get("install", {}).values()
|
||||||
manifest["install"] = hydrate_questions_with_choices(raw_questions)
|
manifest["install"] = hydrate_questions_with_choices(raw_questions)
|
||||||
|
|
||||||
|
manifest["requirements"] = {}
|
||||||
|
for name, check, values, err in _check_manifest_requirements(
|
||||||
|
manifest, action="install"
|
||||||
|
):
|
||||||
|
if Moulinette.interface.type == "api":
|
||||||
|
manifest["requirements"][name] = {
|
||||||
|
"pass": check,
|
||||||
|
"values": values,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
manifest["requirements"][name] = "ok" if check else m18n.n(err, **values)
|
||||||
|
|
||||||
return manifest
|
return manifest
|
||||||
|
|
||||||
|
|
||||||
|
@ -873,7 +889,17 @@ def app_install(
|
||||||
app_id = manifest["id"]
|
app_id = manifest["id"]
|
||||||
|
|
||||||
# Check requirements
|
# Check requirements
|
||||||
_check_manifest_requirements(manifest, action="install")
|
for name, check, values, err in _check_manifest_requirements(
|
||||||
|
manifest, action="install"
|
||||||
|
):
|
||||||
|
if not check:
|
||||||
|
if name == "ram":
|
||||||
|
_ask_confirmation(
|
||||||
|
"confirm_app_install_insufficient_ram", params=values, force=force
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise YunohostValidationError(err, **values)
|
||||||
|
|
||||||
_assert_system_is_sane_for_app(manifest, "pre")
|
_assert_system_is_sane_for_app(manifest, "pre")
|
||||||
|
|
||||||
# Check if app can be forked
|
# Check if app can be forked
|
||||||
|
@ -2351,71 +2377,97 @@ def _get_all_installed_apps_id():
|
||||||
return all_apps_ids_formatted
|
return all_apps_ids_formatted
|
||||||
|
|
||||||
|
|
||||||
def _check_manifest_requirements(manifest: Dict, action: str):
|
def _check_manifest_requirements(
|
||||||
|
manifest: Dict, action: str = ""
|
||||||
|
) -> Iterator[Tuple[str, bool, object, str]]:
|
||||||
"""Check if required packages are met from the manifest"""
|
"""Check if required packages are met from the manifest"""
|
||||||
|
|
||||||
if manifest["packaging_format"] not in [1, 2]:
|
|
||||||
raise YunohostValidationError("app_packaging_format_not_supported")
|
|
||||||
|
|
||||||
app_id = manifest["id"]
|
app_id = manifest["id"]
|
||||||
|
|
||||||
logger.debug(m18n.n("app_requirements_checking", app=app_id))
|
logger.debug(m18n.n("app_requirements_checking", app=app_id))
|
||||||
|
|
||||||
# Yunohost version requirement
|
# Packaging format
|
||||||
|
yield (
|
||||||
yunohost_requirement = version.parse(manifest["integration"]["yunohost"] or "4.3")
|
"packaging_format",
|
||||||
yunohost_installed_version = version.parse(
|
manifest["packaging_format"] in (1, 2),
|
||||||
get_ynh_package_version("yunohost")["version"]
|
{},
|
||||||
|
"app_packaging_format_not_supported", # i18n: app_packaging_format_not_supported
|
||||||
)
|
)
|
||||||
if yunohost_requirement > yunohost_installed_version:
|
|
||||||
# FIXME : i18n
|
# Yunohost version
|
||||||
raise YunohostValidationError(
|
required_yunohost_version = manifest["integration"].get("yunohost", "4.3")
|
||||||
f"This app requires Yunohost >= {yunohost_requirement} but current installed version is {yunohost_installed_version}"
|
current_yunohost_version = get_ynh_package_version("yunohost")["version"]
|
||||||
|
|
||||||
|
yield (
|
||||||
|
"version",
|
||||||
|
version.parse(required_yunohost_version)
|
||||||
|
<= version.parse(current_yunohost_version),
|
||||||
|
{"current": current_yunohost_version, "required": required_yunohost_version},
|
||||||
|
"app_yunohost_version_not_supported", # i18n: app_yunohost_version_not_supported
|
||||||
)
|
)
|
||||||
|
|
||||||
# Architectures
|
# Architectures
|
||||||
arch_requirement = manifest["integration"]["architectures"]
|
arch_requirement = manifest["integration"]["architectures"]
|
||||||
if arch_requirement != "all":
|
|
||||||
arch = system_arch()
|
arch = system_arch()
|
||||||
if arch not in arch_requirement:
|
|
||||||
# FIXME: i18n
|
yield (
|
||||||
raise YunohostValidationError(
|
"arch",
|
||||||
f"This app can only be installed on architectures {', '.join(arch_requirement)} but your server architecture is {arch}"
|
arch_requirement == "all" or arch in arch_requirement,
|
||||||
|
{"current": arch, "required": arch_requirement},
|
||||||
|
"app_arch_not_supported", # i18n: app_arch_not_supported
|
||||||
)
|
)
|
||||||
|
|
||||||
# Multi-instance
|
# Multi-instance
|
||||||
if action == "install" and manifest["integration"]["multi_instance"] == False:
|
if action == "install":
|
||||||
|
multi_instance = manifest["integration"]["multi_instance"] == True
|
||||||
|
if not multi_instance:
|
||||||
apps = _installed_apps()
|
apps = _installed_apps()
|
||||||
sibling_apps = [a for a in apps if a == app_id or a.startswith(f"{app_id}__")]
|
sibling_apps = [
|
||||||
if len(sibling_apps) > 0:
|
a for a in apps if a == app_id or a.startswith(f"{app_id}__")
|
||||||
raise YunohostValidationError("app_already_installed", app=app_id)
|
]
|
||||||
|
multi_instance = len(sibling_apps) > 0
|
||||||
|
|
||||||
|
yield (
|
||||||
|
"install",
|
||||||
|
multi_instance,
|
||||||
|
{},
|
||||||
|
"app_already_installed", # i18n: app_already_installed
|
||||||
|
)
|
||||||
|
|
||||||
# Disk
|
# Disk
|
||||||
if action == "install":
|
if action == "install":
|
||||||
disk_requirement = manifest["integration"]["disk"]
|
disk_req_bin = human_to_binary(manifest["integration"]["disk"])
|
||||||
|
root_free_space = free_space_in_directory("/")
|
||||||
if free_space_in_directory("/") <= human_to_binary(
|
var_free_space = free_space_in_directory("/var")
|
||||||
disk_requirement
|
has_enough_disk = (
|
||||||
) or free_space_in_directory("/var") <= human_to_binary(disk_requirement):
|
root_free_space > disk_req_bin and var_free_space > disk_req_bin
|
||||||
# FIXME : i18m
|
)
|
||||||
raise YunohostValidationError(
|
free_space = binary_to_human(
|
||||||
f"This app requires {disk_requirement} free space."
|
root_free_space
|
||||||
|
if root_free_space == var_free_space
|
||||||
|
else root_free_space + var_free_space
|
||||||
)
|
)
|
||||||
|
|
||||||
# Ram for build
|
yield (
|
||||||
ram_build_requirement = manifest["integration"]["ram"]["build"]
|
"disk",
|
||||||
# Is "include_swap" really useful ? We should probably decide wether to always include it or not instead
|
has_enough_disk,
|
||||||
ram_include_swap = manifest["integration"]["ram"].get("include_swap", False)
|
{"current": free_space, "required": manifest["integration"]["disk"]},
|
||||||
|
"app_not_enough_disk", # i18n: app_not_enough_disk
|
||||||
|
)
|
||||||
|
|
||||||
|
# Ram
|
||||||
|
ram_requirement = manifest["integration"]["ram"]
|
||||||
ram, swap = ram_available()
|
ram, swap = ram_available()
|
||||||
if ram_include_swap:
|
# Is "include_swap" really useful ? We should probably decide wether to always include it or not instead
|
||||||
|
if ram_requirement.get("include_swap", False):
|
||||||
ram += swap
|
ram += swap
|
||||||
|
can_build = ram > human_to_binary(ram_requirement["build"])
|
||||||
|
can_run = ram > human_to_binary(ram_requirement["runtime"])
|
||||||
|
|
||||||
if ram < human_to_binary(ram_build_requirement):
|
yield (
|
||||||
# FIXME : i18n
|
"ram",
|
||||||
ram_human = binary_to_human(ram)
|
can_build and can_run,
|
||||||
raise YunohostValidationError(
|
{"current": binary_to_human(ram), "required": ram_requirement["build"]},
|
||||||
f"This app requires {ram_build_requirement} RAM to install/upgrade but only {ram_human} is available right now."
|
"app_not_enough_ram", # i18n: app_not_enough_ram
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue