Merge pull request #1897 from Chris-Peterson444/autoinstall-exception-FR-6293

AutoinstallError exception
This commit is contained in:
Chris Peterson 2024-02-05 16:19:15 -08:00 committed by GitHub
commit 48e5f9c616
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 686 additions and 8 deletions

View File

@ -6,6 +6,12 @@
"minimum": 1,
"maximum": 1
},
"interactive-sections": {
"type": "array",
"items": {
"type": "string"
}
},
"early-commands": {
"type": "array",
"items": {

View File

@ -6,6 +6,12 @@
"minimum": 1,
"maximum": 1
},
"interactive-sections": {
"type": "array",
"items": {
"type": "string"
}
},
"early-commands": {
"type": "array",
"items": {

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import logging
log = logging.getLogger("subiquity.server.autoinstall")
class AutoinstallError(Exception):
pass
class AutoinstallValidationError(AutoinstallError):
def __init__(
self,
owner: str,
):
self.message = f"Malformed autoinstall in {owner!r} section"
self.owner = owner
super().__init__(self.message)

View File

@ -19,8 +19,10 @@ import os
from typing import Any, Optional
import jsonschema
from jsonschema.exceptions import ValidationError
from subiquity.common.api.server import bind
from subiquity.server.autoinstall import AutoinstallValidationError
from subiquity.server.types import InstallerChannels
from subiquitycore.context import with_context
from subiquitycore.controller import BaseController
@ -54,6 +56,19 @@ class SubiquityController(BaseController):
await self.configured()
self._active = False
def validate_autoinstall(self, ai_data: dict) -> None:
try:
jsonschema.validate(ai_data, self.autoinstall_schema)
except ValidationError as original_exception:
section = self.autoinstall_key
new_exception: AutoinstallValidationError = AutoinstallValidationError(
section,
)
raise new_exception from original_exception
def setup_autoinstall(self):
if not self.app.autoinstall_config:
return
@ -72,7 +87,8 @@ class SubiquityController(BaseController):
ai_data = self.autoinstall_default
if ai_data is not None and self.autoinstall_schema is not None:
jsonschema.validate(ai_data, self.autoinstall_schema)
self.validate_autoinstall(ai_data)
self.load_autoinstall_data(ai_data)
def load_autoinstall_data(self, data):

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.cmdlist import CmdListController
from subiquitycore.tests import SubiTestCase
class TestCmdListController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
CmdListController.autoinstall_schema
)
JsonValidator.check_schema(CmdListController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.codecs import CodecsController
from subiquitycore.tests import SubiTestCase
class TestCodecsController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
CodecsController.autoinstall_schema
)
JsonValidator.check_schema(CodecsController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.debconf import DebconfController
from subiquitycore.tests import SubiTestCase
class TestDebconfController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
DebconfController.autoinstall_schema
)
JsonValidator.check_schema(DebconfController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.drivers import DriversController
from subiquitycore.tests import SubiTestCase
class TestDriversController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
DriversController.autoinstall_schema
)
JsonValidator.check_schema(DriversController.autoinstall_schema)

View File

@ -18,7 +18,9 @@ import subprocess
import uuid
from unittest import IsolatedAsyncioTestCase, mock
import jsonschema
from curtin.commands.extract import TrivialSourceHandler
from jsonschema.validators import validator_for
from subiquity.common.filesystem import gaps, labels
from subiquity.common.filesystem.actions import DeviceAction
@ -399,6 +401,15 @@ class TestSubiquityControllerFilesystem(IsolatedAsyncioTestCase):
self.assertEqual(len(self.fsc._variation_info), 1)
self.assertEqual(self.fsc._variation_info["default"].name, "default")
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
FilesystemController.autoinstall_schema
)
JsonValidator.check_schema(FilesystemController.autoinstall_schema)
class TestGuided(IsolatedAsyncioTestCase):
boot_expectations = [

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.identity import IdentityController
from subiquitycore.tests import SubiTestCase
class TestIdentityController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
IdentityController.autoinstall_schema
)
JsonValidator.check_schema(IdentityController.autoinstall_schema)

View File

@ -17,6 +17,9 @@ import os
import unittest
from unittest.mock import Mock, patch
import jsonschema
from jsonschema.validators import validator_for
from subiquity.common.types import KeyboardSetting
from subiquity.models.keyboard import KeyboardModel
from subiquity.server.controllers.keyboard import KeyboardController
@ -29,6 +32,16 @@ class opts:
dry_run = True
class TestKeyboardController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
KeyboardController.autoinstall_schema
)
JsonValidator.check_schema(KeyboardController.autoinstall_schema)
class TestSubiquityModel(SubiTestCase):
async def test_write_config(self):
os.environ["SUBIQUITY_REPLAY_TIMESCALE"] = "100"

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.locale import LocaleController
from subiquitycore.tests import SubiTestCase
class TestLocaleController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
LocaleController.autoinstall_schema
)
JsonValidator.check_schema(LocaleController.autoinstall_schema)

