move snaplist controller to new world

This commit is contained in:
Michael Hudson-Doyle 2020-10-12 13:31:48 +13:00
parent 4ab676d984
commit 4367f8f97c
10 changed files with 117 additions and 50 deletions

View File

@ -10,6 +10,7 @@ subiquity/client/controllers/network.py
subiquity/client/controllers/progress.py subiquity/client/controllers/progress.py
subiquity/client/controllers/proxy.py subiquity/client/controllers/proxy.py
subiquity/client/controllers/refresh.py subiquity/client/controllers/refresh.py
subiquity/client/controllers/snaplist.py
subiquity/client/controllers/ssh.py subiquity/client/controllers/ssh.py
subiquity/client/controllers/welcome.py subiquity/client/controllers/welcome.py
subiquity/client/controllers/zdev.py subiquity/client/controllers/zdev.py
@ -42,7 +43,6 @@ subiquity/common/types.py
subiquity/controller.py subiquity/controller.py
subiquity/controllers/__init__.py subiquity/controllers/__init__.py
subiquity/controllers/reboot.py subiquity/controllers/reboot.py
subiquity/controllers/snaplist.py
subiquitycore/async_helpers.py subiquitycore/async_helpers.py
subiquitycore/contextlib38.py subiquitycore/contextlib38.py
subiquitycore/context.py subiquitycore/context.py
@ -131,6 +131,7 @@ subiquity/server/controllers/package.py
subiquity/server/controllers/proxy.py subiquity/server/controllers/proxy.py
subiquity/server/controllers/refresh.py subiquity/server/controllers/refresh.py
subiquity/server/controllers/reporting.py subiquity/server/controllers/reporting.py
subiquity/server/controllers/snaplist.py
subiquity/server/controllers/ssh.py subiquity/server/controllers/ssh.py
subiquity/server/controllers/userdata.py subiquity/server/controllers/userdata.py
subiquity/server/controllers/zdev.py subiquity/server/controllers/zdev.py

View File

@ -101,6 +101,7 @@ class SubiquityClient(TuiApplication):
"Filesystem", "Filesystem",
"Identity", "Identity",
"SSH", "SSH",
"SnapList",
"Progress", "Progress",
] ]

View File

