diff --git a/po/POTFILES.in b/po/POTFILES.in
index ff5af63f..7601b8d7 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -10,6 +10,7 @@ subiquity/client/controllers/network.py
subiquity/client/controllers/progress.py
subiquity/client/controllers/proxy.py
subiquity/client/controllers/refresh.py
+subiquity/client/controllers/snaplist.py
subiquity/client/controllers/ssh.py
subiquity/client/controllers/welcome.py
subiquity/client/controllers/zdev.py
@@ -42,7 +43,6 @@ subiquity/common/types.py
subiquity/controller.py
subiquity/controllers/__init__.py
subiquity/controllers/reboot.py
-subiquity/controllers/snaplist.py
subiquitycore/async_helpers.py
subiquitycore/contextlib38.py
subiquitycore/context.py
@@ -131,6 +131,7 @@ subiquity/server/controllers/package.py
subiquity/server/controllers/proxy.py
subiquity/server/controllers/refresh.py
subiquity/server/controllers/reporting.py
+subiquity/server/controllers/snaplist.py
subiquity/server/controllers/ssh.py
subiquity/server/controllers/userdata.py
subiquity/server/controllers/zdev.py
diff --git a/subiquity/client/client.py b/subiquity/client/client.py
index 927a3edc..6f01dc31 100644
--- a/subiquity/client/client.py
+++ b/subiquity/client/client.py
@@ -101,6 +101,7 @@ class SubiquityClient(TuiApplication):
"Filesystem",
"Identity",
"SSH",
+ "SnapList",
"Progress",
]
diff --git a/subiquity/client/controllers/__init__.py b/subiquity/client/controllers/__init__.py
index 41492eb8..fca8d162 100644
--- a/subiquity/client/controllers/__init__.py
+++ b/subiquity/client/controllers/__init__.py
@@ -22,6 +22,7 @@ from .network import NetworkController
from .progress import ProgressController
from .proxy import ProxyController
from .refresh import RefreshController
+from .snaplist import SnapListController
from .ssh import SSHController
from .welcome import WelcomeController
from .zdev import ZdevController
@@ -36,6 +37,7 @@ __all__ = [
'ProxyController',
'RefreshController',
'RepeatedController',
+ 'SnapListController',
'SSHController',
'WelcomeController',
'ZdevController',
diff --git a/subiquity/client/controllers/snaplist.py b/subiquity/client/controllers/snaplist.py
new file mode 100644
index 00000000..2832c0e4
--- /dev/null
+++ b/subiquity/client/controllers/snaplist.py
@@ -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 .
+
+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
diff --git a/subiquity/common/apidef.py b/subiquity/common/apidef.py
index 17da6052..52858932 100644
--- a/subiquity/common/apidef.py
+++ b/subiquity/common/apidef.py
@@ -32,6 +32,9 @@ from subiquity.common.types import (
InstallState,
InstallStatus,
RefreshStatus,
+ SnapInfo,
+ SnapListResponse,
+ SnapSelection,
SSHData,
StorageResponse,
ZdevInfo,
@@ -172,6 +175,13 @@ class API:
class reset:
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 status:
def GET(cur: Optional[InstallState] = None) -> InstallStatus: ...
diff --git a/subiquity/controllers/__init__.py b/subiquity/controllers/__init__.py
index 8ecae971..e81cd12a 100644
--- a/subiquity/controllers/__init__.py
+++ b/subiquity/controllers/__init__.py
@@ -14,9 +14,7 @@
# along with this program. If not, see .
from .reboot import RebootController
-from .snaplist import SnapListController
__all__ = [
'RebootController',
- 'SnapListController',
]
diff --git a/subiquity/server/controllers/__init__.py b/subiquity/server/controllers/__init__.py
index f8492bd8..fefe6b4f 100644
--- a/subiquity/server/controllers/__init__.py
+++ b/subiquity/server/controllers/__init__.py
@@ -26,6 +26,7 @@ from .package import PackageController
from .proxy import ProxyController
from .refresh import RefreshController
from .reporting import ReportingController
+from .snaplist import SnapListController
from .ssh import SSHController
from .userdata import UserdataController
from .zdev import ZdevController
@@ -46,6 +47,7 @@ __all__ = [
'ProxyController',
'RefreshController',
'ReportingController',
+ 'SnapListController',
'SSHController',
'UserdataController',
'ZdevController',
diff --git a/subiquity/controllers/snaplist.py b/subiquity/server/controllers/snaplist.py
similarity index 77%
rename from subiquity/controllers/snaplist.py
rename to subiquity/server/controllers/snaplist.py
index de19e7b1..c8793d7f 100644
--- a/subiquity/controllers/snaplist.py
+++ b/subiquity/server/controllers/snaplist.py
@@ -16,28 +16,28 @@
import logging
from typing import List
+import attr
+
import requests.exceptions
from subiquitycore.async_helpers import (
schedule_task,
)
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 (
- SnapListResponse,
SnapCheckState,
+ SnapInfo,
+ SnapListResponse,
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:
@@ -52,7 +52,7 @@ class SnapdSnapInfoLoader:
self.failed = False
self.snapd = snapd
- self.pending_info_snaps = []
+ self.pending_snaps = []
self.tasks = {} # {snap:task}
def start(self):
@@ -109,7 +109,9 @@ class SnapdSnapInfoLoader:
return self.tasks[snap]
-class SnapListController(SubiquityTuiController):
+class SnapListController(SubiquityController):
+
+ endpoint = API.snaplist
autoinstall_key = "snaps"
autoinstall_default = []
@@ -141,11 +143,12 @@ class SnapListController(SubiquityTuiController):
self.loader = self._make_loader()
def load_autoinstall_data(self, ai_data):
- to_install = {}
+ to_install = []
for snap in ai_data:
- to_install[snap['name']] = SnapSelection(
+ to_install.append(SnapSelection(
+ name=snap['name'],
channel=snap.get('channel', 'stable'),
- is_classic=snap.get('classic', False))
+ is_classic=snap.get('classic', False)))
self.model.set_installed_list(to_install)
def snapd_network_changed(self):
@@ -160,24 +163,12 @@ class SnapListController(SubiquityTuiController):
self.loader = self._make_loader()
self.loader.start()
- async def make_ui(self):
- data = await self.get_snap_list(wait=False)
- 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 make_autoinstall(self):
+ return [attr.asdict(sel) for sel in self.model.selections]
- 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)
-
- async def get_snap_list(self, *, wait: bool) -> SnapListResponse:
+ async def GET(self, wait: bool = False) -> SnapListResponse:
if self.loader.failed or not self.app.base_model.network.has_network:
+ self.configured()
return SnapListResponse(status=SnapCheckState.FAILED)
if not self.loader.snap_list_fetched and not wait:
return SnapListResponse(status=SnapCheckState.LOADING)
@@ -187,19 +178,11 @@ class SnapListController(SubiquityTuiController):
snaps=self.model.get_snap_list(),
selections=self.model.selections)
- def get_snap_info_task(self, snap):
- return self.loader.get_snap_info_task(snap)
-
- def done(self, selections: List[SnapSelection]):
- log.debug(
- "SnapListController.done next_screen snaps_to_install=%s",
- selections)
- self.model.set_installed_list(selections)
+ async def POST(self, data: List[SnapSelection]):
+ self.model.set_installed_list(data)
self.configured()
- self.app.next_screen()
- def cancel(self, sender=None):
- self.app.prev_screen()
-
- def make_autoinstall(self):
- return self.model.selections
+ async def snap_info_GET(self, snap_name: str) -> SnapInfo:
+ snap = self.model._snap_for_name(snap_name)
+ await self.loader.get_snap_info_task(snap)
+ return snap
diff --git a/subiquity/server/server.py b/subiquity/server/server.py
index 565e8df1..7e82f05d 100644
--- a/subiquity/server/server.py
+++ b/subiquity/server/server.py
@@ -128,6 +128,7 @@ class SubiquityServer(Application):
"Filesystem",
"Identity",
"SSH",
+ "SnapList",
"Install",
"Late",
]
diff --git a/subiquity/ui/views/snaplist.py b/subiquity/ui/views/snaplist.py
index 01b66cf3..da792aee 100644
--- a/subiquity/ui/views/snaplist.py
+++ b/subiquity/ui/views/snaplist.py
@@ -297,7 +297,7 @@ class SnapCheckBox(CheckBox):
app = self.parent.controller.app
await app.wait_with_text_dialog(
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),
can_cancel=True)
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
# at least a second to avoid flickering at the user.
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
spinner.stop()
if data.status == SnapCheckState.FAILED: