Smaller functions, early returns and funny mocking
This commit is contained in:
parent
6be4aea138
commit
eb6bd2d263
|
@ -16,6 +16,7 @@ import os
|
|||
import shutil
|
||||
import logging
|
||||
import re
|
||||
from typing import Tuple
|
||||
import apt
|
||||
import apt_pkg
|
||||
|
||||
|
@ -40,89 +41,189 @@ class ConfigureController(SubiquityController):
|
|||
def start(self):
|
||||
self.install_task = self.app.aio_loop.create_task(self.configure())
|
||||
|
||||
def _update_locale_gen(self, localeGenPath, lang):
|
||||
""" Uncomments the line in locale.gen file where lang is found,
|
||||
if found commented. A fully qualified language is expected,
|
||||
since that would have passed thru the Locale
|
||||
controller validation. e.g. en_UK.UTF-8. """
|
||||
def __locale_gen_cmd(self) -> Tuple[str, bool]:
|
||||
""" Returns the locale-gen command path if not in dry-run.
|
||||
Otherwise, copies the locale-gen script, altering the localedef
|
||||
command line to output into a specified directory.
|
||||
Additionally, indicates success by returning True
|
||||
as the second element of the tuple. """
|
||||
|
||||
fileContents: str
|
||||
locGenNeedsWrite = False
|
||||
with open(localeGenPath, "r") as f:
|
||||
fileContents = f.read()
|
||||
lineFound = fileContents.find(lang)
|
||||
if lineFound == -1:
|
||||
# An unsupported locale coming from our UI is a bug
|
||||
log.error("Selected language %s not supported.", lang)
|
||||
return
|
||||
cmd = "usr/sbin/locale-gen"
|
||||
if self.app.opts.dry_run is False or self.app.opts.dry_run is None:
|
||||
return (os.path.join("/", cmd), True)
|
||||
|
||||
pattern = r'#+\s*({}.*)'.format(lang)
|
||||
commented = re.compile(pattern)
|
||||
(fileContents, n) = commented.subn(r'\1', fileContents, count=1)
|
||||
locGenNeedsWrite = n == 1
|
||||
outDir = os.path.join(self.model.root, "usr/lib/locale")
|
||||
usrSbinDir = os.path.join(self.model.root, "usr/sbin")
|
||||
os.makedirs(outDir, exist_ok=True)
|
||||
os.makedirs(usrSbinDir, exist_ok=True)
|
||||
cmdFile = os.path.join(self.model.root, cmd)
|
||||
shutil.copy(os.path.join("/", cmd), cmdFile)
|
||||
# Supply LC_* definition files to avoid complains from localedef.
|
||||
shutil.copytree("/usr/lib/locale/C.UTF-8/", outDir,
|
||||
dirs_exist_ok=True)
|
||||
|
||||
if locGenNeedsWrite:
|
||||
try:
|
||||
with open(localeGenPath, "wt") as f:
|
||||
f.write(fileContents)
|
||||
except IOError as err:
|
||||
log.error("Failed to modify %s file. %s", localeGenPath, err)
|
||||
try:
|
||||
# Altering locale-gen script to output to the desired folder.
|
||||
with open(cmdFile, "r+") as f:
|
||||
script = f.read()
|
||||
pattern = r'(localedef .+) (\|\| :; \\)'
|
||||
localeDefRe = re.compile(pattern)
|
||||
replacement = r'\1 "{}/" \2'.format(outDir)
|
||||
(fileContents, n) = localeDefRe.subn(replacement, script,
|
||||
count=1)
|
||||
if n != 1:
|
||||
log.error("locale-gen script contents were unexpected."
|
||||
" Aborting mock creation")
|
||||
return ("", False)
|
||||
|
||||
async def _install_check_lang_support_packages(self, lang, env):
|
||||
f.seek(0)
|
||||
f.write(fileContents)
|
||||
|
||||
return (cmdFile, True)
|
||||
|
||||
except IOError as err:
|
||||
log.error("Failed to modify %s file. %s", cmdFile, err)
|
||||
return ("", False)
|
||||
|
||||
def __update_locale_cmd(self, lang) -> list[str]:
|
||||
""" Adds mocking cli to update-locale if in dry-run. """
|
||||
updateLocCmd = ["update-locale", "LANG={}".format(lang)]
|
||||
if self.app.opts.dry_run:
|
||||
defaultLocDir = os.path.join(self.model.root,
|
||||
"etc/default/")
|
||||
os.makedirs(defaultLocDir, exist_ok=True)
|
||||
updateLocCmd += ["--locale-file",
|
||||
os.path.join(defaultLocDir, "locale"),
|
||||
"--no-checks"]
|
||||
|
||||
return updateLocCmd
|
||||
|
||||
async def _activate_locale(self, lang, env) -> bool:
|
||||
""" Last commands to run for locale support. Returns True on success"""
|
||||
|
||||
(locGenCmd, ok) = self.__locale_gen_cmd()
|
||||
if ok is False:
|
||||
log.error("Locale generation failed.")
|
||||
return False
|
||||
|
||||
updateCmd = self.__update_locale_cmd(lang)
|
||||
cmds = [[locGenCmd], updateCmd]
|
||||
for cmd in cmds:
|
||||
cp = await arun_command(cmd, env=env)
|
||||
if cp.returncode != 0:
|
||||
log.error('Command "{}" failed with return code {}'
|
||||
.format(cp.args, cp.returncode))
|
||||
return False
|
||||
return True
|
||||
|
||||
async def _install_check_lang_support_packages(self, lang, env) -> bool:
|
||||
""" Installs any packages recommended by
|
||||
check-language-support command. """
|
||||
|
||||
clsCommand = "check-language-support"
|
||||
# lang may have more than 5 chars and be separated by dot or space.
|
||||
clsLang = lang.split('@')[0].split('.')[0].split(' ')[0]
|
||||
|
||||
cp = await arun_command([clsCommand, "-l", lang[0:5]], env=env)
|
||||
# Running that command doesn't require root.
|
||||
cp = await arun_command([clsCommand, "-l", clsLang], env=env)
|
||||
if cp.returncode != 0:
|
||||
log.error('Command \"%s\" failed with return code %d',
|
||||
log.error('Command "%s" failed with return code %d',
|
||||
cp.args, cp.returncode)
|
||||
return
|
||||
return False
|
||||
|
||||
packages = cp.stdout.strip('\n').split(' ')
|
||||
if len(packages) == 0:
|
||||
log.debug("%s didn't recommend any packages. Nothing to do.",
|
||||
clsCommand)
|
||||
return
|
||||
return True
|
||||
|
||||
cache = apt.Cache()
|
||||
if self.app.opts.dry_run:
|
||||
packs_dir = os.path.join(self.model.root,
|
||||
apt_pkg.config
|
||||
.find_dir("Dir::Cache::Archives")[1:])
|
||||
os.makedirs(packs_dir, exist_ok=True)
|
||||
for package in packages:
|
||||
# Downloading instead of installing. Doesn't require root.
|
||||
packs_dir = os.path.join(self.model.root,
|
||||
apt_pkg.config
|
||||
.find_dir("Dir::Cache::Archives")[1:])
|
||||
archive = os.path.join(packs_dir, cache[package].fullname)
|
||||
cmd = ["wget", "-O", archive, cache[package].candidate.uri]
|
||||
os.makedirs(packs_dir, exist_ok=True)
|
||||
await arun_command(cmd, env=env)
|
||||
else:
|
||||
cache.update()
|
||||
cache.open(None)
|
||||
with cache.actiongroup():
|
||||
for package in packages:
|
||||
cache[package].mark_install()
|
||||
cache.commit()
|
||||
|
||||
async def _activate_locale(self, lang, env):
|
||||
""" Final commands to run for locale support """
|
||||
return True
|
||||
|
||||
cmds = [["locale-gen"],
|
||||
["update-locale", "LANG={}".format(lang)]]
|
||||
if self.app.opts.dry_run:
|
||||
for cmd in cmds:
|
||||
# TODO Figure out about mocks.
|
||||
# It would be good if they offered a -P prefix option,
|
||||
# but they don't.
|
||||
# Chroot'ing is not an option.
|
||||
log.debug('Would run: %s', ' '.join(cmd))
|
||||
else:
|
||||
for cmd in cmds:
|
||||
cp = await arun_command(cmd, env=env)
|
||||
if cp.returncode != 0:
|
||||
raise SystemError('Command {} failed with return code {}'
|
||||
.format(cp.args, cp.returncode))
|
||||
cache.update()
|
||||
cache.open(None)
|
||||
with cache.actiongroup():
|
||||
for package in packages:
|
||||
cache[package].mark_install()
|
||||
|
||||
cache.commit()
|
||||
|
||||
return True
|
||||
|
||||
def _update_locale_gen_file(self, localeGenPath, lang) -> bool:
|
||||
""" Uncomments the line in locale.gen file where lang is found,
|
||||
if found commented. A fully qualified language is expected,
|
||||
since that would have passed thru the Locale controller
|
||||
validation. e.g. en_UK.UTF-8. Returns True for success. """
|
||||
|
||||
fileContents: str
|
||||
try:
|
||||
with open(localeGenPath, "r+") as f:
|
||||
fileContents = f.read()
|
||||
lineFound = fileContents.find(lang)
|
||||
if lineFound == -1:
|
||||
# An unsupported locale coming from our UI is a bug
|
||||
log.error("Selected language %s not supported.", lang)
|
||||
return False
|
||||
|
||||
pattern = r'[# ]*({}.*)'.format(lang)
|
||||
commented = re.compile(pattern)
|
||||
(fileContents, n) = commented.subn(r'\1', fileContents,
|
||||
count=1)
|
||||
if n != 1:
|
||||
log.error("Unexpected locale.gen file contents. Aborting.")
|
||||
return False
|
||||
|
||||
f.seek(0)
|
||||
f.write(fileContents)
|
||||
return True
|
||||
|
||||
except IOError as err:
|
||||
log.error("Failed to modify %s file. %s", localeGenPath, err)
|
||||
return False
|
||||
|
||||
def _locale_gen_file_path(self):
|
||||
localeGenPath = "/etc/locale.gen"
|
||||
if self.app.opts.dry_run is False or self.app.opts.dry_run is None:
|
||||
return localeGenPath
|
||||
|
||||
# For testing purposes.
|
||||
etc_dir = os.path.join(self.model.root, "etc")
|
||||
testLocGenPath = os.path.join(etc_dir,
|
||||
os.path.basename(localeGenPath))
|
||||
shutil.copy(localeGenPath, testLocGenPath)
|
||||
shutil.copy(localeGenPath, "{}-".format(testLocGenPath))
|
||||
return testLocGenPath
|
||||
|
||||
async def apply_locale(self, lang):
|
||||
""" Effectively apply the locale configuration to the new system. """
|
||||
env = os.environ.copy()
|
||||
localeGenPath = self._locale_gen_file_path()
|
||||
if self._update_locale_gen_file(localeGenPath, lang) is False:
|
||||
log.error("Failed to update locale.gen")
|
||||
return
|
||||
|
||||
ok = await self._install_check_lang_support_packages(lang, env)
|
||||
|
||||
if ok is False:
|
||||
log.error("Failed to install recommended language packs.")
|
||||
return
|
||||
|
||||
ok = await self._activate_locale(lang, env)
|
||||
if ok is False:
|
||||
log.error("Failed to run locale activation commands.")
|
||||
return
|
||||
|
||||
@with_context(
|
||||
description="final system configuration", level="INFO",
|
||||
|
@ -154,7 +255,6 @@ class ConfigureController(SubiquityController):
|
|||
|
||||
self.app.update_state(ApplicationState.POST_RUNNING)
|
||||
|
||||
localeGenPath = "/etc/locale.gen"
|
||||
dryrun = self.app.opts.dry_run
|
||||
variant = self.app.variant
|
||||
root_dir = self.model.root
|
||||
|
@ -176,18 +276,6 @@ class ConfigureController(SubiquityController):
|
|||
pseudo_files = ["passwd", "shadow", "gshadow", "group",
|
||||
"subgid", "subuid"]
|
||||
|
||||
# locale
|
||||
testLocGenPath = os.path.join(etc_dir,
|
||||
os.path
|
||||
.basename(localeGenPath))
|
||||
shutil.copy(localeGenPath, testLocGenPath)
|
||||
shutil.copy(localeGenPath,
|
||||
"{}-".format(testLocGenPath))
|
||||
# After copies are done, overwrite the original path
|
||||
# because there is code outside of this 'if'
|
||||
# depending on 'localeGenPath' being initialized.
|
||||
localeGenPath = testLocGenPath
|
||||
|
||||
for file in pseudo_files:
|
||||
filepath = os.path.join(etc_dir, file)
|
||||
open(filepath, "a").close()
|
||||
|
@ -210,11 +298,6 @@ class ConfigureController(SubiquityController):
|
|||
create_user_base = ["-P", root_dir]
|
||||
assign_grp_base = ["-P", root_dir]
|
||||
|
||||
env = os.environ.copy()
|
||||
self._update_locale_gen(localeGenPath, lang)
|
||||
await self._install_check_lang_support_packages(lang, env)
|
||||
await self._activate_locale(lang, env)
|
||||
|
||||
create_user_cmd = ["useradd"] + create_user_base + \
|
||||
["-m", "-s", "/bin/bash",
|
||||
"-c", wsl_id.realname,
|
||||
|
@ -233,6 +316,8 @@ class ConfigureController(SubiquityController):
|
|||
if assign_grp_proc.returncode != 0:
|
||||
raise Exception(("Failed to assign group to user %s: %s")
|
||||
% (username, assign_grp_proc.stderr))
|
||||
|
||||
await self.apply_locale(lang)
|
||||
else:
|
||||
wsl_config_update(self.model.wslconfadvanced.wslconfadvanced,
|
||||
root_dir)
|
||||
|
|
Loading…
Reference in New Issue