@ -22,6 +22,7 @@ from .network import NetworkController
from .progress import ProgressController from .progress import ProgressController
from .proxy import ProxyController from .proxy import ProxyController
from .refresh import RefreshController from .refresh import RefreshController
from .snaplist import SnapListController
from .ssh import SSHController from .ssh import SSHController
from .welcome import WelcomeController from .welcome import WelcomeController
from .zdev import ZdevController from .zdev import ZdevController
@ -36,6 +37,7 @@ __all__ = [
'ProxyController', 'ProxyController',
'RefreshController', 'RefreshController',
'RepeatedController', 'RepeatedController',
'SnapListController',
'SSHController', 'SSHController',
'WelcomeController', 'WelcomeController',
'ZdevController', 'ZdevController',

View File

@ -0,0 +1,69 @@
# Copyright 2018 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
from typing import List
from subiquitycore.tuicontroller import (
Skip,
)
from subiquity.client.controller import (
SubiquityTuiController,
)
from subiquity.common.types import (
SnapCheckState,
SnapSelection,
)
from subiquity.ui.views.snaplist import SnapListView
log = logging.getLogger('subiquity.client.controllers.snaplist')
class SnapListController(SubiquityTuiController):
endpoint_name = 'snaplist'
async def make_ui(self):
data = await self.endpoint.GET()
if data.status == SnapCheckState.FAILED:
# If loading snaps failed or the network is disabled, skip the
# screen.
raise Skip()
return SnapListView(self, data)
def run_answers(self):
if 'snaps' in self.answers:
selections = []
for snap_name, selection in self.answers['snaps'].items():
selections.append(SnapSelection(name=snap_name, **selection))
self.done(selections)
def done(self, selections: List[SnapSelection]):
log.debug(
"SnapListController.done next_screen snaps_to_install=%s",
selections)
self.app.next_screen(self.endpoint.POST(selections))
def cancel(self, sender=None):
self.app.prev_screen()
async def get_list_wait(self):
return await self.endpoint.GET(wait=True)
async def get_snap_info(self, snap):
if not snap.channels:
data = await self.endpoint.snap_info.GET(snap_name=snap.name)
snap.channels = data.channels

View File

@ -32,6 +32,9 @@ from subiquity.common.types import (
InstallState, InstallState,
InstallStatus, InstallStatus,
RefreshStatus, RefreshStatus,
SnapInfo,
SnapListResponse,
SnapSelection,
SSHData, SSHData,
StorageResponse, StorageResponse,
ZdevInfo, ZdevInfo,
@ -172,6 +175,13 @@ class API:
class reset: class reset:
def POST() -> StorageResponse: ... def POST() -> StorageResponse: ...
class snaplist:
def GET(wait: bool = False) -> SnapListResponse: ...
def POST(data: Payload[List[SnapSelection]]): ...
class snap_info:
def GET(snap_name: str) -> SnapInfo: ...
class install: class install:
class status: class status:
def GET(cur: Optional[InstallState] = None) -> InstallStatus: ... def GET(cur: Optional[InstallState] = None) -> InstallStatus: ...

View File

@ -14,9 +14,7 @@
# 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 .reboot import RebootController from .reboot import RebootController
from .snaplist import SnapListController
__all__ = [ __all__ = [
'RebootController', 'RebootController',
'SnapListController',
] ]

View File

@ -26,6 +26,7 @@ from .package import PackageController
from .proxy import ProxyController from .proxy import ProxyController
from .refresh import RefreshController from .refresh import RefreshController
from .reporting import ReportingController from .reporting import ReportingController
from .snaplist import SnapListController
from .ssh import SSHController from .ssh import SSHController
from .userdata import UserdataController from .userdata import UserdataController
from .zdev import ZdevController from .zdev import ZdevController
@ -46,6 +47,7 @@ __all__ = [
'ProxyController', 'ProxyController',
'RefreshController', 'RefreshController',
'ReportingController', 'ReportingController',
'SnapListController',
'SSHController', 'SSHController',
'UserdataController', 'UserdataController',
'ZdevController', 'ZdevController',

View File

@ -16,28 +16,28 @@
import logging import logging
from typing import List from typing import List
import attr
import requests.exceptions import requests.exceptions
from subiquitycore.async_helpers import ( from subiquitycore.async_helpers import (
schedule_task, schedule_task,
) )
from subiquitycore.context import with_context from subiquitycore.context import with_context
from subiquitycore.tuicontroller import (
Skip,
)
from subiquity.controller import (
SubiquityTuiController,
)
from subiquity.common.apidef import API
from subiquity.common.types import ( from subiquity.common.types import (
SnapListResponse,
SnapCheckState, SnapCheckState,
SnapInfo,
SnapListResponse,
SnapSelection, SnapSelection,
) )
from subiquity.ui.views.snaplist import SnapListView from subiquity.server.controller import (
SubiquityController,
)
log = logging.getLogger('subiquity.controllers.snaplist')
log = logging.getLogger('subiquity.server.controllers.snaplist')
class SnapdSnapInfoLoader: class SnapdSnapInfoLoader:
@ -52,7 +52,7 @@ class SnapdSnapInfoLoader:
self.failed = False self.failed = False
self.snapd = snapd self.snapd = snapd
self.pending_info_snaps = [] self.pending_snaps = []
self.tasks = {} # {snap:task} self.tasks = {} # {snap:task}
def start(self): def start(self):
@ -109,7 +109,9 @@ class SnapdSnapInfoLoader:
return self.tasks[snap] return self.tasks[snap]
class SnapListController(SubiquityTuiController): class SnapListController(SubiquityController):
endpoint = API.snaplist
autoinstall_key = "snaps" autoinstall_key = "snaps"
autoinstall_default = [] autoinstall_default = []
@ -141,11 +143,12 @@ class SnapListController(SubiquityTuiController):
self.loader = self._make_loader() self.loader = self._make_loader()
def load_autoinstall_data(self, ai_data): def load_autoinstall_data(self, ai_data):
to_install = {} to_install = []
for snap in ai_data: for snap in ai_data:
to_install[snap['name']] = SnapSelection( to_install.append(SnapSelection(
name=snap['name'],
channel=snap.get('channel', 'stable'), channel=snap.get('channel', 'stable'),
is_classic=snap.get('classic', False)) is_classic=snap.get('classic', False)))
self.model.set_installed_list(to_install) self.model.set_installed_list(to_install)
def snapd_network_changed(self): def snapd_network_changed(self):
@ -160,24 +163,12 @@ class SnapListController(SubiquityTuiController):
self.loader = self._make_loader() self.loader = self._make_loader()
self.loader.start() self.loader.start()
async def make_ui(self): def make_autoinstall(self):
data = await self.get_snap_list(wait=False) return [attr.asdict(sel) for sel in self.model.selections]
if data.status == SnapCheckState.FAILED:
# If loading snaps failed or the network is disabled, skip the
# screen.
self.configured()
raise Skip()
return SnapListView(self, data)
def run_answers(self): async def GET(self, wait: bool = False) -> SnapListResponse:
if 'snaps' in self.answers:
selections = []
for snap_name, selection in self.answers['snaps'].items():
selections.append(SnapSelection(name=snap_name, **selection))
self.done(selections)
async def get_snap_list(self, *, wait: bool) -> SnapListResponse:
if self.loader.failed or not self.app.base_model.network.has_network: if self.loader.failed or not self.app.base_model.network.has_network:
self.configured()
return SnapListResponse(status=SnapCheckState.FAILED) return SnapListResponse(status=SnapCheckState.FAILED)
if not self.loader.snap_list_fetched and not wait: if not self.loader.snap_list_fetched and not wait:
return SnapListResponse(status=SnapCheckState.LOADING) return SnapListResponse(status=SnapCheckState.LOADING)
@ -187,19 +178,11 @@ class SnapListController(SubiquityTuiController):
snaps=self.model.get_snap_list(), snaps=self.model.get_snap_list(),
selections=self.model.selections) selections=self.model.selections)
def get_snap_info_task(self, snap): async def POST(self, data: List[SnapSelection]):
return self.loader.get_snap_info_task(snap) self.model.set_installed_list(data)
def done(self, selections: List[SnapSelection]):
log.debug(
"SnapListController.done next_screen snaps_to_install=%s",
selections)
self.model.set_installed_list(selections)
self.configured() self.configured()
self.app.next_screen()
def cancel(self, sender=None): async def snap_info_GET(self, snap_name: str) -> SnapInfo:
self.app.prev_screen() snap = self.model._snap_for_name(snap_name)
await self.loader.get_snap_info_task(snap)
def make_autoinstall(self): return snap
return self.model.selections

View File

@ -128,6 +128,7 @@ class SubiquityServer(Application):
"Filesystem", "Filesystem",
"Identity", "Identity",
"SSH", "SSH",
"SnapList",
"Install", "Install",
"Late", "Late",
] ]