View File

@ -19,6 +19,7 @@ import unittest
from unittest import mock
import jsonschema
from jsonschema.validators import validator_for
from subiquity.common.types import MirrorSelectionFallback
from subiquity.models.mirror import MirrorModel
@ -264,3 +265,12 @@ class TestMirrorController(unittest.IsolatedAsyncioTestCase):
mock_fallback.assert_not_called()
await controller.run_mirror_selection_or_fallback(context=None)
mock_fallback.assert_called_once()
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
MirrorController.autoinstall_schema
)
JsonValidator.check_schema(MirrorController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.network import NetworkController
from subiquitycore.tests import SubiTestCase
class TestNetworkController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
NetworkController.autoinstall_schema
)
JsonValidator.check_schema(NetworkController.autoinstall_schema)

View File

@ -16,6 +16,9 @@
import subprocess
from unittest.mock import Mock, patch
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.oem import OEMController
from subiquitycore.tests import SubiTestCase
from subiquitycore.tests.mocks import make_app
@ -110,3 +113,12 @@ Ubuntu-Oem-Kernel-Flavour: oem
"oem-sutton-balint-meta", context=None, overlay=Mock()
)
)
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
OEMController.autoinstall_schema
)
JsonValidator.check_schema(OEMController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.package import PackageController
from subiquitycore.tests import SubiTestCase
class TestPackageController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
PackageController.autoinstall_schema
)
JsonValidator.check_schema(PackageController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.proxy import ProxyController
from subiquitycore.tests import SubiTestCase
class TestProxyController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
ProxyController.autoinstall_schema
)
JsonValidator.check_schema(ProxyController.autoinstall_schema)

View File

@ -15,6 +15,9 @@
from unittest import mock
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server import snapdapi
from subiquity.server.controllers import refresh as refresh_mod
from subiquity.server.controllers.refresh import RefreshController, SnapChannelSource
@ -99,3 +102,12 @@ class TestRefreshController(SubiTestCase):
await self.rc.configure_snapd(context=self.rc.context)
paw.assert_not_called()
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
RefreshController.autoinstall_schema
)
JsonValidator.check_schema(RefreshController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.reporting import ReportingController
from subiquitycore.tests import SubiTestCase
class TestReportingController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
ReportingController.autoinstall_schema
)
JsonValidator.check_schema(ReportingController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.shutdown import ShutdownController
from subiquitycore.tests import SubiTestCase
class TestShutdownController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
ShutdownController.autoinstall_schema
)
JsonValidator.check_schema(ShutdownController.autoinstall_schema)

View File

@ -16,13 +16,17 @@
import unittest
from unittest.mock import AsyncMock
import jsonschema
import requests
from jsonschema.validators import validator_for
from subiquity.models.snaplist import SnapListModel
from subiquity.server.controllers.snaplist import (
SnapdSnapInfoLoader,
SnapListController,
SnapListFetchError,
)
from subiquitycore.tests import SubiTestCase
from subiquitycore.tests.mocks import make_app
@ -56,3 +60,14 @@ class TestSnapdSnapInfoLoader(unittest.IsolatedAsyncioTestCase):
await self.loader.get_snap_list_task()
self.assertTrue(self.loader.fetch_list_completed())
self.assertFalse(self.loader.fetch_list_failed())
class TestSnapListController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
SnapListController.autoinstall_schema
)
JsonValidator.check_schema(SnapListController.autoinstall_schema)

View File

@ -16,6 +16,9 @@
import unittest
from unittest import mock
import jsonschema
from jsonschema.validators import validator_for
from subiquity.common.types import SSHFetchIdStatus, SSHIdentity
from subiquity.server.controllers.ssh import (
SSHController,
@ -116,3 +119,12 @@ class TestSSHController(unittest.IsolatedAsyncioTestCase):
self.assertEqual(response.status, SSHFetchIdStatus.FINGERPRINT_ERROR)
self.assertEqual(response.error, stderr)
self.assertIsNone(response.identities)
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
SSHController.autoinstall_schema
)
JsonValidator.check_schema(SSHController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.timezone import TimeZoneController
from subiquitycore.tests import SubiTestCase
class TestTimeZoneController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
TimeZoneController.autoinstall_schema
)
JsonValidator.check_schema(TimeZoneController.autoinstall_schema)

