source: determine variant earlier than .start()

The current check for Desktop in the identity controller doesn't
work because the variant isn't set until SourceController.start
is called. Move this logic to earlier in the sequence so that the
variant information can be referenced by later-loaded controllers.
This commit is contained in:
Chris Peterson 2024-05-08 16:00:23 -07:00
parent 3b3e3310d6
commit 68ae5a4cdc
3 changed files with 65 additions and 17 deletions

View File

@ -29,6 +29,8 @@ from subiquity.common.types import SourceSelection, SourceSelectionAndSetting
from subiquity.server.controller import SubiquityController from subiquity.server.controller import SubiquityController
from subiquity.server.types import InstallerChannels from subiquity.server.types import InstallerChannels
SOURCES_DEFAULT_PATH = "/cdrom/casper/install-sources.yaml"
log = logging.getLogger("subiquity.server.controllers.source") log = logging.getLogger("subiquity.server.controllers.source")
@ -77,16 +79,22 @@ class SourceController(SubiquityController):
super().__init__(app) super().__init__(app)
self._handler = None self._handler = None
self.source_path: Optional[str] = None self.source_path: Optional[str] = None
self.ai_source_id: Optional[str] = None
self._configured: bool = False self._configured: bool = False
path = SOURCES_DEFAULT_PATH
if self.app.opts.source_catalog is not None:
path = self.app.opts.source_catalog
if os.path.exists(path):
with open(path) as fp:
self.model.load_from_file(fp)
def make_autoinstall(self): def make_autoinstall(self):
return { return {
"search_drivers": self.model.search_drivers, "search_drivers": self.model.search_drivers,
"id": self.model.current.id, "id": self.model.current.id,
} }
def load_autoinstall_data(self, data: Any) -> None: def load_autoinstall_data(self, data: Optional[dict[str, Any]]) -> None:
if data is None: if data is None:
data = {} data = {}
@ -98,22 +106,11 @@ class SourceController(SubiquityController):
"search_drivers", SEARCH_DRIVERS_AUTOINSTALL_DEFAULT "search_drivers", SEARCH_DRIVERS_AUTOINSTALL_DEFAULT
) )
# At this point, the model has not yet loaded the sources from the # Assign the current source if hinted by autoinstall.
# catalog. So we store the ID and lean on self.start to select the if id := data.get("id"):
# current source accordingly. self.model.current = self.model.get_matching_source(id)
self.ai_source_id = data.get("id")
def start(self): def start(self):
path = "/cdrom/casper/install-sources.yaml"
if self.app.opts.source_catalog is not None:
path = self.app.opts.source_catalog
if not os.path.exists(path):
return
with open(path) as fp:
self.model.load_from_file(fp)
# Assign the current source if hinted by autoinstall.
if self.ai_source_id is not None:
self.model.current = self.model.get_matching_source(self.ai_source_id)
self.app.hub.subscribe( self.app.hub.subscribe(
(InstallerChannels.CONFIGURED, "locale"), self._set_locale (InstallerChannels.CONFIGURED, "locale"), self._set_locale
) )

View File

@ -19,6 +19,7 @@ from jsonschema.validators import validator_for
from subiquity.server.autoinstall import AutoinstallError from subiquity.server.autoinstall import AutoinstallError
from subiquity.server.controllers.filesystem import FilesystemController from subiquity.server.controllers.filesystem import FilesystemController
from subiquity.server.controllers.identity import IdentityController from subiquity.server.controllers.identity import IdentityController
from subiquity.server.controllers.source import SourceController
from subiquitycore.tests import SubiTestCase from subiquitycore.tests import SubiTestCase
from subiquitycore.tests.mocks import make_app from subiquitycore.tests.mocks import make_app
from subiquitycore.tests.parameterized import parameterized from subiquitycore.tests.parameterized import parameterized
@ -43,6 +44,8 @@ class TestControllerUserCreationFlows(SubiTestCase):
self.app.opts.bootloader = False self.app.opts.bootloader = False
self.app.controllers.Filesystem = FilesystemController(self.app) self.app.controllers.Filesystem = FilesystemController(self.app)
self.ic = IdentityController(self.app) self.ic = IdentityController(self.app)
self.app.opts.source_catalog = "examples/sources/install.yaml"
self.app.controllers.Source = SourceController(self.app)
self.ic.model.user = None self.ic.model.user = None
# Test cases for 4a1. Copied for 4a2 but all cases should be valid for desktop. # Test cases for 4a1. Copied for 4a2 but all cases should be valid for desktop.

View File

@ -17,8 +17,14 @@ import unittest
from subiquity.common.serialize import Serializer from subiquity.common.serialize import Serializer
from subiquity.models.source import CatalogEntry from subiquity.models.source import CatalogEntry
from subiquity.models.subiquity import SubiquityModel
from subiquity.models.tests.test_source import make_entry as make_raw_entry from subiquity.models.tests.test_source import make_entry as make_raw_entry
from subiquity.server.controllers.source import convert_source from subiquity.server.controllers.source import SourceController, convert_source
from subiquity.server.server import INSTALL_MODEL_NAMES, POSTINSTALL_MODEL_NAMES
from subiquitycore.pubsub import MessageHub
from subiquitycore.tests import SubiTestCase
from subiquitycore.tests.mocks import make_app
from subiquitycore.tests.parameterized import parameterized
def make_entry(**kw): def make_entry(**kw):
@ -44,3 +50,45 @@ class TestSubiquityModel(unittest.TestCase):
self.assertEqual(convert_source(entry, "fr").name, "French") self.assertEqual(convert_source(entry, "fr").name, "French")
self.assertEqual(convert_source(entry, "fr_CA").name, "French Canadian") self.assertEqual(convert_source(entry, "fr_CA").name, "French Canadian")
self.assertEqual(convert_source(entry, "fr_BE").name, "French") self.assertEqual(convert_source(entry, "fr_BE").name, "French")
class TestSourceController(SubiTestCase):
def setUp(self):
self.base_model = SubiquityModel(
"test", MessageHub(), INSTALL_MODEL_NAMES, POSTINSTALL_MODEL_NAMES
)
self.app = make_app(model=self.base_model)
self.app.opts.source_catalog = "examples/sources/install.yaml"
self.controller = SourceController(self.app)
def _set_source_catalog(self, path):
self.app.opts.source_catalog = path
self.controller = SourceController(self.app)
@parameterized.expand(
# (Sources list, is_desktop)
(
("examples/sources/desktop.yaml", "desktop"),
("examples/sources/install.yaml", "server"),
)
)
def test_install_source_detection__defaults(self, catalog, expected):
"""Test source detection with defaults."""
self._set_source_catalog(catalog)
variant = self.controller.model.current.variant
self.assertEqual(variant, expected)
@parameterized.expand(
# (Sources list, ai_data, expected)
(
("examples/sources/mixed.yaml", {"id": "ubuntu-desktop"}, "desktop"),
("examples/sources/mixed.yaml", {"id": "ubuntu-server"}, "server"),
)
)
def test_install_source_detection__autoinstall(self, catalog, ai_data, expected):
"""Test source detection with autoinstall."""
self._set_source_catalog(catalog)
self.controller.load_autoinstall_data(ai_data)
self.assertEqual(self.controller.model.current.variant, expected)