From 0ad21b7c96fae870632c632cd16a2e8807a00183 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Thu, 17 Dec 2020 14:49:18 +1300 Subject: [PATCH] smoosh InstallState into ApplicationState Having these be separate was slightly confusing, particular as I need to add an "ERROR" state that isn't necessarily due to the install step itself failing. Now we have one enum for the overall status of the installer and a separate field that indicates whether the client should be in interactive or non-interactive mode (or "not-yet-known" which is handled more or less like non-interactive mode). --- DESIGN.md | 35 ++++++----- subiquity/client/client.py | 42 +++++-------- subiquity/client/controllers/progress.py | 30 +++++----- subiquity/common/apidef.py | 7 +-- subiquity/common/types.py | 60 ++++++++----------- subiquity/server/controllers/cmdlist.py | 6 +- subiquity/server/controllers/install.py | 55 ++++------------- subiquity/server/controllers/locale.py | 2 +- subiquity/server/controllers/reboot.py | 6 +- subiquity/server/controllers/reporting.py | 2 +- subiquity/server/server.py | 37 ++++++------ subiquity/ui/views/installprogress.py | 20 +++---- .../ui/views/tests/test_installprogress.py | 4 +- 13 files changed, 128 insertions(+), 178 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 626cdd35..4f359204 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -168,8 +168,8 @@ The API takes a "long poll" approach to status updates. For example, `wait=False` and if the result indicates that the check for updates is still in progress it shows a screen indicating this and calls it again with `wait=True`, which will not return until the check has completed (or failed). In a similar -vein, `install.status.GET()` takes an argument indicating what the client -thinks the install state currently is and will block until that changes. +vein, `meta.status.GET()` takes an argument indicating what the client +thinks the application state currently is and will block until that changes. ### Examples and common patterns @@ -332,24 +332,31 @@ controllers have methods that are called to load and apply the autoinstall data for each controller. The only real difference to the client is that it behaves totally differently if the install is to be totally automated: in this case it does not start the urwid-based UI at all and mostly just "listens" to install -progress via journald and the `install.status.GET()` API call. +progress via journald and the `meta.status.GET()` API call. -### Starting and confirming the install +### The server state machine -The installation code proceeds in stages: +The server code proceeds in stages: - 1. First it waits for all the model objects that feed into the curtin config - to be configured. - 2. It waits for confirmation. - 3. It runs "curtin install" and waits for that to finish. - 4. It waits for the model objects that feed into the cloud-init config to be + 1. It starts up, checks for an autoinstall config and runs any early + commands. + 2. Then it waits for all the model objects that feed into the curtin + config to be configured. + 3. It waits for confirmation. + 4. It runs "curtin install" and waits for that to finish. + 5. It waits for the model objects that feed into the cloud-init config to be configured. - 5. If there appears to be a working network connection, it downloads and + 6. It creates the cloud-init config for the first boot of the + installed system. + 7. If there appears to be a working network connection, it downloads and installs security updates. - 6. It waits for the user to click "reboot". + 8. It runs any late commands. + 9. It waits for the user to click "reboot". -Each of these states gets a different value of the `InstallState` enum, so the -client gets notified via long-polling `install.status.GET()` of progress. +Each of these states gets a different value of the `ApplicationState` +enum, so the client gets notified via long-polling `meta.status.GET()` +of progress. In addition, `ApplicationState.ERROR` indicates something +has gone wrong. ### Refreshing the snap diff --git a/subiquity/client/client.py b/subiquity/client/client.py index 6d662e13..0a7fefd9 100644 --- a/subiquity/client/client.py +++ b/subiquity/client/client.py @@ -46,7 +46,6 @@ from subiquity.common.types import ( ApplicationState, ErrorReportKind, ErrorReportRef, - InstallState, ) from subiquity.journald import journald_listen from subiquity.ui.frame import SubiquityUI @@ -206,24 +205,24 @@ class SubiquityClient(TuiApplication): answer = await run_in_thread(input) await self.confirm_install() - async def noninteractive_watch_install_state(self): - install_state = None + async def noninteractive_watch_app_state(self, initial_status): + app_status = initial_status confirm_task = None while True: - try: - install_status = await self.client.install.status.GET( - cur=install_state) - install_state = install_status.state - except aiohttp.ClientError: - await asyncio.sleep(1) - continue - if install_state == InstallState.NEEDS_CONFIRMATION: - if confirm_task is not None: + app_state = app_status.state + if app_state == ApplicationState.NEEDS_CONFIRMATION: + if confirm_task is None: confirm_task = self.aio_loop.create_task( self.noninteractive_confirmation()) elif confirm_task is not None: confirm_task.cancel() confirm_task = None + try: + app_status = await self.client.meta.status.GET( + cur=app_state) + except aiohttp.ClientError: + await asyncio.sleep(1) + continue def subiquity_event_noninteractive(self, event): if event['SUBIQUITY_EVENT_TYPE'] == 'start': @@ -244,28 +243,20 @@ class SubiquityClient(TuiApplication): print(".", end='', flush=True) else: break - print() + print("\nconnected") journald_listen( self.aio_loop, [status.echo_syslog_id], lambda e: print(e['MESSAGE'])) - if status.state == ApplicationState.STARTING: - print("server is starting...", end='', flush=True) - while status.state == ApplicationState.STARTING: - await asyncio.sleep(1) - print(".", end='', flush=True) - status = await self.client.meta.status.GET() - print() - if status.state == ApplicationState.EARLY_COMMANDS: - print("running early commands...") + if status.state == ApplicationState.STARTING_UP: status = await self.client.meta.status.GET(cur=status.state) await asyncio.sleep(0.5) return status async def start(self): status = await self.connect() - if status.state == ApplicationState.INTERACTIVE: - self.interactive = True + self.interactive = status.interactive + if self.interactive: await super().start() journald_listen( self.aio_loop, @@ -283,7 +274,6 @@ class SubiquityClient(TuiApplication): self.show_error_report(report.ref()) break else: - self.interactive = False if self.opts.run_on_serial: # Thanks to the fact that we are launched with agetty's # --skip-login option, on serial lines we can end up starting @@ -299,7 +289,7 @@ class SubiquityClient(TuiApplication): self.subiquity_event_noninteractive, seek=True) self.aio_loop.create_task( - self.noninteractive_watch_install_state()) + self.noninteractive_watch_app_state(status)) def _exception_handler(self, loop, context): exc = context.get('exception') diff --git a/subiquity/client/controllers/progress.py b/subiquity/client/controllers/progress.py index e02bdf12..c3dbafe2 100644 --- a/subiquity/client/controllers/progress.py +++ b/subiquity/client/controllers/progress.py @@ -21,7 +21,7 @@ import aiohttp from subiquitycore.context import with_context from subiquity.client.controller import SubiquityTuiController -from subiquity.common.types import InstallState +from subiquity.common.types import ApplicationState from subiquity.ui.views.installprogress import ( InstallRunning, ProgressView, @@ -33,12 +33,10 @@ log = logging.getLogger("subiquity.client.controllers.progress") class ProgressController(SubiquityTuiController): - endpoint_name = 'install' - def __init__(self, app): super().__init__(app) self.progress_view = ProgressView(self) - self.install_state = None + self.app_state = None self.crash_report_ref = None self.answers = app.answers.get("InstallProgress", {}) @@ -77,43 +75,43 @@ class ProgressController(SubiquityTuiController): install_running = None while True: try: - install_status = await self.endpoint.status.GET( - cur=self.install_state) + app_status = await self.app.client.meta.status.GET( + cur=self.app_state) except aiohttp.ClientError: await asyncio.sleep(1) continue - self.install_state = install_status.state + self.app_state = app_status.state - self.progress_view.update_for_state(self.install_state) + self.progress_view.update_for_state(self.app_state) if self.ui.body is self.progress_view: self.ui.set_header(self.progress_view.title) - if install_status.error is not None: + if app_status.error is not None: if self.crash_report_ref is None: - self.crash_report_ref = install_status.error + self.crash_report_ref = app_status.error self.ui.set_body(self.progress_view) self.app.show_error_report(self.crash_report_ref) - if self.install_state == InstallState.NEEDS_CONFIRMATION: + if self.app_state == ApplicationState.NEEDS_CONFIRMATION: if self.showing: self.app.show_confirm_install() - if self.install_state == InstallState.RUNNING: - if install_status.confirming_tty != self.app.our_tty: + if self.app_state == ApplicationState.RUNNING: + if app_status.confirming_tty != self.app.our_tty: install_running = InstallRunning( - self.app, install_status.confirming_tty) + self.app, app_status.confirming_tty) self.app.add_global_overlay(install_running) else: if install_running is not None: self.app.remove_global_overlay(install_running) install_running = None - if self.install_state == InstallState.DONE: + if self.app_state == ApplicationState.DONE: if self.answers.get('reboot', False): self.click_reboot() def make_ui(self): - if self.install_state == InstallState.NEEDS_CONFIRMATION: + if self.app_state == ApplicationState.NEEDS_CONFIRMATION: self.app.show_confirm_install() return self.progress_view diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py index cf5acef5..999bd4d4 100644 --- a/subiquity/common/apidef.py +++ b/subiquity/common/apidef.py @@ -29,8 +29,6 @@ from subiquity.common.types import ( ErrorReportRef, KeyboardSetting, IdentityData, - InstallState, - InstallStatus, RefreshStatus, SnapInfo, SnapListResponse, @@ -79,6 +77,7 @@ class API: class crash: def GET() -> None: """Requests to this method will fail with a HTTP 500.""" + class refresh: def GET(wait: bool = False) -> RefreshStatus: """Get information about the snap refresh status. @@ -182,10 +181,6 @@ class API: class snap_info: def GET(snap_name: str) -> SnapInfo: ... - class install: - class status: - def GET(cur: Optional[InstallState] = None) -> InstallStatus: ... - class reboot: def POST(): ... diff --git a/subiquity/common/types.py b/subiquity/common/types.py index 0f07ba02..514b4c6b 100644 --- a/subiquity/common/types.py +++ b/subiquity/common/types.py @@ -25,22 +25,6 @@ from typing import List, Optional import attr -class ApplicationState(enum.Enum): - STARTING = enum.auto() - EARLY_COMMANDS = enum.auto() - INTERACTIVE = enum.auto() - NON_INTERACTIVE = enum.auto() - - -@attr.s(auto_attribs=True) -class ApplicationStatus: - state: ApplicationState - cloud_init_ok: bool - echo_syslog_id: str - log_syslog_id: str - event_syslog_id: str - - class ErrorReportState(enum.Enum): INCOMPLETE = enum.auto() LOADING = enum.auto() @@ -68,6 +52,31 @@ class ErrorReportRef: oops_id: Optional[str] +class ApplicationState(enum.Enum): + STARTING_UP = enum.auto() + WAITING = enum.auto() + NEEDS_CONFIRMATION = enum.auto() + RUNNING = enum.auto() + POST_WAIT = enum.auto() + POST_RUNNING = enum.auto() + UU_RUNNING = enum.auto() + UU_CANCELLING = enum.auto() + DONE = enum.auto() + ERROR = enum.auto() + + +@attr.s(auto_attribs=True) +class ApplicationStatus: + state: ApplicationState + confirming_tty: str + error: Optional[ErrorReportRef] + cloud_init_ok: bool + interactive: Optional[bool] + echo_syslog_id: str + log_syslog_id: str + event_syslog_id: str + + class RefreshCheckState(enum.Enum): UNKNOWN = enum.auto() AVAILABLE = enum.auto() @@ -195,22 +204,3 @@ class SnapListResponse: status: SnapCheckState snaps: List[SnapInfo] = attr.Factory(list) selections: List[SnapSelection] = attr.Factory(list) - - -class InstallState(enum.Enum): - NOT_STARTED = enum.auto() - NEEDS_CONFIRMATION = enum.auto() - RUNNING = enum.auto() - POST_WAIT = enum.auto() - POST_RUNNING = enum.auto() - UU_RUNNING = enum.auto() - UU_CANCELLING = enum.auto() - DONE = enum.auto() - ERROR = enum.auto() - - -@attr.s(auto_attribs=True) -class InstallStatus: - state: InstallState - confirming_tty: str = '' - error: Optional[ErrorReportRef] = None diff --git a/subiquity/server/controllers/cmdlist.py b/subiquity/server/controllers/cmdlist.py index 8c3b5b32..d14377dd 100644 --- a/subiquity/server/controllers/cmdlist.py +++ b/subiquity/server/controllers/cmdlist.py @@ -21,7 +21,7 @@ from systemd import journal from subiquitycore.context import with_context from subiquitycore.utils import arun_command -from subiquity.common.types import InstallState +from subiquity.common.types import ApplicationState from subiquity.server.controller import NonInteractiveController @@ -102,7 +102,7 @@ class LateController(CmdListController): async def _run(self): Install = self.app.controllers.Install await Install.install_task - if Install.install_state == InstallState.DONE: + if self.app.state == ApplicationState.DONE: await self.run() @@ -113,7 +113,7 @@ class ErrorController(CmdListController): @with_context() async def run(self, context): - if self.app.interactive(): + if self.app.interactive: self.syslog_id = self.app.log_syslog_id else: self.syslog_id = self.app.echo_syslog_id diff --git a/subiquity/server/controllers/install.py b/subiquity/server/controllers/install.py index 66e59130..587912e8 100644 --- a/subiquity/server/controllers/install.py +++ b/subiquity/server/controllers/install.py @@ -21,7 +21,6 @@ import re import shutil import sys import tempfile -from typing import Optional from curtin.commands.install import ( ERROR_TARFILE, @@ -40,14 +39,12 @@ from subiquitycore.utils import ( astart_command, ) -from subiquity.common.apidef import API from subiquity.common.errorreport import ErrorReportKind from subiquity.server.controller import ( SubiquityController, ) from subiquity.common.types import ( - InstallState, - InstallStatus, + ApplicationState, ) from subiquity.journald import journald_listen @@ -75,14 +72,9 @@ class TracebackExtractor: class InstallController(SubiquityController): - endpoint = API.install - def __init__(self, app): super().__init__(app) self.model = app.base_model - self._install_state = InstallState.NOT_STARTED - self._install_state_event = asyncio.Event() - self.error_ref = None self.unattended_upgrades_proc = None self.unattended_upgrades_ctx = None @@ -90,48 +82,27 @@ class InstallController(SubiquityController): self.tb_extractor = TracebackExtractor() self.curtin_event_contexts = {} - def interactive(self): - return True - - async def status_GET( - self, cur: Optional[InstallState] = None) -> InstallStatus: - if cur == self.install_state: - await self._install_state_event.wait() - return InstallStatus( - self.install_state, - self.app.confirming_tty, - self.error_ref) - def stop_uu(self): - if self.install_state == InstallState.UU_RUNNING: - self.update_state(InstallState.UU_CANCELLING) + if self.app.state == ApplicationState.UU_RUNNING: + self.app.update_state(ApplicationState.UU_CANCELLING) self.app.aio_loop.create_task(self.stop_unattended_upgrades()) def start(self): self.install_task = self.app.aio_loop.create_task(self.install()) - @property - def install_state(self): - return self._install_state - - def update_state(self, state): - self._install_state_event.set() - self._install_state_event.clear() - self._install_state = state - def tpath(self, *path): return os.path.join(self.model.target, *path) def curtin_error(self): - self.update_state(InstallState.ERROR) kw = {} if sys.exc_info()[0] is not None: log.exception("curtin_error") # send traceback.format_exc() to journal? if self.tb_extractor.traceback: kw["Traceback"] = "\n".join(self.tb_extractor.traceback) - self.error_ref = self.app.make_apport_report( - ErrorReportKind.INSTALL_FAIL, "install failed", **kw).ref() + self.app.fatal_error = self.app.make_apport_report( + ErrorReportKind.INSTALL_FAIL, "install failed", **kw) + self.app.update_state(ApplicationState.ERROR) def logged_command(self, cmd): return ['systemd-cat', '--level-prefix=false', @@ -254,15 +225,15 @@ class InstallController(SubiquityController): try: await asyncio.wait({e.wait() for e in self.model.install_events}) - if not self.app.interactive(): + if not self.app.interactive: if 'autoinstall' in self.app.kernel_cmdline: self.model.confirm() - self.update_state(InstallState.NEEDS_CONFIRMATION) + self.app.update_state(ApplicationState.NEEDS_CONFIRMATION) await self.model.confirmation.wait() - self.update_state(InstallState.RUNNING) + self.app.update_state(ApplicationState.RUNNING) if os.path.exists(self.model.target): await self.unmount_target( @@ -270,22 +241,22 @@ class InstallController(SubiquityController): await self.curtin_install(context=context) - self.update_state(InstallState.POST_WAIT) + self.app.update_state(ApplicationState.POST_WAIT) await asyncio.wait( {e.wait() for e in self.model.postinstall_events}) await self.drain_curtin_events(context=context) - self.update_state(InstallState.POST_RUNNING) + self.app.update_state(ApplicationState.POST_RUNNING) await self.postinstall(context=context) if self.model.network.has_network: - self.update_state(InstallState.UU_RUNNING) + self.app.update_state(ApplicationState.UU_RUNNING) await self.run_unattended_upgrades(context=context) - self.update_state(InstallState.DONE) + self.app.update_state(ApplicationState.DONE) except Exception: self.curtin_error() diff --git a/subiquity/server/controllers/locale.py b/subiquity/server/controllers/locale.py index c73f71bf..f2d2e808 100644 --- a/subiquity/server/controllers/locale.py +++ b/subiquity/server/controllers/locale.py @@ -32,7 +32,7 @@ class LocaleController(SubiquityController): autoinstall_default = 'en_US.UTF-8' def interactive(self): - return self.app.interactive() + return self.app.interactive def load_autoinstall_data(self, data): os.environ["LANG"] = data diff --git a/subiquity/server/controllers/reboot.py b/subiquity/server/controllers/reboot.py index 7fc7b089..12f87d9d 100644 --- a/subiquity/server/controllers/reboot.py +++ b/subiquity/server/controllers/reboot.py @@ -24,7 +24,7 @@ from subiquitycore.utils import arun_command, run_command from subiquity.common.apidef import API from subiquity.server.controller import SubiquityController -from subiquity.server.controllers.install import InstallState +from subiquity.server.controllers.install import ApplicationState log = logging.getLogger("subiquity.controllers.restart") @@ -51,10 +51,10 @@ class RebootController(SubiquityController): await Install.install_task await self.app.controllers.Late.run_event.wait() await self.copy_logs_to_target() - if self.app.interactive(): + if self.app.interactive: await self.user_reboot_event.wait() self.reboot() - elif Install.install_state == InstallState.DONE: + elif self.app.state == ApplicationState.DONE: self.reboot() @with_context() diff --git a/subiquity/server/controllers/reporting.py b/subiquity/server/controllers/reporting.py index 6bb2ac6f..652b759d 100644 --- a/subiquity/server/controllers/reporting.py +++ b/subiquity/server/controllers/reporting.py @@ -69,7 +69,7 @@ class ReportingController(NonInteractiveController): app.add_event_listener(self) def load_autoinstall_data(self, data): - if self.app.interactive(): + if self.app.interactive: return self.config.update(copy.deepcopy(NON_INTERACTIVE_CONFIG)) if data is not None: diff --git a/subiquity/server/server.py b/subiquity/server/server.py index a1e7008b..31dae53e 100644 --- a/subiquity/server/server.py +++ b/subiquity/server/server.py @@ -47,7 +47,6 @@ from subiquity.common.types import ( ApplicationState, ApplicationStatus, ErrorReportRef, - InstallState, ) from subiquity.server.controller import SubiquityController from subiquity.models.subiquity import SubiquityModel @@ -73,8 +72,11 @@ class MetaController: if cur == self.app.state: await self.app.state_event.wait() return ApplicationStatus( - self.app.state, + state=self.app.state, + confirming_tty=self.app.confirming_tty, + error=self.app.fatal_error, cloud_init_ok=self.app.cloud_init_ok, + interactive=self.app.interactive, echo_syslog_id=self.app.echo_syslog_id, event_syslog_id=self.app.event_syslog_id, log_syslog_id=self.app.log_syslog_id) @@ -145,9 +147,11 @@ class SubiquityServer(Application): super().__init__(opts) self.block_log_dir = block_log_dir self.cloud_init_ok = cloud_init_ok - self._state = ApplicationState.STARTING + self._state = ApplicationState.STARTING_UP self.state_event = asyncio.Event() + self.interactive = None self.confirming_tty = '' + self.fatal_error = None self.echo_syslog_id = 'subiquity_echo.{}'.format(os.getpid()) self.event_syslog_id = 'subiquity_event.{}'.format(os.getpid()) @@ -184,14 +188,14 @@ class SubiquityServer(Application): self.event_listeners.append(listener) def _maybe_push_to_journal(self, event_type, context, description): - if not context.get('is-install-context') and self.interactive(): + if not context.get('is-install-context') and self.interactive: controller = context.get('controller') if controller is None or controller.interactive(): return if context.get('request'): return indent = context.full_name().count('/') - 2 - if context.get('is-install-context') and self.interactive(): + if context.get('is-install-context') and self.interactive: indent -= 1 msg = context.description else: @@ -241,20 +245,14 @@ class SubiquityServer(Application): return self.error_reporter.make_apport_report( kind, thing, wait=wait, **kw) - def interactive(self): - if not self.autoinstall_config: - return True - return bool(self.autoinstall_config.get('interactive-sections')) - @web.middleware async def middleware(self, request, handler): override_status = None controller = await controller_for_request(request) if isinstance(controller, SubiquityController): - install_state = self.controllers.Install.install_state if not controller.interactive(): override_status = 'skip' - elif install_state == InstallState.NEEDS_CONFIRMATION: + elif self.state == ApplicationState.NEEDS_CONFIRMATION: if self.base_model.needs_configuration(controller.model_name): override_status = 'confirm' if override_status is not None: @@ -326,18 +324,19 @@ class SubiquityServer(Application): if self.autoinstall_config and self.controllers.Early.cmds: stamp_file = self.state_path("early-commands") if not os.path.exists(stamp_file): - self.update_state(ApplicationState.EARLY_COMMANDS) await self.controllers.Early.run() open(stamp_file, 'w').close() + await asyncio.sleep(1) self.load_autoinstall_config(only_early=False) - if not self.interactive() and not self.opts.dry_run: + if self.autoinstall_config: + self.interactive = bool( + self.autoinstall_config.get('interactive-sections')) + else: + self.interactive = True + if not self.interactive and not self.opts.dry_run: open('/run/casper-no-prompt', 'w').close() self.load_serialized_state() - if self.interactive(): - self.update_state(ApplicationState.INTERACTIVE) - else: - self.update_state(ApplicationState.NON_INTERACTIVE) - await asyncio.sleep(1) + self.update_state(ApplicationState.WAITING) await super().start() await self.apply_autoinstall_config() diff --git a/subiquity/ui/views/installprogress.py b/subiquity/ui/views/installprogress.py index b282b1c4..cea923b5 100644 --- a/subiquity/ui/views/installprogress.py +++ b/subiquity/ui/views/installprogress.py @@ -33,7 +33,7 @@ from subiquitycore.ui.utils import button_pile, Padding, rewrap from subiquitycore.ui.stretchy import Stretchy from subiquitycore.ui.width import widget_width -from subiquity.common.types import InstallState +from subiquity.common.types import ApplicationState log = logging.getLogger("subiquity.views.installprogress") @@ -138,22 +138,22 @@ class ProgressView(BaseView): self._set_button_width() def update_for_state(self, state): - if state == InstallState.NOT_STARTED: + if state == ApplicationState.WAITING: self.title = _("Installing system") btns = [] - elif state == InstallState.NEEDS_CONFIRMATION: + elif state == ApplicationState.NEEDS_CONFIRMATION: self.title = _("Installing system") btns = [] - elif state == InstallState.RUNNING: + elif state == ApplicationState.RUNNING: self.title = _("Installing system") btns = [self.view_log_btn] - elif state == InstallState.POST_WAIT: + elif state == ApplicationState.POST_WAIT: self.title = _("Installing system") btns = [self.view_log_btn] - elif state == InstallState.POST_RUNNING: + elif state == ApplicationState.POST_RUNNING: self.title = _("Installing system") btns = [self.view_log_btn] - elif state == InstallState.UU_RUNNING: + elif state == ApplicationState.UU_RUNNING: self.title = _("Install complete!") self.reboot_btn.base_widget.set_label( _("Cancel update and reboot")) @@ -161,7 +161,7 @@ class ProgressView(BaseView): self.view_log_btn, self.reboot_btn, ] - elif state == InstallState.UU_CANCELLING: + elif state == ApplicationState.UU_CANCELLING: self.title = _("Install complete!") self.reboot_btn.base_widget.set_label(_("Rebooting...")) self.reboot_btn.enabled = False @@ -169,14 +169,14 @@ class ProgressView(BaseView): self.view_log_btn, self.reboot_btn, ] - elif state == InstallState.DONE: + elif state == ApplicationState.DONE: self.title = _("Install complete!") self.reboot_btn.base_widget.set_label(_("Reboot Now")) btns = [ self.view_log_btn, self.reboot_btn, ] - elif state == InstallState.ERROR: + elif state == ApplicationState.ERROR: self.title = _('An error occurred during installation') self.reboot_btn.base_widget.set_label(_("Reboot Now")) self.reboot_btn.enabled = True diff --git a/subiquity/ui/views/tests/test_installprogress.py b/subiquity/ui/views/tests/test_installprogress.py index 411b9d7a..ad06c5db 100644 --- a/subiquity/ui/views/tests/test_installprogress.py +++ b/subiquity/ui/views/tests/test_installprogress.py @@ -4,7 +4,7 @@ from unittest import mock from subiquitycore.testing import view_helpers from subiquity.client.controllers.progress import ProgressController -from subiquity.common.types import InstallState +from subiquity.common.types import ApplicationState from subiquity.ui.views.installprogress import ProgressView @@ -28,7 +28,7 @@ class IdentityViewTests(unittest.TestCase): view = self.make_view() btn = view_helpers.find_button_matching(view, "^Reboot Now$") self.assertIs(btn, None) - view.update_for_state(InstallState.DONE) + view.update_for_state(ApplicationState.DONE) btn = view_helpers.find_button_matching(view, "^Reboot Now$") self.assertIsNot(btn, None) view_helpers.click(btn)