From b6c77c9e43a5e6fbc7e3326b2c09eaa7bc7114ad Mon Sep 17 00:00:00 2001 From: kload Date: Mon, 28 Sep 2015 16:53:36 +0000 Subject: [PATCH] [enh] Regenerate configuration via hook scripts --- data/actionsmap/yunohost.yml | 43 +++++++++-- data/hooks/conf_regen/15-nginx | 57 +++++++++++++++ lib/yunohost/domain.py | 10 ++- lib/yunohost/hook.py | 10 +++ lib/yunohost/service.py | 127 +++++++-------------------------- 5 files changed, 140 insertions(+), 107 deletions(-) create mode 100644 data/hooks/conf_regen/15-nginx diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 93bc7642b..a037b7899 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -243,6 +243,10 @@ domain: authenticate: all authenticator: ldap-anonymous arguments: + -r: + full: --raw + help: Return domains as a bash-usable list instead of JSON + action: store_true -f: full: --filter help: LDAP filter used to search @@ -879,18 +883,47 @@ service: Prints the differences between files if any. api: PUT /services/regenconf arguments: - -n: - full: --name + -s: + full: --service help: Regenerate configuration for a specfic service -f: full: --force help: Override the current configuration with the newly generated one, even if it has been modified action: store_true - -k: - full: --keep - help: Save the current configuration to avoid further notifications + + ### service_safecopy() + safecopy: + action_help: > + Check if the specific file has been modified and display differences. + Stores the file hash in the services.yml file + api: PUT /services/safecopy + arguments: + service: + help: Service name attached to the conf file + new_conf_file: + help: Path to the desired conf file + conf_file: + help: Path to the targeted conf file + -f: + full: --force + help: Override the current configuration with the newly generated one, even if it has been modified action: store_true + ### service_saferemove() + saferemove: + action_help: > + Check if the specific file has been modified before removing it. + Backup the file in /home/yunohost.backup + api: PUT /services/safecopy + arguments: + service: + help: Service name attached to the conf file + conf_file: + help: Path to the targeted conf file + -f: + full: --force + help: Force file deletion + action: store_true ############################# # Firewall # diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx new file mode 100644 index 000000000..2cc5eae36 --- /dev/null +++ b/data/hooks/conf_regen/15-nginx @@ -0,0 +1,57 @@ +#!/bin/bash +set -e + +force=$1 + +function safe_copy () { + if $force; then + sudo yunohost service safecopy \ + -s nginx \ + --force \ + $1 $2 + else + sudo yunohost service safecopy \ + -s nginx \ + $1 $2 + fi +} + +cd /usr/share/yunohost/templates/nginx + +# Copy plain single configuration files +$files="ssowat.conf +yunohost_admin.conf +yunohost_admin.conf.inc +yunohost_api.conf.inc +yunohost_panel.conf.inc" + +for file in files; do + safe_copy $file /etc/nginx/conf.d/$file +done + + +# Copy 'yunohost.local' to the main domain conf directory +main_domain=$(sudo yunohost tools maindomain) +safe_copy yunohost_local.conf \ + /etc/nginx/conf.d/$main_domain.d/yunohost_local.conf + + +need_restart=0 + +# Copy a configuration file for each YunoHost domain +for domain in $(sudo yunohost domain list --raw); do + sudo mkdir /etc/nginx/conf.d/$domain.d + cat server.conf.sed \ + | sed "s/{{ domain }}/$domain/g" \ + | sudo tee $domain.conf + if $(safe_copy $domain.conf /etc/nginx/conf.d/$domain.conf); then + need_restart=1 + fi +done + +# Restart if need be +if $need_restart; then + service nginx restart +else + service nginx reload +fi diff --git a/lib/yunohost/domain.py b/lib/yunohost/domain.py index d8880554f..2463e9776 100644 --- a/lib/yunohost/domain.py +++ b/lib/yunohost/domain.py @@ -36,11 +36,12 @@ from urllib import urlopen from moulinette.core import MoulinetteError -def domain_list(auth, filter=None, limit=None, offset=None): +def domain_list(auth, raw=False, filter=None, limit=None, offset=None): """ List domains Keyword argument: + raw -- Return domains as a bash-usable list instead of JSON filter -- LDAP filter used to search offset -- Starting number for domain fetching limit -- Maximum number of domain fetched @@ -61,7 +62,12 @@ def domain_list(auth, filter=None, limit=None, offset=None): if len(result) > offset and limit > 0: for domain in result[offset:offset+limit]: result_list.append(domain['virtualdomain'][0]) - return { 'domains': result_list } + + if raw: + for domain in result_list: + print domain + else: + return { 'domains': result_list } def domain_add(auth, domain, dyndns=False): diff --git a/lib/yunohost/hook.py b/lib/yunohost/hook.py index 635800e31..4c4bd063f 100644 --- a/lib/yunohost/hook.py +++ b/lib/yunohost/hook.py @@ -185,6 +185,16 @@ def hook_callback(action, hooks=[], args=None): else: hooks_names = hook_list(action, list_by='name', show_info=True)['hooks'] + + # Add similar hooks to the list + # For example: Having a 16-postfix hook in the list will execute a + # xx-postfix_dkim as well + for n in hooks: + for key in hooks.keys(): + if key.startswith("%s_" % n) \ + and n not in hooks: + hooks.append(n) + # Iterate over given hooks names list for n in hooks: try: diff --git a/lib/yunohost/service.py b/lib/yunohost/service.py index 3be221b20..26ac27d68 100644 --- a/lib/yunohost/service.py +++ b/lib/yunohost/service.py @@ -30,7 +30,6 @@ import glob import subprocess import errno import shutil -import jinja2 import difflib import hashlib @@ -270,26 +269,35 @@ def service_log(name, number=50): return result -def service_regenconf(auth, name=None, force=False, keep=False): +def service_regenconf(service=None, force=False): """ Regenerate the configuration file(s) for a service and compare the result with the existing configuration file. Prints the differences between files if any. Keyword argument: - name -- Regenerate configuration for a specfic service + service -- Regenerate configuration for a specfic service force -- Override the current configuration with the newly generated one, even if it has been modified - keep -- Save the current configuration to avoid further notifications """ - if name is not None: - _regenerate_configuration_for(auth, name, force, keep) - - - #TODO: Raise error when force + keep - #TODO: Loop through all the services - #TODO: Win message with regenerated configurations + if force: + arg_force = 1 + else: + arg_force = 0 + + if service is None: + # Regen ALL THE CONFIGURATIONS + hook_callback('conf_regen', args=[arg_force]) + + msignals.display(m18n.n('services_configured'), 'success') + else: + if service not in _get_services().keys(): + raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service)) + + hook_callback('conf_regen', [service] , args=[arg_force]) + + msignals.display(m18n.n('service_configured', service), 'success') def _run_service_command(action, service): @@ -417,16 +425,15 @@ def _hash(filename): return 'no hash yet' -def _safe_remove(conf_file, service=None, force=False, keep=False): +def service_saferemove(service, conf_file, force=False): """ Check if the specific file has been modified before removing it. Backup the file in /home/yunohost.backup Keyword argument: - conf_file -- The file to write service -- Service name of the file to delete + conf_file -- The file to write force -- Force file deletion - keep -- Keep the current file and save its hash """ deleted = False @@ -487,24 +494,23 @@ def _safe_remove(conf_file, service=None, force=False, keep=False): return deleted -def _safe_write(conf_file, new_conf='', service=None, force=False, keep=False): +def service_safecopy(service, new_conf_file, conf_file, force=False): """ Check if the specific file has been modified and display differences. Stores the file hash in the services.yml file Keyword argument: - conf_file -- The file to write - new_conf -- String containing the new content - service -- Service name of the file to write + service -- Service name attached to the conf file + new_conf_file -- Path to the desired conf file + conf_file -- Path to the targeted conf file force -- Force file overriding - keep -- Keep the current file and save its hash """ regenerated = False services = _get_services() - if os.path.exists(new_conf): - filename = new_conf + if os.path.exists(new_conf_file): + filename = new_conf_file with open(filename, 'r') as f: new_conf = ''.join(f.readlines()).rstrip() @@ -537,8 +543,6 @@ def _safe_write(conf_file, new_conf='', service=None, force=False, keep=False): with open(conf_file, 'w') as f: f.write(new_conf) regenerated = True new_hash = _hash(conf_file) - elif keep: - new_hash = previous_hash[0:32] + ', but keep ' + current_hash elif len(diff) == 0: new_hash = _hash(conf_file) else: @@ -565,80 +569,3 @@ def _safe_write(conf_file, new_conf='', service=None, force=False, keep=False): _save_services(services) return regenerated - - -def _regenerate_configuration_for(auth, service, force=False, keep=False): - """ - Handle all the different services' configurations of YunoHost - - Keyword argument: - service -- Service name to take care of - force -- Force configuration overriding - keep -- Keep the current configuration and save its hash - - """ - from yunohost.domain import domain_list - - if service not in _get_services().keys() \ - or not os.path.isdir("%s/%s" % (template_dir, service)): - raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service)) - - # Set the service's template directory as Jinja Environment in order - # to ease the template loading - env = jinja2.Environment( - loader=jinja2.FileSystemLoader("%s/%s" % (template_dir, service)) - ) - - domains = domain_list(auth)['domains'] - - with open('/etc/yunohost/current_host', 'r') as f: - main_domain = f.readline().rstrip() - - if service == 'nginx': - - need_restart = False - - # Copy plain files - for filename in [ - 'ssowat.conf', - 'yunohost_admin.conf', - 'yunohost_admin.conf.inc', - 'yunohost_api.conf.inc', - 'yunohost_panel.conf.inc', - ]: - conf_file = '/etc/nginx/conf.d/%s' % filename - new_conf = '%s/%s/%s' % (template_dir, service, filename) - _safe_write(conf_file, new_conf, service, force, keep) - - # We need one file and one folder per virtualhost - for domain in domains: - conf_file = '/etc/nginx/conf.d/%s.conf' % domain - new_conf = env.get_template('server.conf.j2').render(domain=domain) - need_restart = _safe_write(conf_file, new_conf, service, force, keep) \ - or need_restart - try: - os.makedirs('/etc/nginx/conf.d/%s.d' % domain) - except OSError: pass - - # Copy yunohost_local.conf for the main domain - filename = 'yunohost_local.conf' - conf_file = '/etc/nginx/conf.d/%s.d/%s' % (main_domain, filename) - new_conf = '%s/%s/%s' % (template_dir, service, filename) - _safe_write(conf_file, new_conf, service, force, keep) - - # Backup and remove configuration for unexisting domains - for conf_file in os.listdir('/etc/nginx/conf.d/'): - if conf_file.endswith('.conf') and len(conf_file.split('.')) > 2 \ - and conf_file.replace('.conf', '') not in domains: - _safe_remove('/etc/nginx/conf.d/'+ conf_file, service, force, keep) - - # Restart Nginx - if need_restart: - _run_service_command('restart', service) - else: - _run_service_command('reload', service) - - msignals.display(m18n.n('service_configured', service), 'success') - - if service == 'postfix': - pass