upgrades: Improve handling of backports

- Merge backports functionality into upgrades module.

- No need to enable systemd timer as dh_installsystemd automatically enables
  this during package installation and upgrade.

- Use https:// and deb.debian.org for repository checking. When using Tor for
  package installations request the URL via Tor.

- Make daily checking service more generic for all kind of future apt repository
  updates.

- Force removal of repository file during purge to avoid failures.

- Don't add contrib/non-free as backports is intended to be enabled for just the
  freedombox package and it is free. When the need arises, we can introduce
  contrib/non-free. This also eliminates an issue that adding these components
  doesn't work without the usage of tor.

- Allow generate apt preferences file to avoid lintian complaining about its
  presence. Remove on purge.

- Add unattended upgrades origin pattern to allow it to upgrade from backports
  repositories.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Sunil Mohan Adapa 2019-02-04 15:31:49 -08:00
parent c0de223ae4
commit 5a159f7d39
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
11 changed files with 120 additions and 190 deletions

View File

@ -1,116 +0,0 @@
#!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of FreedomBox.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Script to check for availability of buster-backports and create an
apt sources list for backports if available.
"""
import argparse
import os
import requests
from plinth import action_utils
BUSTER_BACKPORTS_RELEASE_FILE_URL = "http://cdn-fastly.deb.debian.org/debian/dists/buster-backports/Release"
def parse_arguments():
"""Return parsed command line arguments as dictionary."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
subparsers.add_parser('enable', help='Enable buster backports check')
subparsers.add_parser('disable', help='Disable buster backports check')
subparsers.add_parser('check-backports',
help='Check whether buster backports are available')
subparsers.required = True
return parser.parse_args()
def add_buster_backports_sources():
backports_list = '/etc/apt/sources.list.d/freedombox-backports.list'
conf = \
"""deb http://deb.debian.org/debian buster-backports main
deb-src http://deb.debian.org/debian buster-backports main
"""
components = {"main"}
# Use tor+http if download over tor is enabled in sources.list
with open('/etc/apt/sources.list', 'r') as sources_list:
sources = sources_list.readlines()
for line in sources:
if not line.startswith("#") and "tor+http" in line:
conf = conf.replace("http", "tor+http")
# Check for contrib and nonfree components
if "contrib" in line:
components.add("contrib")
if "nonfree" in line:
components.add("nonfree")
break
conf = conf.replace("main", " ".join(components))
if not os.path.exists(backports_list):
try:
with open(backports_list, 'w') as file_handle:
file_handle.write(conf)
except PermissionError:
print(("Failed adding sources list for buster-backports."
"Try running as a superuser."))
def subcommand_check_backports(_):
"""Check whether buster backports is available.
Add a sources file if it's available.
"""
response = requests.get(BUSTER_BACKPORTS_RELEASE_FILE_URL)
if response.status_code == 404:
print("Release file for Buster backports is not available yet.")
else:
print("Buster backports is now available. Adding to sources...")
add_buster_backports_sources()
def subcommand_enable(_):
"""Enable systemd service for the daily job buster-backports-check."""
action_utils.service_enable('buster-backports-check.timer')
action_utils.service_start('buster-backports-check.timer')
def subcommand_disable(_):
"""Disable systemd service for the daily job buster-backports-check."""
action_utils.service_stop('buster-backports-check.timer')
action_utils.service_disable('buster-backports-check.timer')
def main():
"""Parse arguments and perform all duties."""
arguments = parse_arguments()
subcommand = arguments.subcommand.replace('-', '_')
subcommand_method = globals()['subcommand_' + subcommand]
subcommand_method(arguments)
if __name__ == '__main__':
main()

View File

