move refresh controller to new world
This commit is contained in:
parent
7faa1634ff
commit
aac00dad6c
|
@ -3,6 +3,7 @@ subiquity/client/client.py
|
||||||
subiquity/client/controller.py
|
subiquity/client/controller.py
|
||||||
subiquity/client/controllers/__init__.py
|
subiquity/client/controllers/__init__.py
|
||||||
subiquity/client/controllers/progress.py
|
subiquity/client/controllers/progress.py
|
||||||
|
subiquity/client/controllers/refresh.py
|
||||||
subiquity/client/controllers/welcome.py
|
subiquity/client/controllers/welcome.py
|
||||||
subiquity/client/__init__.py
|
subiquity/client/__init__.py
|
||||||
subiquity/client/keycodes.py
|
subiquity/client/keycodes.py
|
||||||
|
@ -35,7 +36,6 @@ subiquity/controllers/mirror.py
|
||||||
subiquity/controllers/network.py
|
subiquity/controllers/network.py
|
||||||
subiquity/controllers/proxy.py
|
subiquity/controllers/proxy.py
|
||||||
subiquity/controllers/reboot.py
|
subiquity/controllers/reboot.py
|
||||||
subiquity/controllers/refresh.py
|
|
||||||
subiquity/controllers/snaplist.py
|
subiquity/controllers/snaplist.py
|
||||||
subiquity/controllers/ssh.py
|
subiquity/controllers/ssh.py
|
||||||
subiquity/controllers/tests/__init__.py
|
subiquity/controllers/tests/__init__.py
|
||||||
|
@ -122,6 +122,7 @@ subiquity/server/controllers/__init__.py
|
||||||
subiquity/server/controllers/install.py
|
subiquity/server/controllers/install.py
|
||||||
subiquity/server/controllers/locale.py
|
subiquity/server/controllers/locale.py
|
||||||
subiquity/server/controllers/package.py
|
subiquity/server/controllers/package.py
|
||||||
|
subiquity/server/controllers/refresh.py
|
||||||
subiquity/server/controllers/reporting.py
|
subiquity/server/controllers/reporting.py
|
||||||
subiquity/server/controllers/userdata.py
|
subiquity/server/controllers/userdata.py
|
||||||
subiquity/server/dryrun.py
|
subiquity/server/dryrun.py
|
||||||
|
|
|
@ -91,6 +91,7 @@ class SubiquityClient(TuiApplication):
|
||||||
|
|
||||||
controllers = [
|
controllers = [
|
||||||
"Welcome",
|
"Welcome",
|
||||||
|
"Refresh",
|
||||||
"Progress",
|
"Progress",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,11 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from .progress import ProgressController
|
from .progress import ProgressController
|
||||||
|
from .refresh import RefreshController
|
||||||
from .welcome import WelcomeController
|
from .welcome import WelcomeController
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ProgressController',
|
'ProgressController',
|
||||||
|
'RefreshController',
|
||||||
'WelcomeController',
|
'WelcomeController',
|
||||||
]
|
]
|
||||||
|
|
|
@ -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()
|
|
@ -22,6 +22,7 @@ from subiquity.common.types import (
|
||||||
ErrorReportRef,
|
ErrorReportRef,
|
||||||
InstallState,
|
InstallState,
|
||||||
InstallStatus,
|
InstallStatus,
|
||||||
|
RefreshStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,6 +60,18 @@ class API:
|
||||||
def GET() -> None:
|
def GET() -> None:
|
||||||
"""Requests to this method will fail with a HTTP 500."""
|
"""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 install:
|
||||||
class status:
|
class status:
|
||||||
def GET(cur: Optional[InstallState] = None) -> InstallStatus: ...
|
def GET(cur: Optional[InstallState] = None) -> InstallStatus: ...
|
||||||
|
|
|
@ -21,7 +21,6 @@ from .mirror import MirrorController
|
||||||
from .network import NetworkController
|
from .network import NetworkController
|
||||||
from .proxy import ProxyController
|
from .proxy import ProxyController
|
||||||
from .reboot import RebootController
|
from .reboot import RebootController
|
||||||
from .refresh import RefreshController
|
|
||||||
from .snaplist import SnapListController
|
from .snaplist import SnapListController
|
||||||
from .ssh import SSHController
|
from .ssh import SSHController
|
||||||
from .zdev import ZdevController
|
from .zdev import ZdevController
|
||||||
|
@ -34,7 +33,6 @@ __all__ = [
|
||||||
'NetworkController',
|
'NetworkController',
|
||||||
'ProxyController',
|
'ProxyController',
|
||||||
'RebootController',
|
'RebootController',
|
||||||
'RefreshController',
|
|
||||||
'RepeatedController',
|
'RepeatedController',
|
||||||
'SnapListController',
|
'SnapListController',
|
||||||
'SSHController',
|
'SSHController',
|
||||||
|
|
|
@ -18,6 +18,7 @@ from .debconf import DebconfController
|
||||||
from .install import InstallController
|
from .install import InstallController
|
||||||
from .locale import LocaleController
|
from .locale import LocaleController
|
||||||
from .package import PackageController
|
from .package import PackageController
|
||||||
|
from .refresh import RefreshController
|
||||||
from .reporting import ReportingController
|
from .reporting import ReportingController
|
||||||
from .userdata import UserdataController
|
from .userdata import UserdataController
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ __all__ = [
|
||||||
'LateController',
|
'LateController',
|
||||||
'LocaleController',
|
'LocaleController',
|
||||||
'PackageController',
|
'PackageController',
|
||||||
|
'RefreshController',
|
||||||
'ReportingController',
|
'ReportingController',
|
||||||
'UserdataController',
|
'UserdataController',
|
||||||
]
|
]
|
||||||
|
|
|
@ -24,24 +24,23 @@ from subiquitycore.async_helpers import (
|
||||||
SingleInstanceTask,
|
SingleInstanceTask,
|
||||||
)
|
)
|
||||||
from subiquitycore.context import with_context
|
from subiquitycore.context import with_context
|
||||||
from subiquitycore.tuicontroller import (
|
|
||||||
Skip,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
from subiquity.common.apidef import API
|
||||||
from subiquity.common.types import (
|
from subiquity.common.types import (
|
||||||
RefreshCheckState,
|
RefreshCheckState,
|
||||||
RefreshStatus,
|
RefreshStatus,
|
||||||
)
|
)
|
||||||
from subiquity.controller import (
|
from subiquity.server.controller import (
|
||||||
SubiquityTuiController,
|
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_key = "refresh-installer"
|
||||||
autoinstall_schema = {
|
autoinstall_schema = {
|
||||||
|
@ -58,23 +57,24 @@ class RefreshController(SubiquityTuiController):
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.ai_data = {}
|
|
||||||
super().__init__(app)
|
super().__init__(app)
|
||||||
|
self.ai_data = {}
|
||||||
self.snap_name = os.environ.get("SNAP_NAME", "subiquity")
|
self.snap_name = os.environ.get("SNAP_NAME", "subiquity")
|
||||||
self.configure_task = None
|
self.configure_task = None
|
||||||
self.check_task = None
|
self.check_task = None
|
||||||
self.status = RefreshStatus(availability=RefreshCheckState.UNKNOWN)
|
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):
|
def load_autoinstall_data(self, data):
|
||||||
if data is not None:
|
if data is not None:
|
||||||
self.ai_data = data
|
self.ai_data = data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active(self):
|
||||||
|
if 'update' in self.ai_data:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return self.interactive()
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
if not self.active:
|
if not self.active:
|
||||||
return
|
return
|
||||||
|
@ -95,16 +95,9 @@ class RefreshController(SubiquityTuiController):
|
||||||
return
|
return
|
||||||
change_id = await self.start_update(context=context)
|
change_id = await self.start_update(context=context)
|
||||||
while True:
|
while True:
|
||||||
try:
|
|
||||||
change = await self.get_progress(change_id)
|
change = await self.get_progress(change_id)
|
||||||
except requests.exceptions.RequestException as e:
|
if change['status'] not in ['Do', 'Doing', 'Done']:
|
||||||
raise e
|
raise Exception("update failed: %s", change['status'])
|
||||||
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")
|
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
@with_context()
|
@with_context()
|
||||||
|
@ -137,8 +130,6 @@ class RefreshController(SubiquityTuiController):
|
||||||
|
|
||||||
def get_refresh_channel(self):
|
def get_refresh_channel(self):
|
||||||
"""Return the channel we should refresh subiquity to."""
|
"""Return the channel we should refresh subiquity to."""
|
||||||
if 'channel' in self.answers:
|
|
||||||
return self.answers['channel']
|
|
||||||
prefix = "subiquity-channel="
|
prefix = "subiquity-channel="
|
||||||
for arg in self.app.kernel_cmdline:
|
for arg in self.app.kernel_cmdline:
|
||||||
if arg.startswith(prefix):
|
if arg.startswith(prefix):
|
||||||
|
@ -209,39 +200,20 @@ class RefreshController(SubiquityTuiController):
|
||||||
|
|
||||||
async def get_progress(self, change):
|
async def get_progress(self, change):
|
||||||
result = await self.app.snapd.get('v2/changes/{}'.format(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):
|
async def GET(self, wait: bool = False) -> RefreshStatus:
|
||||||
if self.app.updated:
|
if wait:
|
||||||
raise Skip()
|
await self.check_task.wait()
|
||||||
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
|
|
||||||
return self.status
|
return self.status
|
||||||
|
|
||||||
def run_answers(self):
|
async def POST(self, context) -> int:
|
||||||
# Handled in the view
|
return await self.start_update(context=context)
|
||||||
pass
|
|
||||||
|
|
||||||
def done(self, sender=None):
|
async def progress_GET(self, change_id: int) -> dict:
|
||||||
log.debug("RefreshController.done next_screen")
|
return await self.get_progress(change_id)
|
||||||
self.app.next_screen()
|
|
||||||
|
|
||||||
def cancel(self, sender=None):
|
|
||||||
self.app.prev_screen()
|
|
|
@ -119,6 +119,7 @@ class SubiquityServer(Application):
|
||||||
"Package",
|
"Package",
|
||||||
"Debconf",
|
"Debconf",
|
||||||
"Locale",
|
"Locale",
|
||||||
|
"Refresh",
|
||||||
"Install",
|
"Install",
|
||||||
"Late",
|
"Late",
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,7 +17,7 @@ import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests
|
import aiohttp
|
||||||
|
|
||||||
from urwid import (
|
from urwid import (
|
||||||
ProgressBar,
|
ProgressBar,
|
||||||
|
@ -246,15 +246,11 @@ class RefreshView(BaseView):
|
||||||
async def _update(self):
|
async def _update(self):
|
||||||
try:
|
try:
|
||||||
change_id = await self.controller.start_update()
|
change_id = await self.controller.start_update()
|
||||||
except requests.exceptions.RequestException as e:
|
except aiohttp.ClientError as e:
|
||||||
self.update_failed(exc_message(e))
|
self.update_failed(exc_message(e))
|
||||||
return
|
return
|
||||||
while True:
|
while True:
|
||||||
try:
|
|
||||||
change = await self.controller.get_progress(change_id)
|
change = await self.controller.get_progress(change_id)
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
self.update_failed(exc_message(e))
|
|
||||||
return
|
|
||||||
if change['status'] == 'Done':
|
if change['status'] == 'Done':
|
||||||
# Clearly if we got here we didn't get restarted by
|
# Clearly if we got here we didn't get restarted by
|
||||||
# snapd/systemctl (dry-run mode or logged in via SSH)
|
# snapd/systemctl (dry-run mode or logged in via SSH)
|
||||||
|
|
Loading…
Reference in New Issue