move refresh controller to new world

This commit is contained in:
Michael Hudson-Doyle 2020-10-09 16:09:48 +13:00
parent 7faa1634ff
commit aac00dad6c
10 changed files with 145 additions and 69 deletions

View File

@ -3,6 +3,7 @@ subiquity/client/client.py
subiquity/client/controller.py
subiquity/client/controllers/__init__.py
subiquity/client/controllers/progress.py
subiquity/client/controllers/refresh.py
subiquity/client/controllers/welcome.py
subiquity/client/__init__.py
subiquity/client/keycodes.py
@ -35,7 +36,6 @@ subiquity/controllers/mirror.py
subiquity/controllers/network.py
subiquity/controllers/proxy.py
subiquity/controllers/reboot.py
subiquity/controllers/refresh.py
subiquity/controllers/snaplist.py
subiquity/controllers/ssh.py
subiquity/controllers/tests/__init__.py
@ -122,6 +122,7 @@ subiquity/server/controllers/__init__.py
subiquity/server/controllers/install.py
subiquity/server/controllers/locale.py
subiquity/server/controllers/package.py
subiquity/server/controllers/refresh.py
subiquity/server/controllers/reporting.py
subiquity/server/controllers/userdata.py
subiquity/server/dryrun.py

View File

@ -91,6 +91,7 @@ class SubiquityClient(TuiApplication):
controllers = [
"Welcome",
"Refresh",
"Progress",
]

View File

@ -14,9 +14,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .progress import ProgressController
from .refresh import RefreshController
from .welcome import WelcomeController
__all__ = [
'ProgressController',
'RefreshController',
'WelcomeController',
]

View File

@ -0,0 +1,90 @@
# Copyright 2019 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 asyncio
import logging
import aiohttp
from subiquitycore.tuicontroller import (
Skip,
)
from subiquity.common.types import (
RefreshCheckState,
)
from subiquity.client.controller import (
SubiquityTuiController,
)
from subiquity.ui.views.refresh import RefreshView
log = logging.getLogger('subiquity.client.controllers.refresh')
class RefreshController(SubiquityTuiController):
endpoint_name = 'refresh'
def __init__(self, app):
super().__init__(app)
self.offered_first_time = False
async def get_progress(self, change):
while True:
try:
return await self.endpoint.progress.GET(change_id=change)
except aiohttp.ClientError:
# Probably the server is restarting.
await asyncio.sleep(1)
async def make_ui(self, index=1):
if self.app.updated:
raise Skip()
show = False
self.status = await self.endpoint.GET()
if index == 1:
if self.status.availability == RefreshCheckState.AVAILABLE:
show = True
self.offered_first_time = True
elif index == 2:
if not self.offered_first_time:
if self.status.availability in [RefreshCheckState.UNKNOWN,
RefreshCheckState.AVAILABLE]:
show = True
else:
raise AssertionError("unexpected index {}".format(index))
if show:
return RefreshView(self)
else:
raise Skip()
async def wait_for_check(self):
self.status = await self.endpoint.GET(wait=True)
return self.status
async def start_update(self):
return await self.endpoint.POST()
def run_answers(self):
# Handled in the view
pass
def done(self, sender=None):
log.debug("RefreshController.done next_screen")
self.app.next_screen()
def cancel(self, sender=None):
self.app.prev_screen()

View File