View File

@ -15,6 +15,9 @@
import unittest
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.ubuntu_pro import UbuntuProController
from subiquity.server.dryrun import DRConfig
from subiquitycore.tests.mocks import make_app
@ -33,3 +36,12 @@ class TestUbuntuProController(unittest.TestCase):
def test_deserialize(self):
self.controller.deserialize("1A2B3C4D")
self.assertEqual(self.controller.model.token, "1A2B3C4D")
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
UbuntuProController.autoinstall_schema
)
JsonValidator.check_schema(UbuntuProController.autoinstall_schema)

View File

@ -0,0 +1,31 @@
# Copyright 2024 Canonical, Ltd.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# 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/>.
import jsonschema
from jsonschema.validators import validator_for
from subiquity.server.controllers.updates import UpdatesController
from subiquitycore.tests import SubiTestCase
class TestUpdatesController(SubiTestCase):
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
UpdatesController.autoinstall_schema
)
JsonValidator.check_schema(UpdatesController.autoinstall_schema)

View File

@ -15,7 +15,9 @@
import unittest
import jsonschema
from cloudinit.config.schema import SchemaValidationError
from jsonschema.validators import validator_for
from subiquity.server.controllers.userdata import UserdataController
from subiquitycore.tests.mocks import make_app
@ -58,3 +60,12 @@ class TestUserdataController(unittest.TestCase):
validate.assert_called_with(
data=invalid_schema, data_source="autoinstall.user-data"
)
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
UserdataController.autoinstall_schema
)
JsonValidator.check_schema(UserdataController.autoinstall_schema)

View File

@ -24,12 +24,13 @@ import jsonschema
import yaml
from aiohttp import web
from cloudinit.config.cc_set_passwords import rand_user_password
from jsonschema.exceptions import ValidationError
from systemd import journal
from subiquity.cloudinit import get_host_combined_cloud_config
from subiquity.common.api.server import bind, controller_for_request
from subiquity.common.apidef import API
from subiquity.common.errorreport import ErrorReporter, ErrorReportKind
from subiquity.common.errorreport import ErrorReport, ErrorReporter, ErrorReportKind
from subiquity.common.serialize import to_json
from subiquity.common.types import (
ApplicationState,
@ -40,6 +41,7 @@ from subiquity.common.types import (
PasswordKind,
)
from subiquity.models.subiquity import ModelNames, SubiquityModel
from subiquity.server.autoinstall import AutoinstallError, AutoinstallValidationError
from subiquity.server.controller import SubiquityController
from subiquity.server.dryrun import DRConfig
from subiquity.server.errors import ErrorController
@ -222,6 +224,12 @@ class SubiquityServer(Application):
"minimum": 1,
"maximum": 1,
},
"interactive-sections": {
"type": "array",
"items": {
"type": "string",
},
},
},
"required": ["version"],
"additionalProperties": True,
@ -395,7 +403,8 @@ class SubiquityServer(Application):
def make_apport_report(self, kind, thing, *, wait=False, **kw):
return self.error_reporter.make_apport_report(kind, thing, wait=wait, **kw)
async def _run_error_cmds(self, report):
async def _run_error_cmds(self, report: Optional[ErrorReport] = None) -> None:
if report is not None and report._info_task is not None:
await report._info_task
Error = getattr(self.controllers, "Error", None)
if Error is not None and Error.cmds:
@ -413,7 +422,7 @@ class SubiquityServer(Application):
return
report = self.error_reporter.report_for_exc(exc)
log.error("top level error", exc_info=exc)
if not report:
if not isinstance(exc, AutoinstallError) and not report:
report = self.make_apport_report(
ErrorReportKind.UNKNOWN, "unknown error", exc=exc
)
@ -466,6 +475,20 @@ class SubiquityServer(Application):
await controller.apply_autoinstall_config()
await controller.configured()
def validate_autoinstall(self):
with self.context.child("core_validation", level="INFO"):
try:
jsonschema.validate(self.autoinstall_config, self.base_schema)
except ValidationError as original_exception:
# SubiquityServer currently only checks for these sections
# of autoinstall. Hardcode until we have better validation.
section = "version or interative-sessions"
new_exception: AutoinstallValidationError = AutoinstallValidationError(
section,
)
raise new_exception from original_exception
def load_autoinstall_config(self, *, only_early):
log.debug(
"load_autoinstall_config only_early %s file %s",
@ -480,8 +503,7 @@ class SubiquityServer(Application):
self.controllers.Reporting.setup_autoinstall()
self.controllers.Reporting.start()
self.controllers.Error.setup_autoinstall()
with self.context.child("core_validation", level="INFO"):
jsonschema.validate(self.autoinstall_config, self.base_schema)
self.validate_autoinstall()
self.controllers.Early.setup_autoinstall()
else:
for controller in self.controllers.instances:

View File

@ -16,6 +16,7 @@
import contextlib
from unittest.mock import patch
from subiquity.server.autoinstall import AutoinstallValidationError
from subiquity.server.controller import SubiquityController
from subiquitycore.tests import SubiTestCase
from subiquitycore.tests.mocks import make_app
@ -41,7 +42,6 @@ class TestController(SubiTestCase):
}
self.controller.autoinstall_key = "sample"
self.controller.autoinstall_key_alias = "sample-alias"
self.controller.autoinstall_default = "default-data"
self.controller.setup_autoinstall()
mock_load.assert_called_once_with("some-sample-data")
@ -56,5 +56,36 @@ class TestController(SubiTestCase):
mock_load.reset_mock()
self.controller.autoinstall_key = "inexistent"
self.controller.autoinstall_key_alias = "inexistent"
self.controller.autoinstall_default = "default-data"
self.controller.setup_autoinstall()
mock_load.assert_called_once_with("default-data")
def test_autoinstall_validation(self):
"""Test validation error type and no apport reporting"""
self.controller.autoinstall_schema = {
"type": "object",
"properties": {
"some-key": {
"type": "boolean",
},
},
}
self.bad_ai_data = {"some-key": "not a bool"}
self.controller.autoinstall_key = "some-key"
# Assert error type is correct
with self.assertRaises(AutoinstallValidationError) as ctx:
self.controller.validate_autoinstall(self.bad_ai_data)
exception = ctx.exception
# Assert error section is based on autoinstall_key
self.assertEquals(exception.owner, "some-key")
# Assert apport report is not created
# This only checks that controllers do not manually create an apport
# report on validation. Should also be tested in Server
self.controller.app.make_apport_report.assert_not_called()

