identity: move config validation to load_autoinstall_data

Moves the logic for checking if user-data or identity section is
provided (on server) in the autoinstall config to the
load_autoinstall_data function. Without this change, the exception
thrown in apply_autoinstall_config won't halt the installer until
the postinstall steps (LP: 2060547).
This commit is contained in:
Chris Peterson 2024-04-08 14:51:03 -07:00
parent c6a1b583de
commit 88f8b21633
2 changed files with 61 additions and 11 deletions

View File

@ -22,8 +22,8 @@ import attr
from subiquity.common.apidef import API from subiquity.common.apidef import API
from subiquity.common.resources import resource_path from subiquity.common.resources import resource_path
from subiquity.common.types import IdentityData, UsernameValidation from subiquity.common.types import IdentityData, UsernameValidation
from subiquity.server.autoinstall import AutoinstallError
from subiquity.server.controller import SubiquityController from subiquity.server.controller import SubiquityController
from subiquitycore.context import with_context
log = logging.getLogger("subiquity.server.controllers.identity") log = logging.getLogger("subiquity.server.controllers.identity")
@ -79,18 +79,29 @@ class IdentityController(SubiquityController):
crypted_password=data["password"], crypted_password=data["password"],
) )
self.model.add_user(identity_data) self.model.add_user(identity_data)
@with_context()
async def apply_autoinstall_config(self, context=None):
if self.model.user:
return return
# The identity section is required except if (any):
# 1. a user-data section is provided
if "user-data" in self.app.autoinstall_config: if "user-data" in self.app.autoinstall_config:
return return
if self.app.base_model.target is None: # 2. we are installing not-Server (Desktop)
return
if self.app.base_model.source.current.variant != "server": if self.app.base_model.source.current.variant != "server":
return return
raise Exception("neither identity nor user-data provided") # 3. we are only refreshing the reset partition
# (The identity controller doesn't figure this out until the apply
# step, so we are going to cheat and inspect the situation here)
storage_config = self.app.autoinstall_config.get("storage")
if (
storage_config is not None
and storage_config.get("layout") is not None
and storage_config["layout"].get("reset-partition-only")
):
return
# 4. identity section is interactive
if self.interactive():
return
raise AutoinstallError("neither identity nor user-data provided")
def make_autoinstall(self): def make_autoinstall(self):
if self.model.user is None: if self.model.user is None:

View File

@ -16,6 +16,7 @@
import jsonschema import jsonschema
from jsonschema.validators import validator_for from jsonschema.validators import validator_for
from subiquity.server.autoinstall import AutoinstallError
from subiquity.server.controllers.identity import IdentityController from subiquity.server.controllers.identity import IdentityController
from subiquitycore.tests import SubiTestCase from subiquitycore.tests import SubiTestCase
from subiquitycore.tests.mocks import make_app from subiquitycore.tests.mocks import make_app
@ -42,10 +43,48 @@ class TestControllerUserCreationFlows(SubiTestCase):
async def test_server_requires_identity_case_4a1(self): async def test_server_requires_identity_case_4a1(self):
self.app.base_model.source.current.variant = "server" self.app.base_model.source.current.variant = "server"
with self.assertRaises(Exception):
await self.ic.apply_autoinstall_config() # Autoinstall: no identity or user data and identity is not interactive
self.app.autoinstall_config = {"interactive-sections": ["not-identity"]}
with self.assertRaises(AutoinstallError):
self.ic.load_autoinstall_data(None)
async def test_server_requires_identity_case_4a1__ok_interactive(self):
"""Test no require identity for interactive identity"""
self.app.base_model.source.current.variant = "server"
# Explicitly interactive
self.app.autoinstall_config = {"interactive-sections": ["identity"]}
self.ic.load_autoinstall_data(None)
# Implicitly interactive
self.app.autoinstall_config = {"interactive-sections": ["*"]}
self.ic.load_autoinstall_data(None)
# No Autoinstall => interactive
self.app.autoinstall_config = {}
self.ic.load_autoinstall_data(None)
async def test_server_requires_identity_case_4a1__reset_only_true(self):
"""Test no require identity for reset-partition-only=yes."""
self.app.base_model.source.current.variant = "server"
# No raise if reset-parition-only specified
self.app.autoinstall_config = {
"storage": {"layout": {"reset-partition-only": True}}
}
self.ic.load_autoinstall_data(None)
async def test_server_requires_identity_case_4a1__reset_only_false(self):
"""Test require identity for reset-partition-only=no."""
self.app.base_model.source.current.variant = "server"
# raises if no reset-parition-only in storage:layout:
self.app.autoinstall_config = {"storage": {"layout": {}}}
with self.assertRaises(AutoinstallError):
self.ic.load_autoinstall_data(None)
async def test_desktop_does_not_require_identity_case_4a2(self): async def test_desktop_does_not_require_identity_case_4a2(self):
self.app.base_model.source.current.variant = "desktop" self.app.base_model.source.current.variant = "desktop"
await self.ic.apply_autoinstall_config() self.ic.load_autoinstall_data(None)
# should not raise # should not raise