#!/usr/bin/python3
"""Install the packages of a feature, then restart vpn-router-setup without waiting.

Run by vpn-router-install@<feature>.service, which vpn-router-setup starts when the feature is on and its packages are
missing: webdav (rclone) and dns (PowerDNS with its gpgsql backend) from the Ubuntu archive, and s3 (versitygw), whose
pinned release (vpnrouter_s3.VERSION) is downloaded for this host's architecture into a new folder, checked against
its pinned SHA-256 and installed with apt-get. Recommended packages are left out, so PowerDNS comes without its bind
backend. A failure is a message on standard error and the exit status 1.

  install-packages FEATURE
"""

import hashlib
import os
import subprocess
import sys
import tempfile
import urllib.request

sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))

import vpnrouter_s3

#: The packages of each feature from the archive.
FEATURES = {'webdav': ['rclone'], 'dns': ['pdns-server', 'pdns-backend-pgsql']}

#: The features installed from a downloaded package.
DOWNLOADS = ('s3',)

#: apt-get, waiting up to ten minutes for dpkg's lock, which a package installation that runs setup holds.
APT = ['apt-get', '-o', 'DPkg::Lock::Timeout=600']

#: The options of apt-get install: no questions, no recommended packages, and the installed configuration files kept.
INSTALL = ['-y', '--no-install-recommends', '-o', 'Dpkg::Options::=--force-confdef', '-o',
           'Dpkg::Options::=--force-confold', 'install']

#: Seconds a download may take.
DOWNLOAD_TIMEOUT = 300


def command(args, environment):
    print(f'$ {" ".join(args)}', flush=True)
    if subprocess.run(args, env=environment).returncode != 0:
        sys.exit(f'install-packages: {args[0]} failed')


def download_versitygw(folder):
    """Download the pinned versitygw .deb of this host's architecture into the folder and check it; its path."""
    arch = vpnrouter_s3.architecture()
    if arch not in vpnrouter_s3.SHA256:
        sys.exit(f'install-packages: versitygw has no package for {arch}; only {", ".join(vpnrouter_s3.SHA256)}')
    url = vpnrouter_s3.DEB_URL.format(version=vpnrouter_s3.VERSION, arch=arch)
    path = os.path.join(folder, os.path.basename(url))
    print(f'Downloading {url}', flush=True)
    digest = hashlib.sha256()
    with urllib.request.urlopen(url, timeout=DOWNLOAD_TIMEOUT) as response, open(path, 'wb') as handle:
        while chunk := response.read(1024 * 1024):
            digest.update(chunk)
            handle.write(chunk)
    if digest.hexdigest() != vpnrouter_s3.SHA256[arch]:
        sys.exit(f'install-packages: {url} has the SHA-256 {digest.hexdigest()}, not {vpnrouter_s3.SHA256[arch]}')
    os.chmod(folder, 0o755)
    os.chmod(path, 0o644)
    return path


def main():
    if len(sys.argv) != 2 or sys.argv[1] not in (*FEATURES, *DOWNLOADS):
        sys.exit(f'usage: install-packages {"|".join((*FEATURES, *DOWNLOADS))}')
    environment = dict(os.environ, DEBIAN_FRONTEND='noninteractive')
    if sys.argv[1] in FEATURES:
        command([*APT, 'update'], environment)
        command([*APT, *INSTALL, *FEATURES[sys.argv[1]]], environment)
    else:
        with tempfile.TemporaryDirectory() as folder:
            try:
                path = download_versitygw(folder)
            except OSError as exc:
                sys.exit(f'install-packages: the download failed: {exc}')
            command([*APT, *INSTALL, path], environment)
    subprocess.run(['systemctl', 'restart', '--no-block', 'vpn-router-setup.service'])
    return 0


if __name__ == '__main__':
    sys.exit(main())
