diff --git a/bin/subiquity-configure-apt b/bin/subiquity-configure-apt index 2f8d0dd6..3814712e 100755 --- a/bin/subiquity-configure-apt +++ b/bin/subiquity-configure-apt @@ -1,67 +1,3 @@ #!/bin/bash -eux -# Configure apt so that installs from the pool on the cdrom are preferred -# during installation but not in the installed system. -# -# This has a few steps. -# -# 1. As the remaining steps mean that any changes to apt configuration are do -# not persist into the installed system, we get curtin to configure apt a -# bit earlier than it would by default. -# -# 2. Bind-mount the cdrom into the installed system as /run/cdrom -# -# 3. Set up an overlay over /target/etc/apt. This means that any changes we -# make will not persist into the installed system and we do not have to -# worry about cleaning up after ourselves. -# -# 4. Configure apt in /target to look at the pool on the cdrom. This has two -# subcases: -# -# a. if we expect the network to be working, this basically means -# prepending "deb file:///run/cdrom $(lsb_release -sc) main restricted" -# to the sources.list file. -# -# b. if the network is not expected to be working, we replace the -# sources.list with a file just referencing the cdrom. -# -# 5. If the network is not expected to be working, we also set up an overlay -# over /target/var/lib/apt/lists (if the network is working, we'll run "apt -# update" after the /target/etc/apt overlay has been cleared). - -if [ -z "$TARGET_MOUNT_POINT" ]; then - # Nothing good can happen from running this script without - # TARGET_MOUNT_POINT set. - exit 1; -fi - -PY=$1 -HAS_NETWORK=$2 - -$PY -m curtin apt-config - -setup_overlay () { - local dir=$1 - local t=$(mktemp -d) - mkdir $t/work $t/upper - mount -t overlay overlay -o lowerdir=$dir,workdir=$t/work,upperdir=$t/upper $dir -} - -setup_overlay $TARGET_MOUNT_POINT/etc/apt - -mkdir -p "$TARGET_MOUNT_POINT/cdrom" -mount --bind /cdrom "$TARGET_MOUNT_POINT/cdrom" - -if [ $HAS_NETWORK = 'true' ]; then - mv "$TARGET_MOUNT_POINT/etc/apt/sources.list" "$TARGET_MOUNT_POINT/etc/apt/sources.list.d/original.list" -else - # Do not use the configured proxy during the install if one was configured. - rm -f $TARGET_MOUNT_POINT/etc/apt/apt.conf.d/90curtin-aptproxy - setup_overlay $TARGET_MOUNT_POINT/var/lib/apt/lists -fi - -cat > "$TARGET_MOUNT_POINT/etc/apt/sources.list" < TimeZoneInfo: ... def POST(tz: str): ... + class install: + class configure_apt: + def POST(): + "Not for client use." + class shutdown: def POST(mode: ShutdownMode, immediate: bool = False): ... diff --git a/subiquity/models/subiquity.py b/subiquity/models/subiquity.py index 768b4eba..0e5dce12 100644 --- a/subiquity/models/subiquity.py +++ b/subiquity/models/subiquity.py @@ -18,7 +18,6 @@ from collections import OrderedDict import functools import logging import os -import sys import uuid import yaml @@ -338,7 +337,6 @@ class SubiquityModel: 'curthooks_commands': { '001-configure-apt': [ resource_path('bin/subiquity-configure-apt'), - sys.executable, str(self.network.has_network).lower(), ], }, diff --git a/subiquity/server/controllers/install.py b/subiquity/server/controllers/install.py index 3ad63122..f237955c 100644 --- a/subiquity/server/controllers/install.py +++ b/subiquity/server/controllers/install.py @@ -18,6 +18,7 @@ import logging import os import re import shutil +import tempfile from curtin.commands.install import ( ERROR_TARFILE, @@ -31,7 +32,9 @@ from subiquitycore.async_helpers import ( run_in_thread, ) from subiquitycore.context import with_context +from subiquitycore.lsb_release import lsb_release +from subiquity.common.apidef import API from subiquity.common.errorreport import ErrorReportKind from subiquity.common.types import ( ApplicationState, @@ -47,7 +50,6 @@ from subiquity.server.controller import ( SubiquityController, ) - log = logging.getLogger("subiquity.server.controllers.install") @@ -72,6 +74,8 @@ class TracebackExtractor: class InstallController(SubiquityController): + endpoint = API.install + def __init__(self, app): super().__init__(app) self.model = app.base_model @@ -80,6 +84,9 @@ class InstallController(SubiquityController): self.unattended_upgrades_ctx = None self.tb_extractor = TracebackExtractor() + def interactive(self): + return True + def stop_uu(self): if self.app.state == ApplicationState.UU_RUNNING: self.app.update_state(ApplicationState.UU_CANCELLING) @@ -147,14 +154,14 @@ class InstallController(SubiquityController): self.app.update_state(ApplicationState.RUNNING) - config_location = self.write_config() + self.config_location = self.write_config() if os.path.exists(self.model.target): await self.unmount_target( context=context, target=self.model.target) await self.curtin_install( - context=context, config_location=config_location) + context=context, config_location=self.config_location) self.app.update_state(ApplicationState.POST_WAIT) @@ -257,6 +264,88 @@ class InstallController(SubiquityController): self.unattended_upgrades_cmd is not None: self.unattended_upgrades_cmd.proc.terminate() + async def configure_apt_POST(self, context): + # Configure apt so that installs from the pool on the cdrom are + # preferred during installation but not in the installed system. + # + # This has a few steps. + # + # 1. As the remaining steps mean that any changes to apt configuration + # are do not persist into the installed system, we get curtin to + # configure apt a bit earlier than it would by default. + # + # 2. Bind-mount the cdrom into the installed system as /cdrom. + # + # 3. Set up an overlay over /target/etc/apt. This means that any + # changes we make will not persist into the installed system and we + # do not have to worry about cleaning up after ourselves. + # + # 4. Configure apt in /target to look at the pool on the cdrom. This + # has two subcases: + # + # a. if we expect the network to be working, this basically means + # prepending + # "deb file:///run/cdrom $(lsb_release -sc) main restricted" + # to the sources.list file. + # + # b. if the network is not expected to be working, we replace the + # sources.list with a file just referencing the cdrom. + # + # 5. If the network is not expected to be working, we also set up an + # overlay over /target/var/lib/apt/lists (if the network is working, + # we'll run "apt update" after the /target/etc/apt overlay has been + # cleared). + + async def mount(device, mountpoint, options=None, type=None): + opts = [] + if options is not None: + opts.extend(['-o', options]) + if type is not None: + opts.extend(['-t', type]) + await self.app.command_runner.run( + ['mount'] + opts + [device, mountpoint]) + + async def unmount(mountpoint): + await self.app.command_runner.run(['umount', mountpoint]) + + async def setup_overlay(dir): + tdir = tempfile.mkdtemp() + w = f'{tdir}/work' + u = f'{tdir}/upper' + for d in w, u: + os.mkdir(d) + await mount( + 'overlay', dir, type='overlay', + options=f'lowerdir={dir},upperdir={u},workdir={w}') + + await run_curtin_command( + self.app, context, 'apt-config', '-t', self.tpath(), + config=self.config_location) + + await setup_overlay(self.tpath('etc/apt')) + + os.mkdir(self.tpath('cdrom')) + await mount('/cdrom', self.tpath('cdrom'), options='bind') + + if self.model.network.has_network: + os.rename( + self.tpath('etc/apt/sources.list'), + self.tpath('etc/apt/sources.list.d/original.list')) + else: + os.unlink(self.tpath('etc/apt/apt.conf.d/90curtin-aptproxy')) + await setup_overlay(self.tpath('var/lib/apt/lists')) + + codename = lsb_release()['codename'] + + write_file( + self.tpath('etc/apt/sources.list'), + f'deb [check-date=no] file:///cdrom {codename} main restricted\n', + ) + + await run_curtin_command( + self.app, context, "in-target", "-t", self.tpath(), + "--", "apt-get", "update") + uu_apt_conf = b"""\ # Config for the unattended-upgrades run to avoid failing on battery power or