View File

@ -17,7 +17,11 @@ import os
import shlex
from unittest.mock import Mock, patch
import jsonschema
from jsonschema.validators import validator_for
from subiquity.common.types import PasswordKind
from subiquity.server.autoinstall import AutoinstallValidationError
from subiquity.server.server import (
MetaController,
SubiquityServer,
@ -141,6 +145,56 @@ early-commands: ["{cmd}"]
self.assertEqual(after_early, self.server.autoinstall_config)
class TestAutoinstallValidation(SubiTestCase):
async def asyncSetUp(self):
opts = Mock()
opts.dry_run = True
opts.output_base = self.tmp_dir()
opts.machine_config = "examples/machines/simple.json"
self.server = SubiquityServer(opts, None)
self.server.base_schema = {
"type": "object",
"properties": {
"some-key": {
"type": "boolean",
},
},
}
self.server.make_apport_report = Mock()
def test_valid_schema(self):
"""Test that the expected autoinstall JSON schema is valid"""
JsonValidator: jsonschema.protocols.Validator = validator_for(
SubiquityServer.base_schema
)
JsonValidator.check_schema(SubiquityServer.base_schema)
def test_autoinstall_validation__error_type(self):
"""Test that bad autoinstall data throws AutoinstallValidationError"""
bad_ai_data = {"some-key": "not a bool"}
self.server.autoinstall_config = bad_ai_data
with self.assertRaises(AutoinstallValidationError):
self.server.validate_autoinstall()
async def test_autoinstall_validation__no_error_report(self):
"""Test no apport reporting"""
exception = AutoinstallValidationError("Mock")
loop = Mock()
context = {"exception": exception}
with patch("subiquity.server.server.log"):
with patch.object(self.server, "_run_error_cmds"):
self.server._exception_handler(loop, context)
self.server.make_apport_report.assert_not_called()
class TestMetaController(SubiTestCase):
async def test_interactive_sections_not_present(self):
mc = MetaController(make_app())

View File

@ -50,5 +50,6 @@ def make_app(model=None):
app.log_syslog_id = None
app.report_start_event = mock.Mock()
app.report_finish_event = mock.Mock()
app.make_apport_report = mock.Mock()
return app