mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
appslists -> apps catalog
This commit is contained in:
parent
f31c02cd64
commit
3b7899db8c
7 changed files with 482 additions and 484 deletions
|
@ -55,11 +55,11 @@
|
|||
"apps_already_up_to_date": "All apps are already up-to-date",
|
||||
"apps_permission_not_found": "No permission found for the installed apps",
|
||||
"apps_permission_restoration_failed": "Permission '{permission:s}' for app {app:s} restoration has failed",
|
||||
"appslist_init_success": "Appslist system initialized!",
|
||||
"appslist_updating": "Updating application list...",
|
||||
"appslist_failed_to_download": "Unable to download the {applist} appslist : {error}",
|
||||
"appslist_obsolete_cache": "The applist cache is empty or obsolete.",
|
||||
"appslist_update_success": "The application list has been updated!",
|
||||
"apps_catalog_init_success": "Apps catalog system initialized!",
|
||||
"apps_catalog_updating": "Updating applications catalog...",
|
||||
"apps_catalog_failed_to_download": "Unable to download the {apps_catalog} apps catalog: {error}",
|
||||
"apps_catalog_obsolete_cache": "The apps catalog cache is empty or obsolete.",
|
||||
"apps_catalog_update_success": "The application catalog has been updated!",
|
||||
"ask_current_admin_password": "Current administration password",
|
||||
"ask_email": "E-mail address",
|
||||
"ask_firstname": "First name",
|
||||
|
@ -144,7 +144,6 @@
|
|||
"confirm_app_install_danger": "DANGER! This app is known to be still experimental (if not explicitly not working)! You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or break your system… If you are willing to take that risk anyway, type '{answers:s}'",
|
||||
"confirm_app_install_thirdparty": "DANGER! This app is not part of Yunohost's app catalog. Installing third-party apps may compromise the integrity and security of your system. You should probably NOT install it unless you know what you are doing. NO SUPPORT will be provided if this app doesn't work or break your system… If you are willing to take that risk anyway, type '{answers:s}'",
|
||||
"custom_app_url_required": "You must provide a URL to upgrade your custom app {app:s}",
|
||||
"custom_appslist_name_required": "You must provide a name for your custom app list",
|
||||
"diagnosis_basesystem_host": "Server is running Debian {debian_version}.",
|
||||
"diagnosis_basesystem_kernel": "Server is running Linux kernel {kernel_version}",
|
||||
"diagnosis_basesystem_ynh_single_version": "{0} version: {1} ({2})",
|
||||
|
@ -374,10 +373,10 @@
|
|||
"migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)",
|
||||
"migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)",
|
||||
"migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services",
|
||||
"migration_description_0010_migrate_to_apps_json": "Remove deprecated applists and use the new unified 'apps.json' list instead (outdated, replaced by migration 12)",
|
||||
"migration_description_0010_migrate_to_apps_json": "Remove deprecated apps catalogs and use the new unified 'apps.json' list instead (outdated, replaced by migration 13)",
|
||||
"migration_description_0011_setup_group_permission": "Set up user group and set up permission for apps and services",
|
||||
"migration_description_0012_postgresql_password_to_md5_authentication": "Force PostgreSQL authentication to use MD5 for local connections",
|
||||
"migration_description_0013_futureproof_appslist_system": "Migrate to the new future-proof appslist system",
|
||||
"migration_description_0013_futureproof_apps_catalog_system": "Migrate to the new future-proof apps catalog system",
|
||||
"migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.",
|
||||
"migration_0003_patching_sources_list": "Patching the sources.lists…",
|
||||
"migration_0003_main_upgrade": "Starting main upgrade…",
|
||||
|
@ -388,7 +387,7 @@
|
|||
"migration_0003_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Stretch.",
|
||||
"migration_0003_still_on_jessie_after_main_upgrade": "Something went wrong during the main upgrade: Is the system still on Jessie‽ To investigate the issue, please look at {log}:s…",
|
||||
"migration_0003_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.\n\nAdditionally, the port for SMTP, used by external e-mail clients (like Thunderbird or K9-Mail) was changed from 465 (SSL/TLS) to 587 (STARTTLS). The old port (465) will automatically be closed, and the new port (587) will be opened in the firewall. You and your users *will* have to adapt the configuration of your e-mail clients accordingly.",
|
||||
"migration_0003_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from an applist, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}",
|
||||
"migration_0003_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from an apps_catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}",
|
||||
"migration_0003_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}",
|
||||
"migration_0005_postgresql_94_not_installed": "PostgreSQL was not installed on your system. Nothing to do.",
|
||||
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 is installed, but not postgresql 9.6‽ Something weird might have happened on your system:(…",
|
||||
|
|
|
@ -54,11 +54,11 @@ APPS_SETTING_PATH = '/etc/yunohost/apps/'
|
|||
INSTALL_TMP = '/var/cache/yunohost'
|
||||
APP_TMP_FOLDER = INSTALL_TMP + '/from_file'
|
||||
|
||||
APPSLISTS_CACHE = '/var/cache/yunohost/repo'
|
||||
APPSLISTS_CONF = '/etc/yunohost/appslists.yml'
|
||||
APPSLISTS_CRON_PATH = "/etc/cron.daily/yunohost-fetch-appslists"
|
||||
APPSLISTS_API_VERSION = 1
|
||||
APPSLISTS_DEFAULT_URL = "https://app.yunohost.org/default"
|
||||
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 = 1
|
||||
APPS_CATALOG_DEFAULT_URL = "https://app.yunohost.org/default"
|
||||
|
||||
re_github_repo = re.compile(
|
||||
r'^(http[s]?://|git@)github.com[/:]'
|
||||
|
@ -88,8 +88,8 @@ def app_list(filter=None, raw=False, installed=False, with_backup=False):
|
|||
|
||||
list_dict = {} if raw else []
|
||||
|
||||
# Get app list from applist cache
|
||||
app_dict = _load_appslist()
|
||||
# Get app list from catalog cache
|
||||
app_dict = _load_apps_catalog()
|
||||
|
||||
# Get app list from the app settings directory
|
||||
for app in os.listdir(APPS_SETTING_PATH):
|
||||
|
@ -2740,144 +2740,144 @@ def _parse_app_instance_name(app_instance_name):
|
|||
#
|
||||
|
||||
|
||||
def _initialize_appslists_system():
|
||||
def _initialize_apps_catalog_system():
|
||||
"""
|
||||
This function is meant to intialize the appslist system with YunoHost's default applist.
|
||||
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_appslist_list = [{"id": "default", "url": APPSLISTS_DEFAULT_URL}]
|
||||
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 appslist at the same time every night
|
||||
# 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 appslist system with YunoHost's default app list")
|
||||
write_to_yaml(APPSLISTS_CONF, default_appslist_list)
|
||||
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 appslist fetch daily cron job")
|
||||
write_to_file(APPSLISTS_CRON_PATH, '\n'.join(cron_job))
|
||||
chown(APPSLISTS_CRON_PATH, uid="root", gid="root")
|
||||
chmod(APPSLISTS_CRON_PATH, 0o755)
|
||||
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 appslist system... : %s" % str(e))
|
||||
raise YunohostError("Could not initialize the apps catalog system... : %s" % str(e))
|
||||
|
||||
logger.success(m18n.n("appslist_init_success"))
|
||||
logger.success(m18n.n("apps_catalog_init_success"))
|
||||
|
||||
|
||||
def _read_appslist_list():
|
||||
def _read_apps_catalog_list():
|
||||
"""
|
||||
Read the json corresponding to the list of appslists
|
||||
Read the json corresponding to the list of apps catalogs
|
||||
"""
|
||||
|
||||
# Legacy code - can be removed after moving to buster (if the migration got merged before buster)
|
||||
if os.path.exists('/etc/yunohost/appslists.json'):
|
||||
from yunohost.tools import _get_migration_by_name
|
||||
migration = _get_migration_by_name("futureproof_appslist_system")
|
||||
migration = _get_migration_by_name("futureproof_apps_catalog_system")
|
||||
migration.migrate()
|
||||
|
||||
try:
|
||||
list_ = read_yaml(APPSLISTS_CONF)
|
||||
list_ = read_yaml(APPS_CATALOG_CONF)
|
||||
# Support the case where file exists but is empty
|
||||
# by returning [] if list_ is None
|
||||
return list_ if list_ else []
|
||||
except Exception as e:
|
||||
raise YunohostError("Could not read the appslist list ... : %s" % str(e))
|
||||
raise YunohostError("Could not read the apps_catalog list ... : %s" % str(e))
|
||||
|
||||
|
||||
def _actual_appslist_api_url(base_url):
|
||||
def _actual_apps_catalog_api_url(base_url):
|
||||
|
||||
return "{base_url}/v{version}/apps.json".format(base_url=base_url, version=APPSLISTS_API_VERSION)
|
||||
return "{base_url}/v{version}/apps.json".format(base_url=base_url, version=APPS_CATALOG_API_VERSION)
|
||||
|
||||
|
||||
def _update_appslist():
|
||||
def _update_apps_catalog():
|
||||
"""
|
||||
Fetches the json for each appslist and update the cache
|
||||
Fetches the json for each apps_catalog and update the cache
|
||||
|
||||
appslist_list is for example :
|
||||
apps_catalog_list is for example :
|
||||
[ {"id": "default", "url": "https://app.yunohost.org/default/"} ]
|
||||
|
||||
Then for each appslist, the actual json URL to be fetched is like :
|
||||
Then for each apps_catalog, the actual json URL to be fetched is like :
|
||||
https://app.yunohost.org/default/vX/apps.json
|
||||
|
||||
And store it in :
|
||||
/var/cache/yunohost/repo/default.json
|
||||
"""
|
||||
|
||||
appslist_list = _read_appslist_list()
|
||||
apps_catalog_list = _read_apps_catalog_list()
|
||||
|
||||
logger.info(m18n.n("appslist_updating"))
|
||||
logger.info(m18n.n("apps_catalog_updating"))
|
||||
|
||||
# Create cache folder if needed
|
||||
if not os.path.exists(APPSLISTS_CACHE):
|
||||
logger.debug("Initialize folder for appslist cache")
|
||||
mkdir(APPSLISTS_CACHE, mode=0o750, parents=True, uid='root')
|
||||
if not os.path.exists(APPS_CATALOG_CACHE):
|
||||
logger.debug("Initialize folder for apps catalog cache")
|
||||
mkdir(APPS_CATALOG_CACHE, mode=0o750, parents=True, uid='root')
|
||||
|
||||
for appslist in appslist_list:
|
||||
applist_id = appslist["id"]
|
||||
actual_api_url = _actual_appslist_api_url(appslist["url"])
|
||||
for apps_catalog in apps_catalog_list:
|
||||
apps_catalog_id = apps_catalog["id"]
|
||||
actual_api_url = _actual_apps_catalog_api_url(apps_catalog["url"])
|
||||
|
||||
# Fetch the json
|
||||
try:
|
||||
appslist_content = download_json(actual_api_url)
|
||||
apps_catalog_content = download_json(actual_api_url)
|
||||
except Exception as e:
|
||||
raise YunohostError("appslist_failed_to_download", applist=applist_id, error=str(e))
|
||||
raise YunohostError("apps_catalog_failed_to_download", apps_catalog=apps_catalog_id, error=str(e))
|
||||
|
||||
# Remember the appslist api version for later
|
||||
appslist_content["from_api_version"] = APPSLISTS_API_VERSION
|
||||
# Remember the apps_catalog api version for later
|
||||
apps_catalog_content["from_api_version"] = APPS_CATALOG_API_VERSION
|
||||
|
||||
# Save the appslist data in the cache
|
||||
cache_file = "{cache_folder}/{list}.json".format(cache_folder=APPSLISTS_CACHE, list=applist_id)
|
||||
# Save the apps_catalog data in the cache
|
||||
cache_file = "{cache_folder}/{list}.json".format(cache_folder=APPS_CATALOG_CACHE, list=apps_catalog_id)
|
||||
try:
|
||||
write_to_json(cache_file, appslist_content)
|
||||
write_to_json(cache_file, apps_catalog_content)
|
||||
except Exception as e:
|
||||
raise YunohostError("Unable to write cache data for %s appslist : %s" % (applist_id, str(e)))
|
||||
raise YunohostError("Unable to write cache data for %s apps_catalog : %s" % (apps_catalog_id, str(e)))
|
||||
|
||||
logger.success(m18n.n("appslist_update_success"))
|
||||
logger.success(m18n.n("apps_catalog_update_success"))
|
||||
|
||||
|
||||
def _load_appslist():
|
||||
def _load_apps_catalog():
|
||||
"""
|
||||
Read all the appslist cache file and build a single dict (app_dict)
|
||||
Read all the apps catalog cache files and build a single dict (app_dict)
|
||||
corresponding to all known apps in all indexes
|
||||
"""
|
||||
|
||||
app_dict = {}
|
||||
|
||||
for appslist_id in [L["id"] for L in _read_appslist_list()]:
|
||||
for apps_catalog_id in [L["id"] for L in _read_apps_catalog_list()]:
|
||||
|
||||
# Let's load the json from cache for this appslist
|
||||
cache_file = "{cache_folder}/{list}.json".format(cache_folder=APPSLISTS_CACHE, list=appslist_id)
|
||||
# Let's load the json from cache for this catalog
|
||||
cache_file = "{cache_folder}/{list}.json".format(cache_folder=APPS_CATALOG_CACHE, list=apps_catalog_id)
|
||||
|
||||
try:
|
||||
appslist_content = read_json(cache_file) if os.path.exists(cache_file) else None
|
||||
apps_catalog_content = read_json(cache_file) if os.path.exists(cache_file) else None
|
||||
except Exception as e:
|
||||
raise ("Unable to read cache for appslist %s : %s" % (appslist_id, str(e)))
|
||||
raise ("Unable to read cache for apps_catalog %s : %s" % (apps_catalog_id, str(e)))
|
||||
|
||||
# Check that the version of the data matches version ....
|
||||
# ... otherwise it means we updated yunohost in the meantime
|
||||
# and need to update the cache for everything to be consistent
|
||||
if not appslist_content or appslist_content.get("from_api_version") != APPSLISTS_API_VERSION:
|
||||
logger.info(m18n.n("appslist_obsolete_cache"))
|
||||
_update_appslist()
|
||||
appslist_content = read_json(cache_file)
|
||||
if not apps_catalog_content or apps_catalog_content.get("from_api_version") != APPS_CATALOG_API_VERSION:
|
||||
logger.info(m18n.n("apps_catalog_obsolete_cache"))
|
||||
_update_apps_catalog()
|
||||
apps_catalog_content = read_json(cache_file)
|
||||
|
||||
del appslist_content["from_api_version"]
|
||||
del apps_catalog_content["from_api_version"]
|
||||
|
||||
# Add apps from this applist to the output
|
||||
for app, info in appslist_content.items():
|
||||
# Add apps from this catalog to the output
|
||||
for app, info in apps_catalog_content.items():
|
||||
|
||||
# (N.B. : there's a small edge case where multiple appslist could be listing the same apps ...
|
||||
# (N.B. : there's a small edge case where multiple apps catalog could be listing the same apps ...
|
||||
# in which case we keep only the first one found)
|
||||
if app in app_dict:
|
||||
logger.warning("Duplicate app %s found between appslist %s and %s" % (app, appslist_id, app_dict[app]['repository']))
|
||||
logger.warning("Duplicate app %s found between apps catalog %s and %s" % (app, apps_catalog_id, app_dict[app]['repository']))
|
||||
continue
|
||||
|
||||
info['repository'] = appslist_id
|
||||
info['repository'] = apps_catalog_id
|
||||
app_dict[app] = info
|
||||
|
||||
return app_dict
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_json
|
||||
|
||||
from yunohost.tools import Migration
|
||||
from yunohost.app import (_initialize_apps_catalog_system,
|
||||
_update_apps_catalog,
|
||||
APPS_CATALOG_CACHE,
|
||||
APPS_CATALOG_CONF)
|
||||
|
||||
logger = getActionLogger('yunohost.migration')
|
||||
|
||||
LEGACY_APPS_CATALOG_CONF = '/etc/yunohost/appslists.json'
|
||||
LEGACY_APPS_CATALOG_CONF_BACKUP = LEGACY_APPS_CATALOG_CONF + ".old"
|
||||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate to the new future-proof apps catalog system"
|
||||
|
||||
def migrate(self):
|
||||
|
||||
if not os.path.exists(LEGACY_APPS_CATALOG_CONF):
|
||||
logger.info("No need to do anything")
|
||||
|
||||
# Destroy old lecacy cache
|
||||
if os.path.exists(APPS_CATALOG_CACHE):
|
||||
shutil.rmtree(APPS_CATALOG_CACHE)
|
||||
|
||||
# Backup the legacy file
|
||||
try:
|
||||
legacy_catalogs = read_json(LEGACY_APPS_CATALOG_CONF)
|
||||
# If there's only one catalog, we assume it's just the old official catalog
|
||||
# Otherwise, warn the (power-?)users that they should migrate their old catalogs manually
|
||||
if len(legacy_catalogs) > 1:
|
||||
logger.warning("It looks like you had additional apps_catalog in the configuration file %s! YunoHost now uses %s instead, but it won't migrate your custom apps_catalog. You should do this manually. The old file has been backuped in %s." % (LEGACY_APPS_CATALOG_CONF, APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP))
|
||||
except Exception as e:
|
||||
logger.warning("Unable to parse the legacy conf %s (error : %s) ... migrating anyway" % (LEGACY_APPS_CATALOG_CONF, str(e)))
|
||||
|
||||
os.rename(LEGACY_APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP)
|
||||
|
||||
_initialize_apps_catalog_system()
|
||||
_update_apps_catalog()
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from moulinette.utils.log import getActionLogger
|
||||
from moulinette.utils.filesystem import read_json
|
||||
|
||||
from yunohost.tools import Migration
|
||||
from yunohost.app import (_initialize_appslists_system,
|
||||
_update_appslist,
|
||||
APPSLISTS_CACHE,
|
||||
APPSLISTS_CONF)
|
||||
|
||||
logger = getActionLogger('yunohost.migration')
|
||||
|
||||
LEGACY_APPSLISTS_CONF = '/etc/yunohost/appslists.json'
|
||||
LEGACY_APPSLISTS_CONF_BACKUP = LEGACY_APPSLISTS_CONF + ".old"
|
||||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate to the new future-proof appslist system"
|
||||
|
||||
def migrate(self):
|
||||
|
||||
if not os.path.exists(LEGACY_APPSLISTS_CONF):
|
||||
logger.info("No need to do anything")
|
||||
|
||||
# Destroy old lecacy cache
|
||||
if os.path.exists(APPSLISTS_CACHE):
|
||||
shutil.rmtree(APPSLISTS_CACHE)
|
||||
|
||||
# Backup the legacy file
|
||||
try:
|
||||
legacy_list = read_json(LEGACY_APPSLISTS_CONF)
|
||||
# If there's only one list, we assume it's just the old official list
|
||||
# Otherwise, warn the (power-?)users that they should migrate their old list manually
|
||||
if len(legacy_list) > 1:
|
||||
logger.warning("It looks like you had additional appslist in the configuration file %s! YunoHost now uses %s instead, but it won't migrate your custom appslist. You should do this manually. The old file has been backuped in %s." % (LEGACY_APPSLISTS_CONF, APPSLISTS_CONF, LEGACY_APPSLISTS_CONF_BACKUP))
|
||||
except Exception as e:
|
||||
logger.warning("Unable to parse the legacy conf %s (error : %s) ... migrating anyway" % (LEGACY_APPSLISTS_CONF, str(e)))
|
||||
|
||||
os.rename(LEGACY_APPSLISTS_CONF, LEGACY_APPSLISTS_CONF_BACKUP)
|
||||
|
||||
_initialize_appslists_system()
|
||||
_update_appslist()
|
358
src/yunohost/tests/test_appscatalog.py
Normal file
358
src/yunohost/tests/test_appscatalog.py
Normal file
|
@ -0,0 +1,358 @@
|
|||
import os
|
||||
import pytest
|
||||
import requests
|
||||
import requests_mock
|
||||
import glob
|
||||
import shutil
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.utils.filesystem import read_json, write_to_json, write_to_yaml, mkdir
|
||||
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.app import (_initialize_apps_catalog_system,
|
||||
_read_apps_catalog_list,
|
||||
_update_apps_catalog,
|
||||
_actual_apps_catalog_api_url,
|
||||
_load_apps_catalog,
|
||||
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 = """{
|
||||
"foo": {"id": "foo", "level": 4},
|
||||
"bar": {"id": "bar", "level": 7}
|
||||
}
|
||||
"""
|
||||
|
||||
class AnyStringWith(str):
|
||||
def __eq__(self, other):
|
||||
return self in other
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
# Clear apps catalog cache
|
||||
# Otherwise when using apps stuff after running the test,
|
||||
# we'll still have the dummy unusable list
|
||||
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
|
||||
|
||||
#
|
||||
# ################################################
|
||||
#
|
||||
|
||||
|
||||
def test_apps_catalog_init(mocker):
|
||||
|
||||
# Cache is empty
|
||||
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()
|
||||
assert len(apps_catalog_list)
|
||||
|
||||
# Cache is expected to still be empty though
|
||||
# (if we did update the apps_catalog during init,
|
||||
# we couldn't differentiate easily exceptions
|
||||
# related to lack of network connectivity)
|
||||
assert not glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
|
||||
|
||||
def test_apps_catalog_emptylist():
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
# Let's imagine somebody removed the default apps catalog because uh idk they dont want to use our default apps catalog
|
||||
os.system("rm %s" % APPS_CATALOG_CONF)
|
||||
os.system("touch %s" % APPS_CATALOG_CONF)
|
||||
|
||||
apps_catalog_list = _read_apps_catalog_list()
|
||||
assert not len(apps_catalog_list)
|
||||
|
||||
|
||||
def test_apps_catalog_update_success(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
# Cache is empty
|
||||
assert not glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
_actual_apps_catalog_api_url,
|
||||
# Mock the server response with a dummy apps catalog
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
_update_apps_catalog()
|
||||
m18n.n.assert_any_call("apps_catalog_updating")
|
||||
m18n.n.assert_any_call("apps_catalog_update_success")
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
|
||||
app_dict = _load_apps_catalog()
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_apps_catalog_update_404(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# 404 error
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL,
|
||||
status_code=404)
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_apps_catalog()
|
||||
m18n.n.assert_any_call("apps_catalog_failed_to_download")
|
||||
|
||||
def test_apps_catalog_update_timeout(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Timeout
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL,
|
||||
exc=requests.exceptions.ConnectTimeout)
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_apps_catalog()
|
||||
m18n.n.assert_any_call("apps_catalog_failed_to_download")
|
||||
|
||||
|
||||
def test_apps_catalog_update_sslerror(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# SSL error
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL,
|
||||
exc=requests.exceptions.SSLError)
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_apps_catalog()
|
||||
m18n.n.assert_any_call("apps_catalog_failed_to_download")
|
||||
|
||||
|
||||
def test_apps_catalog_update_corrupted(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Corrupted json
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL,
|
||||
text=DUMMY_APP_CATALOG[:-2])
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_apps_catalog()
|
||||
m18n.n.assert_any_call("apps_catalog_failed_to_download")
|
||||
|
||||
|
||||
def test_apps_catalog_load_with_empty_cache(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
# Cache is empty
|
||||
assert not glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy apps catalog
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
|
||||
# Try to load the apps catalog
|
||||
# This should implicitly trigger an update in the background
|
||||
mocker.spy(m18n, "n")
|
||||
app_dict = _load_apps_catalog()
|
||||
m18n.n.assert_any_call("apps_catalog_obsolete_cache")
|
||||
m18n.n.assert_any_call("apps_catalog_update_success")
|
||||
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_apps_catalog_load_with_conflicts_between_lists(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
conf = [{"id": "default", "url": APPS_CATALOG_DEFAULT_URL},
|
||||
{"id": "default2", "url": APPS_CATALOG_DEFAULT_URL.replace("yunohost.org", "yolohost.org")}]
|
||||
|
||||
write_to_yaml(APPS_CATALOG_CONF, conf)
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy apps catalog
|
||||
# + the same apps catalog for the second list
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL.replace("yunohost.org", "yolohost.org"), text=DUMMY_APP_CATALOG)
|
||||
|
||||
# Try to load the apps catalog
|
||||
# This should implicitly trigger an update in the background
|
||||
mocker.spy(logger, "warning")
|
||||
app_dict = _load_apps_catalog()
|
||||
logger.warning.assert_any_call(AnyStringWith("Duplicate"))
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_apps_catalog_load_with_oudated_api_version(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
_update_apps_catalog()
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
|
||||
# Tweak the cache to replace the from_api_version with a different one
|
||||
for cache_file in glob.glob(APPS_CATALOG_CACHE + "/*"):
|
||||
cache_json = read_json(cache_file)
|
||||
assert cache_json["from_api_version"] == APPS_CATALOG_API_VERSION
|
||||
cache_json["from_api_version"] = 0
|
||||
write_to_json(cache_file, cache_json)
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy apps catalog
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
app_dict = _load_apps_catalog()
|
||||
m18n.n.assert_any_call("apps_catalog_update_success")
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
# Check that we indeed have the new api number in cache
|
||||
for cache_file in glob.glob(APPS_CATALOG_CACHE + "/*"):
|
||||
cache_json = read_json(cache_file)
|
||||
assert cache_json["from_api_version"] == APPS_CATALOG_API_VERSION
|
||||
|
||||
|
||||
|
||||
def test_apps_catalog_migrate_legacy_explicitly():
|
||||
|
||||
open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}')
|
||||
mkdir(APPS_CATALOG_CACHE, 0o750, parents=True)
|
||||
open(APPS_CATALOG_CACHE+"/yunohost_old.json", "w").write('{"foo":{}, "bar": {}}')
|
||||
open(APPS_CATALOG_CRON_PATH, "w").write("# Some old cron")
|
||||
|
||||
from yunohost.tools import _get_migration_by_name
|
||||
migration = _get_migration_by_name("futureproof_apps_catalog_system")
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy apps catalog
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
migration.migrate()
|
||||
|
||||
# Old conf shouldnt be there anymore (got renamed to .old)
|
||||
assert not os.path.exists("/etc/yunohost/appslists.json")
|
||||
# Old cache should have been removed
|
||||
assert not os.path.exists(APPS_CATALOG_CACHE+"/yunohost_old.json")
|
||||
# Cron should have been changed
|
||||
assert "/bin/bash" in open(APPS_CATALOG_CRON_PATH, "r").read()
|
||||
assert cron_job_is_there()
|
||||
|
||||
# Reading the apps_catalog should work
|
||||
app_dict = _load_apps_catalog()
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_apps_catalog_migrate_legacy_implicitly():
|
||||
|
||||
open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}')
|
||||
mkdir(APPS_CATALOG_CACHE, 0o750, parents=True)
|
||||
open(APPS_CATALOG_CACHE+"/yunohost_old.json", "w").write('{"old_foo":{}, "old_bar": {}}')
|
||||
open(APPS_CATALOG_CRON_PATH, "w").write("# Some old cron")
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
app_dict = _load_apps_catalog()
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
# Old conf shouldnt be there anymore (got renamed to .old)
|
||||
assert not os.path.exists("/etc/yunohost/appslists.json")
|
||||
# Old cache should have been removed
|
||||
assert not os.path.exists(APPS_CATALOG_CACHE+"/yunohost_old.json")
|
||||
# Cron should have been changed
|
||||
assert "/bin/bash" in open(APPS_CATALOG_CRON_PATH, "r").read()
|
||||
assert cron_job_is_there()
|
||||
|
|
@ -1,358 +0,0 @@
|
|||
import os
|
||||
import pytest
|
||||
import requests
|
||||
import requests_mock
|
||||
import glob
|
||||
import shutil
|
||||
|
||||
from moulinette import m18n
|
||||
from moulinette.utils.filesystem import read_json, write_to_json, write_to_yaml, mkdir
|
||||
|
||||
from yunohost.utils.error import YunohostError
|
||||
from yunohost.app import (_initialize_appslists_system,
|
||||
_read_appslist_list,
|
||||
_update_appslist,
|
||||
_actual_appslist_api_url,
|
||||
_load_appslist,
|
||||
logger,
|
||||
APPSLISTS_CACHE,
|
||||
APPSLISTS_CONF,
|
||||
APPSLISTS_CRON_PATH,
|
||||
APPSLISTS_API_VERSION,
|
||||
APPSLISTS_DEFAULT_URL)
|
||||
|
||||
APPSLISTS_DEFAULT_URL_FULL = _actual_appslist_api_url(APPSLISTS_DEFAULT_URL)
|
||||
CRON_FOLDER, CRON_NAME = APPSLISTS_CRON_PATH.rsplit("/", 1)
|
||||
|
||||
DUMMY_APPLIST = """{
|
||||
"foo": {"id": "foo", "level": 4},
|
||||
"bar": {"id": "bar", "level": 7}
|
||||
}
|
||||
"""
|
||||
|
||||
class AnyStringWith(str):
|
||||
def __eq__(self, other):
|
||||
return self in other
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
# Clear applist cache
|
||||
shutil.rmtree(APPSLISTS_CACHE, ignore_errors=True)
|
||||
|
||||
# Clear appslist cron
|
||||
if os.path.exists(APPSLISTS_CRON_PATH):
|
||||
os.remove(APPSLISTS_CRON_PATH)
|
||||
|
||||
# Clear appslist conf
|
||||
if os.path.exists(APPSLISTS_CONF):
|
||||
os.remove(APPSLISTS_CONF)
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
# Clear applist cache
|
||||
# Otherwise when using apps stuff after running the test,
|
||||
# we'll still have the dummy unusable list
|
||||
shutil.rmtree(APPSLISTS_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
|
||||
|
||||
#
|
||||
# ################################################
|
||||
#
|
||||
|
||||
|
||||
def test_appslist_init(mocker):
|
||||
|
||||
# Cache is empty
|
||||
assert not glob.glob(APPSLISTS_CACHE + "/*")
|
||||
# Conf doesn't exist yet
|
||||
assert not os.path.exists(APPSLISTS_CONF)
|
||||
# Conf doesn't exist yet
|
||||
assert not os.path.exists(APPSLISTS_CRON_PATH)
|
||||
|
||||
# Initialize ...
|
||||
mocker.spy(m18n, "n")
|
||||
_initialize_appslists_system()
|
||||
m18n.n.assert_any_call('appslist_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(APPSLISTS_CONF)
|
||||
appslist_list = _read_appslist_list()
|
||||
assert len(appslist_list)
|
||||
|
||||
# Cache is expected to still be empty though
|
||||
# (if we did update the appslist during init,
|
||||
# we couldn't differentiate easily exceptions
|
||||
# related to lack of network connectivity)
|
||||
assert not glob.glob(APPSLISTS_CACHE + "/*")
|
||||
|
||||
|
||||
def test_appslist_emptylist():
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
# Let's imagine somebody removed the default applist because uh idk they dont want to use our default applist
|
||||
os.system("rm %s" % APPSLISTS_CONF)
|
||||
os.system("touch %s" % APPSLISTS_CONF)
|
||||
|
||||
appslist_list = _read_appslist_list()
|
||||
assert not len(appslist_list)
|
||||
|
||||
|
||||
def test_appslist_update_success(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
# Cache is empty
|
||||
assert not glob.glob(APPSLISTS_CACHE + "/*")
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
_actual_appslist_api_url,
|
||||
# Mock the server response with a dummy applist
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL, text=DUMMY_APPLIST)
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
_update_appslist()
|
||||
m18n.n.assert_any_call("appslist_updating")
|
||||
m18n.n.assert_any_call("appslist_update_success")
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPSLISTS_CACHE + "/*")
|
||||
|
||||
app_dict = _load_appslist()
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_appslist_update_404(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# 404 error
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL,
|
||||
status_code=404)
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_appslist()
|
||||
m18n.n.assert_any_call("appslist_failed_to_download")
|
||||
|
||||
def test_appslist_update_timeout(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Timeout
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL,
|
||||
exc=requests.exceptions.ConnectTimeout)
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_appslist()
|
||||
m18n.n.assert_any_call("appslist_failed_to_download")
|
||||
|
||||
|
||||
def test_appslist_update_sslerror(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# SSL error
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL,
|
||||
exc=requests.exceptions.SSLError)
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_appslist()
|
||||
m18n.n.assert_any_call("appslist_failed_to_download")
|
||||
|
||||
|
||||
def test_appslist_update_corrupted(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Corrupted json
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL,
|
||||
text=DUMMY_APPLIST[:-2])
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
mocker.spy(m18n, "n")
|
||||
_update_appslist()
|
||||
m18n.n.assert_any_call("appslist_failed_to_download")
|
||||
|
||||
|
||||
def test_appslist_load_with_empty_cache(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
# Cache is empty
|
||||
assert not glob.glob(APPSLISTS_CACHE + "/*")
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy applist
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL, text=DUMMY_APPLIST)
|
||||
|
||||
# Try to load the applist
|
||||
# This should implicitly trigger an update in the background
|
||||
mocker.spy(m18n, "n")
|
||||
app_dict = _load_appslist()
|
||||
m18n.n.assert_any_call("appslist_obsolete_cache")
|
||||
m18n.n.assert_any_call("appslist_update_success")
|
||||
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPSLISTS_CACHE + "/*")
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_appslist_load_with_conflicts_between_lists(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
conf = [{"id": "default", "url": APPSLISTS_DEFAULT_URL},
|
||||
{"id": "default2", "url": APPSLISTS_DEFAULT_URL.replace("yunohost.org", "yolohost.org")}]
|
||||
|
||||
write_to_yaml(APPSLISTS_CONF, conf)
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy applist
|
||||
# + the same applist for the second list
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL, text=DUMMY_APPLIST)
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL.replace("yunohost.org", "yolohost.org"), text=DUMMY_APPLIST)
|
||||
|
||||
# Try to load the applist
|
||||
# This should implicitly trigger an update in the background
|
||||
mocker.spy(logger, "warning")
|
||||
app_dict = _load_appslist()
|
||||
logger.warning.assert_any_call(AnyStringWith("Duplicate"))
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPSLISTS_CACHE + "/*")
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_appslist_load_with_oudated_api_version(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_appslists_system()
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL, text=DUMMY_APPLIST)
|
||||
_update_appslist()
|
||||
|
||||
# Cache shouldn't be empty anymore empty
|
||||
assert glob.glob(APPSLISTS_CACHE + "/*")
|
||||
|
||||
# Tweak the cache to replace the from_api_version with a different one
|
||||
for cache_file in glob.glob(APPSLISTS_CACHE + "/*"):
|
||||
cache_json = read_json(cache_file)
|
||||
assert cache_json["from_api_version"] == APPSLISTS_API_VERSION
|
||||
cache_json["from_api_version"] = 0
|
||||
write_to_json(cache_file, cache_json)
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy applist
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL, text=DUMMY_APPLIST)
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
app_dict = _load_appslist()
|
||||
m18n.n.assert_any_call("appslist_update_success")
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
# Check that we indeed have the new api number in cache
|
||||
for cache_file in glob.glob(APPSLISTS_CACHE + "/*"):
|
||||
cache_json = read_json(cache_file)
|
||||
assert cache_json["from_api_version"] == APPSLISTS_API_VERSION
|
||||
|
||||
|
||||
|
||||
def test_appslist_migrate_legacy_explicitly():
|
||||
|
||||
open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}')
|
||||
mkdir(APPSLISTS_CACHE, 0o750, parents=True)
|
||||
open(APPSLISTS_CACHE+"/yunohost_old.json", "w").write('{"foo":{}, "bar": {}}')
|
||||
open(APPSLISTS_CRON_PATH, "w").write("# Some old cron")
|
||||
|
||||
from yunohost.tools import _get_migration_by_name
|
||||
migration = _get_migration_by_name("futureproof_appslist_system")
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy applist
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL, text=DUMMY_APPLIST)
|
||||
migration.migrate()
|
||||
|
||||
# Old conf shouldnt be there anymore (got renamed to .old)
|
||||
assert not os.path.exists("/etc/yunohost/appslists.json")
|
||||
# Old cache should have been removed
|
||||
assert not os.path.exists(APPSLISTS_CACHE+"/yunohost_old.json")
|
||||
# Cron should have been changed
|
||||
assert "/bin/bash" in open(APPSLISTS_CRON_PATH, "r").read()
|
||||
assert cron_job_is_there()
|
||||
|
||||
# Reading the appslist should work
|
||||
app_dict = _load_appslist()
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
|
||||
def test_appslist_migrate_legacy_implicitly():
|
||||
|
||||
open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}')
|
||||
mkdir(APPSLISTS_CACHE, 0o750, parents=True)
|
||||
open(APPSLISTS_CACHE+"/yunohost_old.json", "w").write('{"old_foo":{}, "old_bar": {}}')
|
||||
open(APPSLISTS_CRON_PATH, "w").write("# Some old cron")
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
m.register_uri("GET", APPSLISTS_DEFAULT_URL_FULL, text=DUMMY_APPLIST)
|
||||
app_dict = _load_appslist()
|
||||
|
||||
assert "foo" in app_dict.keys()
|
||||
assert "bar" in app_dict.keys()
|
||||
|
||||
# Old conf shouldnt be there anymore (got renamed to .old)
|
||||
assert not os.path.exists("/etc/yunohost/appslists.json")
|
||||
# Old cache should have been removed
|
||||
assert not os.path.exists(APPSLISTS_CACHE+"/yunohost_old.json")
|
||||
# Cron should have been changed
|
||||
assert "/bin/bash" in open(APPSLISTS_CRON_PATH, "r").read()
|
||||
assert cron_job_is_there()
|
||||
|
|
@ -37,7 +37,7 @@ from moulinette.utils.log import getActionLogger
|
|||
from moulinette.utils.process import check_output, call_async_output
|
||||
from moulinette.utils.filesystem import read_json, write_to_json, read_yaml, write_to_yaml
|
||||
|
||||
from yunohost.app import _update_appslist, app_info, app_upgrade, app_ssowatconf, app_list
|
||||
from yunohost.app import _update_apps_catalog, app_info, app_upgrade, app_ssowatconf, app_list
|
||||
from yunohost.domain import domain_add, domain_list
|
||||
from yunohost.dyndns import _dyndns_available, _dyndns_provides
|
||||
from yunohost.firewall import firewall_upnp
|
||||
|
@ -351,14 +351,14 @@ def tools_postinstall(operation_logger, domain, password, ignore_dyndns=False,
|
|||
# Enable UPnP silently and reload firewall
|
||||
firewall_upnp('enable', no_refresh=True)
|
||||
|
||||
# Initialize the appslist system
|
||||
_initialize_appslist_system()
|
||||
# Initialize the apps catalog system
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
# Try to update the appslist ...
|
||||
# Try to update the apps catalog ...
|
||||
# we don't fail miserably if this fails,
|
||||
# because that could be for example an offline installation...
|
||||
try:
|
||||
_update_appslist()
|
||||
_update_apps_catalog()
|
||||
except Exception as e:
|
||||
logger.warning(str(e))
|
||||
|
||||
|
@ -407,7 +407,6 @@ def tools_update(apps=False, system=False):
|
|||
Keyword arguments:
|
||||
system -- Fetch available system packages upgrades (equivalent to apt update)
|
||||
apps -- Fetch the application list to check which apps can be upgraded
|
||||
appslist -- Just update the application list cache
|
||||
"""
|
||||
|
||||
# If neither --apps nor --system specified, do both
|
||||
|
@ -454,7 +453,7 @@ def tools_update(apps=False, system=False):
|
|||
upgradable_apps = []
|
||||
if apps:
|
||||
try:
|
||||
_update_appslist()
|
||||
_update_apps_catalog()
|
||||
except YunohostError as e:
|
||||
logger.error(str(e))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue