Merge pull request #1417 from mwhudson/install-step-refactor

refactor CurtinInstallStep a bit
This commit is contained in:
Michael Hudson-Doyle 2022-09-15 08:44:22 +12:00 committed by GitHub
commit 60d6746a41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 99 deletions

View File

@ -438,12 +438,6 @@ class SubiquityModel:
def render(self):
config = {
'curthooks_commands': {
'001-mount-cdrom': [
'mount', '--bind', '/cdrom', '/target/cdrom',
],
},
'grub': {
'terminal': 'unmodified',
'probe_additional_os': True

View File

@ -278,6 +278,11 @@ class AptConfigurer:
await self.cleanup()
async def setup_target(self):
# Call this after the rootfs has been extracted to the real target
# system but before any configuration is applied to it.
await self.mount('/cdrom', '/target/cdrom', options='bind')
class DryRunAptConfigurer(AptConfigurer):

View File

@ -19,7 +19,6 @@ import os
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
from typing import Any, Callable, Dict, List
@ -75,14 +74,41 @@ class TracebackExtractor:
@attr.s(auto_attribs=True)
class CurtinInstallStep:
""" Represents the parameters of a single step (i.e., invocation of curtin
install). """
"""Represents the parameters of a single invocation of curtin install."""
controller: "InstallController"
name: str
stages: List[str]
config_file: Path
log_file: Path
error_file: Path
acquire_config: Callable[["CurtinInstallStep", Path], Dict[str, Any]]
resume_data_file: Path
source: str
acquire_config: Callable[["CurtinInstallStep"], Dict[str, Any]]
@with_context(
description="executing curtin install {self.name} step")
async def run(self, context):
""" Run a curtin install step. """
self.controller.app.note_file_for_apport(
f"Curtin{self.name}Config", str(self.config_file))
self.controller.write_config(
config_file=self.config_file,
config=self.acquire_config(self)
)
# Make sure the log directory exists.
self.log_file.parent.mkdir(parents=True, exist_ok=True)
# Add a marker to identify the step in the log file.
with open(str(self.log_file), mode="a") as fh:
fh.write(f"\n---- [[ subiquity step {self.name} ]] ----\n")
await run_curtin_command(
self.controller.app, context, "install", self.source,
"--set", f'json:stages={json.dumps(self.stages)}',
config=str(self.config_file), private_mounts=False)
class InstallController(SubiquityController):
@ -120,19 +146,19 @@ class InstallController(SubiquityController):
config_file.parent.mkdir(parents=True, exist_ok=True)
generate_config_yaml(str(config_file), config)
def acquire_generic_config(self, step: CurtinInstallStep,
resume_data_file: Path) -> Dict[str, Any]:
def acquire_generic_config(self,
step: CurtinInstallStep) -> Dict[str, Any]:
""" Return a dictionary object to be used as the configuration of a
generic curtin install step. """
config = self.model.render()
config["install"]["log_file"] = str(step.log_file)
config["install"]["log_file_append"] = True
config["install"]["error_tarfile"] = str(step.error_file)
config["install"]["resume_data"] = str(resume_data_file)
config["install"]["resume_data"] = str(step.resume_data_file)
return config
def acquire_initial_config(self, step: CurtinInstallStep,
resume_data_file: Path) -> Dict[str, Any]:
def acquire_initial_config(self,
step: CurtinInstallStep) -> Dict[str, Any]:
""" Return a dictionary object to be used as the configuration of the
initial curtin install step. """
return {
@ -144,7 +170,7 @@ class InstallController(SubiquityController):
"log_file": str(step.log_file),
"log_file_append": True,
"error_tarfile": str(step.error_file),
"resume_data": str(resume_data_file),
"resume_data": str(step.resume_data_file),
}
}
@ -162,34 +188,9 @@ class InstallController(SubiquityController):
configurer = await mirror.wait_config()
return await configurer.configure_for_install(context)
@with_context(
description="executing curtin install {step.name} step")
async def run_curtin_install_step(
self, *, context, step: CurtinInstallStep, resume_data_file: Path,
source) -> subprocess.CompletedProcess:
""" Run a curtin install step. """
self.app.note_file_for_apport(
f"Curtin{step.name}Config", str(step.config_file))
self.write_config(
config_file=step.config_file,
config=step.acquire_config(step, resume_data_file)
)
# Make sure the log directory exists.
step.log_file.parent.mkdir(parents=True, exist_ok=True)
# Add a marker to identify the step in the log file.
with open(str(step.log_file), mode="a") as fh:
fh.write(f"\n---- [[ subiquity step {step.name} ]] ----\n")
return await run_curtin_command(
self.app, context,
"install", source,
"--set", f'json:stages={json.dumps(step.stages)}',
config=str(step.config_file),
private_mounts=False)
async def setup_target(self, context):
mirror = self.app.controllers.Mirror
await mirror.apt_configurer.setup_target()
@with_context(
description="installing system", level="INFO", childlevel="DEBUG")
@ -210,45 +211,42 @@ class InstallController(SubiquityController):
resume_data_file = Path(tempfile.mkdtemp()) / "resume-data.json"
await self.run_curtin_install_step(step=CurtinInstallStep(
name="initial",
stages=[],
config_file=config_dir / "subiquity-initial.conf",
def make_curtin_step(name, stages, acquire_config):
return CurtinInstallStep(
controller=self,
name=name,
stages=stages,
config_file=config_dir / f"subiquity-{name}.conf",
log_file=install_log_file,
error_file=error_file,
acquire_config=self.acquire_initial_config,
), resume_data_file=resume_data_file,
context=context, source=source)
resume_data_file=resume_data_file,
source=source,
acquire_config=acquire_config,
)
generic_steps = [
CurtinInstallStep(
name="partitioning",
stages=["partitioning"],
config_file=config_dir / "subiquity-partitioning.conf",
log_file=install_log_file,
error_file=error_file,
make_curtin_step(
"initial",
stages=[],
acquire_config=self.acquire_initial_config
).run,
make_curtin_step(
name="partitioning", stages=["partitioning"],
acquire_config=self.acquire_generic_config,
), CurtinInstallStep(
name="extract",
stages=["extract"],
config_file=config_dir / "subiquity-extract.conf",
log_file=install_log_file,
error_file=error_file,
).run,
make_curtin_step(
name="extract", stages=["extract"],
acquire_config=self.acquire_generic_config,
), CurtinInstallStep(
name="curthooks",
stages=["curthooks"],
config_file=config_dir / "subiquity-curthooks.conf",
log_file=install_log_file,
error_file=error_file,
).run,
self.setup_target,
make_curtin_step(
name="curthooks", stages=["curthooks"],
acquire_config=self.acquire_generic_config,
),
).run,
]
for step in generic_steps:
await self.run_curtin_install_step(
step=step, resume_data_file=resume_data_file,
context=context, source=source)
await step(context=context)
@with_context()
async def install(self, *, context):

View File

@ -38,19 +38,19 @@ class TestWriteConfig(unittest.IsolatedAsyncioTestCase):
@patch("subiquity.server.controllers.install.run_curtin_command")
async def test_run_curtin_install_step(self, run_cmd):
step = CurtinInstallStep(
name="MyStep",
stages=["partitioning", "extract"],
config_file=Path("/config.yaml"),
log_file=Path("/logfile.log"),
error_file=Path("/error.tar"),
acquire_config=self.controller.acquire_initial_config)
controller=self.controller,
name="MyStep",
stages=["partitioning", "extract"],
config_file=Path("/config.yaml"),
log_file=Path("/logfile.log"),
error_file=Path("/error.tar"),
resume_data_file=Path("/resume-data.json"),
source="/source",
acquire_config=self.controller.acquire_initial_config)
with patch("subiquity.server.controllers.install.open",
mock_open()) as m_open:
await self.controller.run_curtin_install_step(
step=step,
resume_data_file=Path("/resume-data.json"),
source="/source")
await step.run(context=step.controller.context)
m_open.assert_called_once_with("/logfile.log", mode="a")
@ -64,15 +64,17 @@ class TestWriteConfig(unittest.IsolatedAsyncioTestCase):
def test_acquire_initial_config(self):
step = CurtinInstallStep(
name="initial",
stages=["initial"],
config_file=Path("/config-initial.yaml"),
log_file=Path("/logfile-initial.log"),
error_file=Path("/error-initial.tar"),
acquire_config=self.controller.acquire_initial_config)
controller=self.controller,
name="initial",
stages=["initial"],
config_file=Path("/config-initial.yaml"),
log_file=Path("/logfile-initial.log"),
error_file=Path("/error-initial.tar"),
resume_data_file=Path("/resume-data.json"),
source="/source",
acquire_config=self.controller.acquire_initial_config)
config = self.controller.acquire_initial_config(
step=step, resume_data_file=Path("/resume-data.json"))
config = self.controller.acquire_initial_config(step=step)
self.assertDictEqual(config, {
"install": {
@ -89,17 +91,19 @@ class TestWriteConfig(unittest.IsolatedAsyncioTestCase):
def test_acquire_generic_config(self):
step = CurtinInstallStep(
name="partitioning",
stages=["partitioning"],
config_file=Path("/config-partitioning.yaml"),
log_file=Path("/logfile-partitioning.log"),
error_file=Path("/error-partitioning.tar"),
acquire_config=self.controller.acquire_initial_config)
controller=self.controller,
name="partitioning",
stages=["partitioning"],
config_file=Path("/config-partitioning.yaml"),
log_file=Path("/logfile-partitioning.log"),
error_file=Path("/error-partitioning.tar"),
resume_data_file=Path("/resume-data.json"),
source="/source",
acquire_config=self.controller.acquire_initial_config)
with patch.object(self.controller.model, "render",
return_value={"install": {}}):
config = self.controller.acquire_generic_config(
step=step, resume_data_file=Path("/resume-data.json"))
config = self.controller.acquire_generic_config(step=step)
self.assertEqual(config["install"]["log_file"],
"/logfile-partitioning.log")