@ -22,6 +22,7 @@ from subiquity.common.types import (
ErrorReportRef,
InstallState,
InstallStatus,
RefreshStatus,
)
@ -59,6 +60,18 @@ class API:
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.
If wait is true, block until the status is known."""
def POST() -> int:
"""Start the update and return the change id."""
class progress:
def GET(change_id: int) -> dict: ...
class install:
class status:
def GET(cur: Optional[InstallState] = None) -> InstallStatus: ...

View File

@ -21,7 +21,6 @@ from .mirror import MirrorController
from .network import NetworkController
from .proxy import ProxyController
from .reboot import RebootController
from .refresh import RefreshController
from .snaplist import SnapListController
from .ssh import SSHController
from .zdev import ZdevController
@ -34,7 +33,6 @@ __all__ = [
'NetworkController',
'ProxyController',
'RebootController',
'RefreshController',
'RepeatedController',
'SnapListController',
'SSHController',

View File

@ -18,6 +18,7 @@ from .debconf import DebconfController
from .install import InstallController
from .locale import LocaleController
from .package import PackageController
from .refresh import RefreshController
from .reporting import ReportingController
from .userdata import UserdataController
@ -29,6 +30,7 @@ __all__ = [
'LateController',
'LocaleController',
'PackageController',
'RefreshController',
'ReportingController',
'UserdataController',
]

View File

@ -24,24 +24,23 @@ from subiquitycore.async_helpers import (
SingleInstanceTask,
)
from subiquitycore.context import with_context
from subiquitycore.tuicontroller import (
Skip,
)
from subiquity.common.apidef import API
from subiquity.common.types import (
RefreshCheckState,
RefreshStatus,
)
from subiquity.controller import (
SubiquityTuiController,
from subiquity.server.controller import (
SubiquityController,
)
from subiquity.ui.views.refresh import RefreshView
log = logging.getLogger('subiquity.controllers.refresh')
log = logging.getLogger('subiquity.server.controllers.refresh')
class RefreshController(SubiquityTuiController):
class RefreshController(SubiquityController):
endpoint = API.refresh
autoinstall_key = "refresh-installer"
autoinstall_schema = {
@ -58,23 +57,24 @@ class RefreshController(SubiquityTuiController):
]
def __init__(self, app):
self.ai_data = {}
super().__init__(app)
self.ai_data = {}
self.snap_name = os.environ.get("SNAP_NAME", "subiquity")
self.configure_task = None
self.check_task = None
self.status = RefreshStatus(availability=RefreshCheckState.UNKNOWN)
self.offered_first_time = False
if 'update' in self.ai_data:
self.active = self.ai_data['update']
else:
self.active = self.interactive()
def load_autoinstall_data(self, data):
if data is not None:
self.ai_data = data
@property
def active(self):
if 'update' in self.ai_data:
return True
else:
return self.interactive()
def start(self):
if not self.active:
return
@ -95,16 +95,9 @@ class RefreshController(SubiquityTuiController):
return
change_id = await self.start_update(context=context)
while True:
try:
change = await self.get_progress(change_id)
except requests.exceptions.RequestException as e:
raise e
if change['status'] == 'Done':
# Clearly if we got here we didn't get restarted by
# snapd/systemctl (dry-run mode or logged in via SSH)
self.app.restart(remove_last_screen=False)
if change['status'] not in ['Do', 'Doing']:
raise Exception("update failed")
change = await self.get_progress(change_id)
if change['status'] not in ['Do', 'Doing', 'Done']:
raise Exception("update failed: %s", change['status'])
await asyncio.sleep(0.1)
@with_context()
@ -137,8 +130,6 @@ class RefreshController(SubiquityTuiController):
def get_refresh_channel(self):
"""Return the channel we should refresh subiquity to."""
if 'channel' in self.answers:
return self.answers['channel']
prefix = "subiquity-channel="
for arg in self.app.kernel_cmdline:
if arg.startswith(prefix):
@ -209,39 +200,20 @@ class RefreshController(SubiquityTuiController):
async def get_progress(self, change):
result = await self.app.snapd.get('v2/changes/{}'.format(change))
return result['result']
change = result['result']
if change['status'] == 'Done':
# Clearly if we got here we didn't get restarted by
# snapd/systemctl (dry-run mode)
self.app.restart()
return change
def make_ui(self, index=1):
if self.app.updated:
raise Skip()
show = False
if index == 1:
if self.status.availability == RefreshCheckState.AVAILABLE:
show = True
self.offered_first_time = True
elif index == 2:
if not self.offered_first_time:
if self.status.availability in [RefreshCheckState.UNKNOWN,
RefreshCheckState.AVAILABLE]:
show = True
else:
raise AssertionError("unexpected index {}".format(index))
if show:
return RefreshView(self)
else:
raise Skip()
async def wait_for_check(self):
await self.check_task.task
async def GET(self, wait: bool = False) -> RefreshStatus:
if wait:
await self.check_task.wait()
return self.status
def run_answers(self):
# Handled in the view
pass
async def POST(self, context) -> int:
return await self.start_update(context=context)
def done(self, sender=None):
log.debug("RefreshController.done next_screen")
self.app.next_screen()
def cancel(self, sender=None):
self.app.prev_screen()
async def progress_GET(self, change_id: int) -> dict:
return await self.get_progress(change_id)

View File

@ -119,6 +119,7 @@ class SubiquityServer(Application):
"Package",
"Debconf",
"Locale",
"Refresh",
"Install",
"Late",
]

View File

@ -17,7 +17,7 @@ import asyncio
import json
import logging
import requests
import aiohttp
from urwid import (
ProgressBar,
@ -246,15 +246,11 @@ class RefreshView(BaseView):
async def _update(self):
try:
change_id = await self.controller.start_update()
except requests.exceptions.RequestException as e:
except aiohttp.ClientError as e:
self.update_failed(exc_message(e))
return
while True:
try:
change = await self.controller.get_progress(change_id)
except requests.exceptions.RequestException as e:
self.update_failed(exc_message(e))
return
change = await self.controller.get_progress(change_id)
if change['status'] == 'Done':
# Clearly if we got here we didn't get restarted by
# snapd/systemctl (dry-run mode or logged in via SSH)