Smaller functions, early returns and funny mocking

This commit is contained in:
Carlos Nihelton 2021-11-08 13:11:30 -03:00
parent 6be4aea138
commit eb6bd2d263
1 changed files with 160 additions and 75 deletions

View File

@ -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)