@ -20,12 +20,16 @@ Configures or runs unattended-upgrades
"""
import argparse
import os
import re
import subprocess
import sys
from plinth import action_utils
AUTO_CONF_FILE = '/etc/apt/apt.conf.d/20auto-upgrades'
LOG_FILE = '/var/log/unattended-upgrades/unattended-upgrades.log'
BUSTER_BACKPORTS_RELEASE_FILE_URL = 'https://deb.debian.org/debian/dists/buster-backports/Release'
def parse_arguments():
@ -40,6 +44,9 @@ def parse_arguments():
subparsers.add_parser('disable-auto', help='Disable automatic upgrades.')
subparsers.add_parser('get-log', help='Print the automatic upgrades log')
subparsers.add_parser('setup-repositories',
help='Setup software repositories for FreedomBox')
subparsers.required = True
return parser.parse_args()
@ -102,6 +109,91 @@ def subcommand_get_log(_):
pass
def _get_protocol():
"""Return the protocol to use for newly added repository sources."""
try:
from plinth.modules.tor import utils
if utils.is_apt_transport_tor_enabled():
return 'tor+http'
except Exception:
pass
return 'http'
def _is_release_file_available(protocol):
"""Return whether the release for backports is available."""
wrapper = None
if protocol == 'tor+http':
wrapper = 'torsocks'
result = action_utils.check_url(BUSTER_BACKPORTS_RELEASE_FILE_URL,
wrapper=wrapper)
return result == 'passed'
def _add_buster_backports_sources(sources_list, protocol):
"""Add buster backports sources to freedombox repositories list."""
sources = '''# This file is managed by FreedomBox, do not edit.
# Allow carefully selected updates to 'freedombox' from backports.
deb {protocol}://deb.debian.org/debian buster-backports main
deb-src {protocol}://deb.debian.org/debian buster-backports main
'''
sources = sources.format(protocol=protocol)
with open(sources_list, 'w') as file_handle:
file_handle.write(sources)
def _check_and_backports_sources():
"""Add buster backports sources after checking if it is available."""
sources_list = '/etc/apt/sources.list.d/freedombox.list'
if os.path.exists(sources_list):
print('Repositories list up-to-date. Skipping update.')
return
protocol = _get_protocol()
if protocol == 'tor+http':
print('Package download over Tor is enabled.')
if not _is_release_file_available(protocol):
print('Release file for Buster backports is not available yet.')
return
print('Buster backports is now available. Adding to sources.')
_add_buster_backports_sources(sources_list, protocol)
def _add_apt_preferences():
"""Setup APT preferences to upgrade selected packages from backports."""
preferences_file = '/etc/apt/preferences.d/50freedombox.pref'
if os.path.exists(preferences_file):
print('Preferences up-to-date. Skipping update')
return
preferences = '''Explanation: This file is managed by FreedomBox, do not edit.
Explanation: Allow carefully selected updates to 'freedombox' from backports.
Package: freedombox
Pin: release a=buster-backports
Pin-Priority: 800
'''
print('Updating APT preferences.')
with open(preferences_file, 'w') as file_handle:
file_handle.write(preferences)
def subcommand_setup_repositories(_):
"""Setup software repositories needed for FreedomBox.
Repositories list for now only contains the backports. If the file exists,
assume that it contains backports.
"""
_check_and_backports_sources()
_add_apt_preferences()
def main():
"""Parse arguments and perform all duties"""
arguments = parse_arguments()

View File

@ -5,3 +5,12 @@ Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// Allow upgrading from backports repository. This origin pattern is added to
// other configured patterns. This only works if the current distribution is a
// stable release. Also all backuports packages have a priority of 100 which is
// ignored. Only packages that have higher priority set explicitly will get
// upgraded. Only selected FreedomBox packages have high priority set on them.
Unattended-Upgrade::Origins-Pattern {
"o=Debian Backports,a=${distro_codename}-backports,l=Debian Backports";
};

View File

@ -1,3 +0,0 @@
Package: freedombox
Pin: release a=buster-backports
Pin-Priority: 800

View File

@ -1,2 +0,0 @@
plinth.modules.backports

View File

@ -1,24 +0,0 @@
#
# This file is part of FreedomBox.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
[Unit]
Description=Service to check for buster backports and enable them
ConditionPathExists=!/etc/apt/sources.list.d/freedombox-backports.list
[Service]
ExecStart=/usr/share/plinth/actions/backports check-backports

View File

@ -14,8 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
URLs for the Backports module.
"""
urlpatterns = []
[Unit]
Description=FreedomBox: Setup software repositories
[Service]
ExecStart=/usr/share/plinth/actions/upgrades setup-repositories
Type=oneshot

View File

@ -16,7 +16,7 @@
#
[Unit]
Description=Daily check of whether buster backports are available yet
Description=FreedomBox: Daily check for setting up software repositories
[Timer]
OnCalendar=daily

20
debian/postrm vendored
View File

@ -4,16 +4,20 @@ set -e
case "$1" in
purge)
deluser --system --quiet plinth || true
rm -rf /var/lib/plinth
deluser --system --quiet plinth || true
rm -rf /var/lib/plinth
# Remove legacy directory too
rm -rf /var/log/plinth
# Remove legacy directory too
rm -rf /var/log/plinth
if [ -e '/etc/apt/sources.list.d/freedombox-backports.list' ]; then
rm /etc/apt/sources.list.d/freedombox-backports.list
fi
;;
if [ -e '/etc/apt/sources.list.d/freedombox.list' ]; then
rm -f /etc/apt/sources.list.d/freedombox.list
fi
if [ -e '/etc/apt/preferences.d/50freedombox.pref' ]; then
rm -f /etc/apt/preferences.d/50freedombox.pref
fi
;;
esac
#DEBHELPER#

View File

@ -1,30 +0,0 @@
#
# This file is part of FreedomBox.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
FreedomBox app to manage Debian backports.
"""
from plinth import actions
is_essential = True
version = 1
def setup(helper, old_version=None):
"""Configure the module."""
helper.call('post', actions.superuser_run, 'backports', ['enable'])

View File

@ -238,8 +238,6 @@ setuptools.setup(
glob.glob('data/etc/apache2/includes/*.conf')),
('/etc/apt/apt.conf.d',
glob.glob('data/etc/apt/apt.conf.d/60unattended-upgrades')),
('/etc/apt/preferences.d',
glob.glob('data/etc/apt/preferences.d/freedombox.pref')),
('/etc/avahi/services/',
glob.glob('data/etc/avahi/services/*.service')),
('/etc/ikiwiki', glob.glob('data/etc/ikiwiki/*.setup')),