autoinstall: Allow "autoinstall" as top-level key

This commit is contained in:
Chris Peterson 2024-03-22 16:49:19 -07:00
parent a25f2e03c1
commit ba3dbb52ef
2 changed files with 70 additions and 2 deletions

View File

@ -673,9 +673,28 @@ class SubiquityServer(Application):
with open(cfg_path) as fp: with open(cfg_path) as fp:
config: dict[str, Any] = yaml.safe_load(fp) config: dict[str, Any] = yaml.safe_load(fp)
autoinstall_config: dict[str, Any] = dict() autoinstall_config: dict[str, Any]
autoinstall_config = config # Support "autoinstall" as a top-level key
if "autoinstall" in config:
autoinstall_config = config.pop("autoinstall")
# but the only top level key
if len(config) != 0:
self.interactive = bool(autoinstall_config.get("interactive-sections"))
msg: str = (
"autoinstall.yaml is not a valid cloud config datasource.\n"
"No other keys may be present alongside 'autoinstall' at "
"the top level."
)
context.error(msg)
raise AutoinstallValidationError(
owner="top-level keys",
details="autoinstall.yaml is not a valid cloud config datasource",
)
else:
autoinstall_config = config
return autoinstall_config return autoinstall_config

View File

@ -20,6 +20,7 @@ from typing import Any
from unittest.mock import AsyncMock, Mock, patch from unittest.mock import AsyncMock, Mock, patch
import jsonschema import jsonschema
import yaml
from jsonschema.validators import validator_for from jsonschema.validators import validator_for
from subiquity.cloudinit import CloudInitSchemaValidationError from subiquity.cloudinit import CloudInitSchemaValidationError
@ -156,6 +157,7 @@ early-commands: ["{cmd}"]
class TestAutoinstallValidation(SubiTestCase): class TestAutoinstallValidation(SubiTestCase):
async def asyncSetUp(self): async def asyncSetUp(self):
self.tempdir = self.tmp_dir()
opts = Mock() opts = Mock()
opts.dry_run = True opts.dry_run = True
opts.output_base = self.tmp_dir() opts.output_base = self.tmp_dir()
@ -171,6 +173,15 @@ class TestAutoinstallValidation(SubiTestCase):
} }
self.server.make_apport_report = Mock() self.server.make_apport_report = Mock()
def path(self, relative_path):
return self.tmp_path(relative_path, dir=self.tempdir)
def create(self, path, contents):
path = self.path(path)
with open(path, "w") as fp:
fp.write(contents)
return path
# Pseudo Load Controllers to avoid patching the loading logic for each # Pseudo Load Controllers to avoid patching the loading logic for each
# controller when we still want access to class attributes # controller when we still want access to class attributes
def pseudo_load_controllers(self): def pseudo_load_controllers(self):
@ -445,6 +456,44 @@ class TestAutoinstallValidation(SubiTestCase):
self.assertEqual(cfg, expected) self.assertEqual(cfg, expected)
async def test_autoinstall_validation__top_level_autoinstall(self):
"""Test allow autoinstall as top-level key"""
new_style = {
"autoinstall": {
"version": 1,
"interactive-sections": ["identity"],
"apt": "...",
}
}
old_style = new_style["autoinstall"]
# Read new style correctly
path = self.create("autoinstall.yaml", yaml.dump(new_style))
self.assertEqual(self.server._read_config(cfg_path=path), old_style)
# No changes to old style
path = self.create("autoinstall.yaml", yaml.dump(old_style))
self.assertEqual(self.server._read_config(cfg_path=path), old_style)
async def test_autoinstall_validation__not_cloudinit_datasource(self):
"""Test no cloud init datasources in new style autoinstall"""
new_style = {
"autoinstall": {
"version": 1,
"interactive-sections": ["identity"],
"apt": "...",
},
"cloudinit-data": "I am data",
}
with self.assertRaises(AutoinstallValidationError) as ctx:
path = self.create("autoinstall.yaml", yaml.dump(new_style))
self.server._read_config(cfg_path=path)
self.assertEqual("top-level keys", ctx.exception.owner)
class TestMetaController(SubiTestCase): class TestMetaController(SubiTestCase):
async def test_interactive_sections_not_present(self): async def test_interactive_sections_not_present(self):