View File

@ -297,7 +297,7 @@ class SnapCheckBox(CheckBox):
app = self.parent.controller.app app = self.parent.controller.app
await app.wait_with_text_dialog( await app.wait_with_text_dialog(
asyncio.shield( asyncio.shield(
self.parent.controller.get_snap_info_task(self.snap)), self.parent.controller.get_snap_info(self.snap)),
_("Fetching info for {snap}").format(snap=self.snap.name), _("Fetching info for {snap}").format(snap=self.snap.name),
can_cancel=True) can_cancel=True)
if len(self.snap.channels) == 0: # or other indication of failure if len(self.snap.channels) == 0: # or other indication of failure
@ -357,7 +357,7 @@ class SnapListView(BaseView):
# If we show the loading screen at all, we want to show it for # If we show the loading screen at all, we want to show it for
# at least a second to avoid flickering at the user. # at least a second to avoid flickering at the user.
min_wait = self.controller.app.aio_loop.create_task(asyncio.sleep(1)) min_wait = self.controller.app.aio_loop.create_task(asyncio.sleep(1))
data = await self.controller.get_snap_list(wait=True) data = await self.controller.get_list_wait()
await min_wait await min_wait
spinner.stop() spinner.stop()
if data.status == SnapCheckState.FAILED: if data.status == SnapCheckState.FAILED: