diff --git a/autoinstall-schema.json b/autoinstall-schema.json index a6bf8e63..34a5c22c 100644 --- a/autoinstall-schema.json +++ b/autoinstall-schema.json @@ -123,6 +123,9 @@ "properties": { "search_drivers": { "type": "boolean" + }, + "id": { + "type": "string" } }, "required": [ diff --git a/examples/autoinstall-user-data.yaml b/examples/autoinstall-user-data.yaml index e7de1003..dfa8b535 100644 --- a/examples/autoinstall-user-data.yaml +++ b/examples/autoinstall-user-data.yaml @@ -13,6 +13,8 @@ late-commands: - echo a keyboard: layout: gb +source: + id: ubuntu-server-minimal updates: security user-data: users: diff --git a/scripts/runtests.sh b/scripts/runtests.sh index 1e9e4faf..88686abb 100755 --- a/scripts/runtests.sh +++ b/scripts/runtests.sh @@ -225,8 +225,11 @@ LANG=C.UTF-8 timeout --foreground 60 \ --output-base "$tmpdir" \ --machine-config examples/simple.json \ --autoinstall examples/autoinstall-user-data.yaml \ - --kernel-cmdline autoinstall + --kernel-cmdline autoinstall \ + --source-catalog examples/install-sources.yaml validate +python3 scripts/check-yaml-fields.py "$tmpdir"/var/log/installer/autoinstall-user-data \ + 'autoinstall.source.id="ubuntu-server-minimal"' grep -q 'finish: subiquity/Install/install/postinstall/run_unattended_upgrades: SUCCESS: downloading and installing security updates' $tmpdir/subiquity-server-debug.log # The OOBE doesn't exist in WSL < 20.04 diff --git a/subiquity/models/source.py b/subiquity/models/source.py index e1df107c..d326f9ce 100644 --- a/subiquity/models/source.py +++ b/subiquity/models/source.py @@ -88,6 +88,13 @@ class SourceModel: if self.current is None: self.current = self.sources[0] + def get_matching_source(self, id_: str) -> CatalogEntry: + """ Return a source object that has the ID requested. """ + for source in self.sources: + if source.id == id_: + return source + raise KeyError + def get_source(self): path = os.path.join(self._dir, self.current.path) if self.current.preinstalled_langs: diff --git a/subiquity/server/controllers/source.py b/subiquity/server/controllers/source.py index 031be32e..06373a9c 100644 --- a/subiquity/server/controllers/source.py +++ b/subiquity/server/controllers/source.py @@ -13,6 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import contextlib from typing import Any, Optional import os @@ -59,6 +60,9 @@ class SourceController(SubiquityController): "search_drivers": { "type": "boolean", }, + "id": { + "type": "string", + }, }, "required": ["search_drivers"], } @@ -71,15 +75,19 @@ class SourceController(SubiquityController): super().__init__(app) self._handler = None self.source_path: Optional[str] = None + self.ai_source_id: Optional[str] = None def make_autoinstall(self): - return {"search_drivers": self.model.search_drivers} + return { + "search_drivers": self.model.search_drivers, + "id": self.model.current.id, + } def load_autoinstall_data(self, data: Any) -> None: if data is None: # For some reason, the schema validator does not reject # "source: null" despite "type" being "object" - data = self.autoinstall_default + data = {**self.autoinstall_default, "id": None} # search_drivers is marked required so the schema validator should # reject any missing data. @@ -87,6 +95,15 @@ class SourceController(SubiquityController): self.model.search_drivers = data["search_drivers"] + # At this point, the model has not yet loaded the sources from the + # catalog. So we store the data and lean on apply_autoinstall_config. + self.ai_source_id = data.get("id") + + async def apply_autoinstall_config(self) -> None: + if self.ai_source_id is None: + return + self.model.current = self.model.get_matching_source(self.ai_source_id) + def start(self): path = '/cdrom/casper/install-sources.yaml' if self.app.opts.source_catalog is not None: @@ -129,7 +146,6 @@ class SourceController(SubiquityController): async def POST(self, source_id: str, search_drivers: bool = False) -> None: self.model.search_drivers = search_drivers - for source in self.model.sources: - if source.id == source_id: - self.model.current = source + with contextlib.suppress(KeyError): + self.model.current = self.model.get_matching_source(source_id) await self.configured()