source: add ability to select source in autoinstall

The source autoinstall section now supports the "id" field where the
user can supply the ID of a source, e.g., "ubuntu-server" or
"ubuntu-server-minimal".

If the field is not supplied, the installation will use the source
declared default: true (if any) in the source catalog. Otherwise, it the
first source declared will be used.

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-08-03 11:54:06 +02:00
parent 5378b5298c
commit 0c73bf9b86
5 changed files with 37 additions and 6 deletions

View File

@ -123,6 +123,9 @@
"properties": { "properties": {
"search_drivers": { "search_drivers": {
"type": "boolean" "type": "boolean"
},
"id": {
"type": "string"
} }
}, },
"required": [ "required": [

View File

@ -13,6 +13,8 @@ late-commands:
- echo a - echo a
keyboard: keyboard:
layout: gb layout: gb
source:
id: ubuntu-server-minimal
updates: security updates: security
user-data: user-data:
users: users:

View File

@ -225,8 +225,11 @@ LANG=C.UTF-8 timeout --foreground 60 \
--output-base "$tmpdir" \ --output-base "$tmpdir" \
--machine-config examples/simple.json \ --machine-config examples/simple.json \
--autoinstall examples/autoinstall-user-data.yaml \ --autoinstall examples/autoinstall-user-data.yaml \
--kernel-cmdline autoinstall --kernel-cmdline autoinstall \
--source-catalog examples/install-sources.yaml
validate 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 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 # The OOBE doesn't exist in WSL < 20.04

View File

@ -88,6 +88,13 @@ class SourceModel:
if self.current is None: if self.current is None:
self.current = self.sources[0] 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): def get_source(self):
path = os.path.join(self._dir, self.current.path) path = os.path.join(self._dir, self.current.path)
if self.current.preinstalled_langs: if self.current.preinstalled_langs:

View File

@ -13,6 +13,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import contextlib
from typing import Any, Optional from typing import Any, Optional
import os import os
@ -59,6 +60,9 @@ class SourceController(SubiquityController):
"search_drivers": { "search_drivers": {
"type": "boolean", "type": "boolean",
}, },
"id": {
"type": "string",
},
}, },
"required": ["search_drivers"], "required": ["search_drivers"],
} }
@ -71,15 +75,19 @@ 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
def make_autoinstall(self): 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: def load_autoinstall_data(self, data: Any) -> None:
if data is None: if data is None:
# For some reason, the schema validator does not reject # For some reason, the schema validator does not reject
# "source: null" despite "type" being "object" # "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 # search_drivers is marked required so the schema validator should
# reject any missing data. # reject any missing data.
@ -87,6 +95,15 @@ class SourceController(SubiquityController):
self.model.search_drivers = data["search_drivers"] 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): def start(self):
path = '/cdrom/casper/install-sources.yaml' path = '/cdrom/casper/install-sources.yaml'
if self.app.opts.source_catalog is not None: if self.app.opts.source_catalog is not None:
@ -129,7 +146,6 @@ class SourceController(SubiquityController):
async def POST(self, source_id: str, async def POST(self, source_id: str,
search_drivers: bool = False) -> None: search_drivers: bool = False) -> None:
self.model.search_drivers = search_drivers self.model.search_drivers = search_drivers
for source in self.model.sources: with contextlib.suppress(KeyError):
if source.id == source_id: self.model.current = self.model.get_matching_source(source_id)
self.model.current = source
await self.configured() await self.configured()