make CurtinInstallStep more self-contained
This commit is contained in:
parent
80869d23f2
commit
c34f0a8bfa
|
@ -19,9 +19,8 @@ import os
|
|||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Any, Callable, Dict, List
|
||||
from typing import Any, Callable, Dict, List, Protocol
|
||||
|
||||
import attr
|
||||
import yaml
|
||||
|
@ -73,16 +72,49 @@ class TracebackExtractor:
|
|||
self.traceback.append(line)
|
||||
|
||||
|
||||
class InstallStep(Protocol):
|
||||
|
||||
async def run(self, *, context) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@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 +152,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 +176,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,35 +194,6 @@ 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)
|
||||
|
||||
@with_context(
|
||||
description="installing system", level="INFO", childlevel="DEBUG")
|
||||
async def curtin_install(self, *, context, source):
|
||||
|
@ -210,45 +213,41 @@ 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,
|
||||
)
|
||||
|
||||
await make_curtin_step(
|
||||
"initial",
|
||||
stages=[],
|
||||
acquire_config=self.acquire_initial_config).run(context=context)
|
||||
|
||||
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(
|
||||
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,
|
||||
),
|
||||
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,
|
||||
),
|
||||
make_curtin_step(
|
||||
name="curthooks", stages=["curthooks"],
|
||||
acquire_config=self.acquire_generic_config,
|
||||
),
|
||||
]
|
||||
|
||||
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.run(context=context)
|
||||
|
||||
@with_context()
|
||||
async def install(self, *, context):
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue