From 5bd9a4fc94fd8c3d4faaee0636fb32b9da47fecf Mon Sep 17 00:00:00 2001 From: Dan Bungert Date: Tue, 6 Jun 2023 16:56:56 -0600 Subject: [PATCH] server: fix when cloud-config has no default user If a cloud-config is sent that adjusts the users but creates no default user, Subiquity crashes with a exception while mishandling the None. cloudinit.distros.ug_util.extract_default is documented as returning None in the event that the default user cannot be found. Example bad cloud-config: ```yaml users: - name: ubuntu-server ``` --- subiquity/server/server.py | 5 ++++- subiquity/server/tests/test_server.py | 26 +++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/subiquity/server/server.py b/subiquity/server/server.py index a599e7ba..0f54d816 100644 --- a/subiquity/server/server.py +++ b/subiquity/server/server.py @@ -629,7 +629,10 @@ class SubiquityServer(Application): self.installer_user_name = username - if self._user_has_password(username): + if username is None: + # extract_default can return None, if there is no default user + self.installer_user_passwd_kind = PasswordKind.NONE + elif self._user_has_password(username): # Was the password set to a random password by a version of # cloud-init that records the username in the log? (This is the # case we hit on upgrading the subiquity snap) diff --git a/subiquity/server/tests/test_server.py b/subiquity/server/tests/test_server.py index 05e42d11..52c02823 100644 --- a/subiquity/server/tests/test_server.py +++ b/subiquity/server/tests/test_server.py @@ -15,7 +15,7 @@ import os import shlex -from unittest.mock import Mock +from unittest.mock import Mock, patch from subiquitycore.utils import run_command from subiquitycore.tests import SubiTestCase @@ -27,6 +27,7 @@ from subiquity.server.server import ( iso_autoinstall_path, root_autoinstall_path, ) +from subiquity.common.types import PasswordKind class TestAutoinstallLoad(SubiTestCase): @@ -150,3 +151,26 @@ class TestMetaController(SubiTestCase): mc = MetaController(make_app()) mc.app.autoinstall_config['interactive-sections'] = ['network'] self.assertEqual(['network'], await mc.interactive_sections_GET()) + + +class TestDefaultUser(SubiTestCase): + @patch('subiquity.server.server.ug_util.normalize_users_groups', + Mock(return_value=(None, None))) + @patch('subiquity.server.server.ug_util.extract_default', + Mock(return_value=(None, None))) + @patch('subiquity.server.server.user_key_fingerprints', + Mock(side_effect=Exception('should not be called'))) + async def test_no_default_user(self): + opts = Mock() + opts.dry_run = True + opts.output_base = self.tmp_dir() + opts.machine_config = 'examples/simple.json' + server = SubiquityServer(opts, None) + server.cloud = Mock() + server._user_has_password = Mock( + side_effect=Exception('should not be called')) + + opts.dry_run = False # exciting! + server.set_installer_password() + self.assertIsNone(server.installer_user_name) + self.assertEqual(PasswordKind.NONE, server.installer_user_passwd_kind)