Merge pull request #1217 from ogayot/ua-rebranding

Rename UbuntuAdvantage -> UbuntuPro
This commit is contained in:
Olivier Gayot 2022-03-17 09:20:43 +01:00
commit 2bf39f02ca
32 changed files with 250 additions and 139 deletions

View File

@ -260,7 +260,7 @@
} }
] ]
}, },
"ubuntu-advantage": { "ubuntu-pro": {
"type": "object", "type": "object",
"properties": { "properties": {
"token": { "token": {
@ -272,6 +272,20 @@
} }
} }
}, },
"ubuntu-advantage": {
"type": "object",
"properties": {
"token": {
"type": "string",
"minLength": 24,
"maxLength": 30,
"pattern": "^C[1-9A-HJ-NP-Za-km-z]+$",
"description": "A valid token starts with a C and is followed by 23 to 29 Base58 characters.\nSee https://pkg.go.dev/github.com/btcsuite/btcutil/base58#CheckEncode"
}
},
"deprecated": true,
"description": "Compatibility only - use ubuntu-pro instead"
},
"proxy": { "proxy": {
"type": [ "type": [
"string", "string",

View File

@ -45,7 +45,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: "" token: ""
SSH: SSH:
install_server: false install_server: false

View File

@ -21,7 +21,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
SSH: SSH:
install_server: false install_server: false

View File

@ -20,7 +20,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
SSH: SSH:
install_server: true install_server: true

View File

@ -64,7 +64,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
SSH: SSH:
install_server: true install_server: true

View File

@ -45,7 +45,7 @@ Filesystem:
fstype: ext4 fstype: ext4
mount: / mount: /
- action: done - action: done
UbuntuAdvantage: UbuntuPro:
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
Identity: Identity:
realname: Ubuntu realname: Ubuntu

View File

@ -25,7 +25,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: "" token: ""
SSH: SSH:
install_server: true install_server: true

View File

@ -96,7 +96,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
SSH: SSH:
install_server: false install_server: false

View File

@ -55,7 +55,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
SSH: SSH:
install_server: false install_server: false

View File

@ -24,7 +24,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: "" token: ""
SSH: SSH:
install_server: true install_server: true

View File

@ -28,7 +28,7 @@ Identity:
hostname: ubuntu-server hostname: ubuntu-server
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
UbuntuAdvantage: UbuntuPro:
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
SSH: SSH:
install_server: false install_server: false

View File

@ -22,7 +22,7 @@ Identity:
# ubuntu # ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1' password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
ssh-import-id: gh:mwhudson ssh-import-id: gh:mwhudson
UbuntuAdvantage: UbuntuPro:
token: "" token: ""
SnapList: SnapList:
snaps: snaps:

View File

@ -48,7 +48,7 @@ snaps:
channel: 3.2/stable channel: 3.2/stable
updates: all updates: all
timezone: Pacific/Guam timezone: Pacific/Guam
ubuntu-advantage: ubuntu-pro:
# Token that passes the basic format checking but is invalid (i.e. contains more than 16 bytes of random data) # Token that passes the basic format checking but is invalid (i.e. contains more than 16 bytes of random data)
token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7 token: C1NWcZTHLteJXGVMM6YhvHDpGrhyy7
storage: storage:

View File

@ -111,7 +111,7 @@ class SubiquityClient(TuiApplication):
"Refresh", "Refresh",
"Filesystem", "Filesystem",
"Identity", "Identity",
"UbuntuAdvantage", "UbuntuPro",
"SSH", "SSH",
"SnapList", "SnapList",
"Progress", "Progress",

View File

@ -26,7 +26,7 @@ from .serial import SerialController
from .snaplist import SnapListController from .snaplist import SnapListController
from .source import SourceController from .source import SourceController
from .ssh import SSHController from .ssh import SSHController
from .ubuntu_advantage import UbuntuAdvantageController from .ubuntu_pro import UbuntuProController
from .welcome import WelcomeController from .welcome import WelcomeController
from .zdev import ZdevController from .zdev import ZdevController
@ -45,7 +45,7 @@ __all__ = [
'SnapListController', 'SnapListController',
'SourceController', 'SourceController',
'SSHController', 'SSHController',
'UbuntuAdvantageController', 'UbuntuProController',
'WelcomeController', 'WelcomeController',
'ZdevController', 'ZdevController',
] ]

View File

@ -12,8 +12,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Module that defines the client-side controller class for Ubuntu Advantage. """ Module that defines the client-side controller class for Ubuntu Pro. """
"""
import asyncio import asyncio
import logging import logging
@ -23,21 +22,21 @@ from subiquitycore.async_helpers import schedule_task
from subiquity.client.controller import SubiquityTuiController from subiquity.client.controller import SubiquityTuiController
from subiquity.common.types import ( from subiquity.common.types import (
UbuntuAdvantageInfo, UbuntuProInfo,
UbuntuAdvantageCheckTokenStatus as TokenStatus, UbuntuProCheckTokenStatus as TokenStatus,
) )
from subiquity.ui.views.ubuntu_advantage import UbuntuAdvantageView from subiquity.ui.views.ubuntu_pro import UbuntuProView
from subiquitycore.lsb_release import lsb_release from subiquitycore.lsb_release import lsb_release
from subiquitycore.tuicontroller import Skip from subiquitycore.tuicontroller import Skip
log = logging.getLogger("subiquity.client.controllers.ubuntu_advantage") log = logging.getLogger("subiquity.client.controllers.ubuntu_pro")
class UbuntuAdvantageController(SubiquityTuiController): class UbuntuProController(SubiquityTuiController):
""" Client-side controller for Ubuntu Advantage configuration. """ """ Client-side controller for Ubuntu Pro configuration. """
endpoint_name = "ubuntu_advantage" endpoint_name = "ubuntu_pro"
def __init__(self, app) -> None: def __init__(self, app) -> None:
""" Initializer for the client-side UA controller. """ """ Initializer for the client-side UA controller. """
@ -45,7 +44,7 @@ class UbuntuAdvantageController(SubiquityTuiController):
super().__init__(app) super().__init__(app)
async def make_ui(self) -> UbuntuAdvantageView: async def make_ui(self) -> UbuntuProView:
""" Generate the UI, based on the data provided by the model. """ """ Generate the UI, based on the data provided by the model. """
dry_run: bool = self.app.opts.dry_run dry_run: bool = self.app.opts.dry_run
@ -57,8 +56,8 @@ class UbuntuAdvantageController(SubiquityTuiController):
await self.endpoint.skip.POST() await self.endpoint.skip.POST()
raise Skip("Not running LTS version") raise Skip("Not running LTS version")
ubuntu_advantage_info = await self.endpoint.GET() ubuntu_pro_info = await self.endpoint.GET()
return UbuntuAdvantageView(self, ubuntu_advantage_info.token) return UbuntuProView(self, ubuntu_pro_info.token)
def run_answers(self) -> None: def run_answers(self) -> None:
if "token" in self.answers: if "token" in self.answers:
@ -69,16 +68,16 @@ class UbuntuAdvantageController(SubiquityTuiController):
async def inner() -> None: async def inner() -> None:
answer = await self.endpoint.check_token.GET(token) answer = await self.endpoint.check_token.GET(token)
if answer.status == TokenStatus.INVALID_TOKEN: if answer.status == TokenStatus.INVALID_TOKEN:
if isinstance(self.ui.body, UbuntuAdvantageView): if isinstance(self.ui.body, UbuntuProView):
self.ui.body.show_invalid_token() self.ui.body.show_invalid_token()
elif answer.status == TokenStatus.EXPIRED_TOKEN: elif answer.status == TokenStatus.EXPIRED_TOKEN:
if isinstance(self.ui.body, UbuntuAdvantageView): if isinstance(self.ui.body, UbuntuProView):
self.ui.body.show_expired_token() self.ui.body.show_expired_token()
elif answer.status == TokenStatus.UNKNOWN_ERROR: elif answer.status == TokenStatus.UNKNOWN_ERROR:
if isinstance(self.ui.body, UbuntuAdvantageView): if isinstance(self.ui.body, UbuntuProView):
self.ui.body.show_unknown_error() self.ui.body.show_unknown_error()
else: else:
if isinstance(self.ui.body, UbuntuAdvantageView): if isinstance(self.ui.body, UbuntuProView):
self.ui.body.show_activable_services(answer.services) self.ui.body.show_activable_services(answer.services)
self._check_task = schedule_task(inner()) self._check_task = schedule_task(inner())
@ -93,5 +92,5 @@ class UbuntuAdvantageController(SubiquityTuiController):
def done(self, token: str) -> None: def done(self, token: str) -> None:
self.app.next_screen( self.app.next_screen(
self.endpoint.POST(UbuntuAdvantageInfo(token=token)) self.endpoint.POST(UbuntuProInfo(token=token))
) )

View File

@ -33,7 +33,19 @@ def make_schema(app):
cschema = getattr(controller, "autoinstall_schema", None) cschema = getattr(controller, "autoinstall_schema", None)
if cschema is None: if cschema is None:
continue continue
schema['properties'][ckey] = cschema schema['properties'][ckey] = cschema
ckey_alias = getattr(controller, 'autoinstall_key_alias', None)
if ckey_alias is None:
continue
cschema = cschema.copy()
cschema["deprecated"] = True
cschema["description"] = f"Compatibility only - use {ckey} instead"
schema['properties'][ckey_alias] = cschema
return schema return schema

View File

@ -50,8 +50,8 @@ from subiquity.common.types import (
StorageResponse, StorageResponse,
StorageResponseV2, StorageResponseV2,
TimeZoneInfo, TimeZoneInfo,
UbuntuAdvantageInfo, UbuntuProInfo,
UbuntuAdvantageCheckTokenAnswer, UbuntuProCheckTokenAnswer,
WLANSupportInstallState, WLANSupportInstallState,
ZdevInfo, ZdevInfo,
WSLConfigurationBase, WSLConfigurationBase,
@ -321,16 +321,16 @@ class API:
def GET() -> List[str]: ... def GET() -> List[str]: ...
def POST(data: Payload[List[str]]): ... def POST(data: Payload[List[str]]): ...
class ubuntu_advantage: class ubuntu_pro:
def GET() -> UbuntuAdvantageInfo: ... def GET() -> UbuntuProInfo: ...
def POST(data: Payload[UbuntuAdvantageInfo]) -> None: ... def POST(data: Payload[UbuntuProInfo]) -> None: ...
class skip: class skip:
def POST() -> None: ... def POST() -> None: ...
class check_token: class check_token:
def GET(token: Payload[str]) \ def GET(token: Payload[str]) \
-> UbuntuAdvantageCheckTokenAnswer: ... -> UbuntuProCheckTokenAnswer: ...
class LinkAction(enum.Enum): class LinkAction(enum.Enum):

View File

@ -416,11 +416,11 @@ class TimeZoneInfo:
@attr.s(auto_attribs=True) @attr.s(auto_attribs=True)
class UbuntuAdvantageInfo: class UbuntuProInfo:
token: str = attr.ib(repr=False) token: str = attr.ib(repr=False)
class UbuntuAdvantageCheckTokenStatus(enum.Enum): class UbuntuProCheckTokenStatus(enum.Enum):
VALID_TOKEN = enum.auto() VALID_TOKEN = enum.auto()
INVALID_TOKEN = enum.auto() INVALID_TOKEN = enum.auto()
EXPIRED_TOKEN = enum.auto() EXPIRED_TOKEN = enum.auto()
@ -428,16 +428,16 @@ class UbuntuAdvantageCheckTokenStatus(enum.Enum):
@attr.s(auto_attribs=True) @attr.s(auto_attribs=True)
class UbuntuAdvantageService: class UbuntuProService:
name: str name: str
description: str description: str
@attr.s(auto_attribs=True) @attr.s(auto_attribs=True)
class UbuntuAdvantageCheckTokenAnswer: class UbuntuProCheckTokenAnswer:
status: UbuntuAdvantageCheckTokenStatus status: UbuntuProCheckTokenStatus
services: Optional[List[UbuntuAdvantageService]] services: Optional[List[UbuntuProService]]
class ShutdownMode(enum.Enum): class ShutdownMode(enum.Enum):

View File

@ -43,7 +43,7 @@ from .snaplist import SnapListModel
from .source import SourceModel from .source import SourceModel
from .ssh import SSHModel from .ssh import SSHModel
from .timezone import TimeZoneModel from .timezone import TimeZoneModel
from .ubuntu_advantage import UbuntuAdvantageModel from .ubuntu_pro import UbuntuProModel
from .updates import UpdatesModel from .updates import UpdatesModel
@ -147,7 +147,7 @@ class SubiquityModel:
self.ssh = SSHModel() self.ssh = SSHModel()
self.source = SourceModel() self.source = SourceModel()
self.timezone = TimeZoneModel() self.timezone = TimeZoneModel()
self.ubuntu_advantage = UbuntuAdvantageModel() self.ubuntu_pro = UbuntuProModel()
self.updates = UpdatesModel() self.updates = UpdatesModel()
self.userdata = {} self.userdata = {}

View File

@ -15,12 +15,12 @@
import unittest import unittest
from subiquity.models.ubuntu_advantage import UbuntuAdvantageModel from subiquity.models.ubuntu_pro import UbuntuProModel
class TestUbuntuAdvantageModel(unittest.TestCase): class TestUbuntuProModel(unittest.TestCase):
def test_make_cloudconfig_(self): def test_make_cloudconfig_(self):
model = UbuntuAdvantageModel() model = UbuntuProModel()
# Test with a token # Test with a token
model.token = "0a1b2c3d4e5f6" model.token = "0a1b2c3d4e5f6"

View File

@ -12,16 +12,16 @@
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Module that defines the model for Ubuntu Advantage configuration. """ """ Module that defines the model for Ubuntu Pro configuration. """
import logging import logging
log = logging.getLogger("subiquity.models.ubuntu_advantage") log = logging.getLogger("subiquity.models.ubuntu_pro")
class UbuntuAdvantageModel: class UbuntuProModel:
""" """
Model that represents the Ubuntu Advantage configuration. Model that represents the Ubuntu Pro configuration.
Currently, we rely only on cloud-init so we have no means to validate that Currently, we rely only on cloud-init so we have no means to validate that
the provided token is correct ; nor to retrieve information about the the provided token is correct ; nor to retrieve information about the
subscription. subscription.

View File

@ -38,6 +38,11 @@ class SubiquityController(BaseController):
autoinstall_default: Any = None autoinstall_default: Any = None
endpoint: Optional[type] = None endpoint: Optional[type] = None
# If we want to update the autoinstall_key, we can add the old value
# here to keep being backwards compatible. The old value will be marked
# deprecated in favor of autoinstall_key.
autoinstall_key_alias: Optional[str] = None
def __init__(self, app): def __init__(self, app):
super().__init__(app) super().__init__(app)
self.context.set('controller', self) self.context.set('controller', self)
@ -46,9 +51,19 @@ class SubiquityController(BaseController):
if not self.app.autoinstall_config: if not self.app.autoinstall_config:
return return
with self.context.child("load_autoinstall_data"): with self.context.child("load_autoinstall_data"):
ai_data = self.app.autoinstall_config.get( key_candidates = [self.autoinstall_key]
self.autoinstall_key, if self.autoinstall_key_alias is not None:
self.autoinstall_default) key_candidates.append(self.autoinstall_key_alias)
for key in key_candidates:
try:
ai_data = self.app.autoinstall_config[key]
break
except KeyError:
pass
else:
ai_data = self.autoinstall_default
if ai_data is not None and self.autoinstall_schema is not None: if ai_data is not None and self.autoinstall_schema is not None:
jsonschema.validate(ai_data, self.autoinstall_schema) jsonschema.validate(ai_data, self.autoinstall_schema)
self.load_autoinstall_data(ai_data) self.load_autoinstall_data(ai_data)
@ -77,7 +92,15 @@ class SubiquityController(BaseController):
return True return True
i_sections = self.app.autoinstall_config.get( i_sections = self.app.autoinstall_config.get(
'interactive-sections', []) 'interactive-sections', [])
return '*' in i_sections or self.autoinstall_key in i_sections
if "*" in i_sections:
return True
if self.autoinstall_key in i_sections:
return True
return (self.autoinstall_key_alias is not None
and self.autoinstall_key_alias in i_sections)
async def configured(self): async def configured(self):
"""Let the world know that this controller's model is now configured. """Let the world know that this controller's model is now configured.

View File

@ -33,7 +33,7 @@ from .snaplist import SnapListController
from .source import SourceController from .source import SourceController
from .ssh import SSHController from .ssh import SSHController
from .timezone import TimeZoneController from .timezone import TimeZoneController
from .ubuntu_advantage import UbuntuAdvantageController from .ubuntu_pro import UbuntuProController
from .updates import UpdatesController from .updates import UpdatesController
from .userdata import UserdataController from .userdata import UserdataController
from .zdev import ZdevController from .zdev import ZdevController
@ -61,7 +61,7 @@ __all__ = [
'SourceController', 'SourceController',
'SSHController', 'SSHController',
'TimeZoneController', 'TimeZoneController',
'UbuntuAdvantageController', 'UbuntuProController',
'UpdatesController', 'UpdatesController',
'UserdataController', 'UserdataController',
'ZdevController', 'ZdevController',

View File

@ -15,15 +15,15 @@
import unittest import unittest
from subiquity.server.controllers.ubuntu_advantage import ( from subiquity.server.controllers.ubuntu_pro import (
UbuntuAdvantageController, UbuntuProController,
) )
from subiquitycore.tests.mocks import make_app from subiquitycore.tests.mocks import make_app
class TestUbuntuAdvantageController(unittest.TestCase): class TestUbuntuProController(unittest.TestCase):
def setUp(self): def setUp(self):
self.controller = UbuntuAdvantageController(make_app()) self.controller = UbuntuProController(make_app())
def test_serialize(self): def test_serialize(self):
self.controller.model.token = "1a2b3C" self.controller.model.token = "1a2b3C"

View File

@ -19,13 +19,13 @@ import os
from subiquity.common.apidef import API from subiquity.common.apidef import API
from subiquity.common.types import ( from subiquity.common.types import (
UbuntuAdvantageInfo, UbuntuProInfo,
UbuntuAdvantageCheckTokenAnswer, UbuntuProCheckTokenAnswer,
UbuntuAdvantageCheckTokenStatus, UbuntuProCheckTokenStatus,
) )
from subiquity.server.ubuntu_advantage import ( from subiquity.server.ubuntu_advantage import (
InvalidUATokenError, InvalidTokenError,
ExpiredUATokenError, ExpiredTokenError,
CheckSubscriptionError, CheckSubscriptionError,
UAInterface, UAInterface,
UAInterfaceStrategy, UAInterfaceStrategy,
@ -34,20 +34,21 @@ from subiquity.server.ubuntu_advantage import (
) )
from subiquity.server.controller import SubiquityController from subiquity.server.controller import SubiquityController
log = logging.getLogger("subiquity.server.controllers.ubuntu_advantage") log = logging.getLogger("subiquity.server.controllers.ubuntu_pro")
TOKEN_DESC = """\ TOKEN_DESC = """\
A valid token starts with a C and is followed by 23 to 29 Base58 characters. A valid token starts with a C and is followed by 23 to 29 Base58 characters.
See https://pkg.go.dev/github.com/btcsuite/btcutil/base58#CheckEncode""" See https://pkg.go.dev/github.com/btcsuite/btcutil/base58#CheckEncode"""
class UbuntuAdvantageController(SubiquityController): class UbuntuProController(SubiquityController):
""" Represent the server-side Ubuntu Advantage controller. """ """ Represent the server-side Ubuntu Pro controller. """
endpoint = API.ubuntu_advantage endpoint = API.ubuntu_pro
model_name = "ubuntu_advantage" model_name = "ubuntu_pro"
autoinstall_key = "ubuntu-advantage" autoinstall_key = "ubuntu-pro"
autoinstall_key_alias = "ubuntu-advantage"
autoinstall_schema = { autoinstall_schema = {
"type": "object", "type": "object",
"properties": { "properties": {
@ -62,7 +63,7 @@ class UbuntuAdvantageController(SubiquityController):
} }
def __init__(self, app) -> None: def __init__(self, app) -> None:
""" Initializer for server-side UA controller. """ """ Initializer for server-side Ubuntu Pro controller. """
strategy: UAInterfaceStrategy strategy: UAInterfaceStrategy
if app.opts.dry_run: if app.opts.dry_run:
strategy = MockedUAInterfaceStrategy(scale_factor=app.scale_factor) strategy = MockedUAInterfaceStrategy(scale_factor=app.scale_factor)
@ -84,7 +85,7 @@ class UbuntuAdvantageController(SubiquityController):
def make_autoinstall(self) -> dict: def make_autoinstall(self) -> dict:
""" Return a dictionary that can be used as an autoinstall snippet for """ Return a dictionary that can be used as an autoinstall snippet for
Ubuntu Advantage. Ubuntu Pro.
""" """
if not self.model.token: if not self.model.token:
return {} return {}
@ -102,11 +103,11 @@ class UbuntuAdvantageController(SubiquityController):
""" Loads the last-known state of the model. """ """ Loads the last-known state of the model. """
self.model.token = token self.model.token = token
async def GET(self) -> UbuntuAdvantageInfo: async def GET(self) -> UbuntuProInfo:
""" Handle a GET request coming from the client-side controller. """ """ Handle a GET request coming from the client-side controller. """
return UbuntuAdvantageInfo(token=self.model.token) return UbuntuProInfo(token=self.model.token)
async def POST(self, data: UbuntuAdvantageInfo) -> None: async def POST(self, data: UbuntuProInfo) -> None:
""" Handle a POST request coming from the client-side controller and """ Handle a POST request coming from the client-side controller and
then call .configured(). then call .configured().
""" """
@ -119,7 +120,7 @@ class UbuntuAdvantageController(SubiquityController):
await self.configured() await self.configured()
async def check_token_GET(self, token: str) \ async def check_token_GET(self, token: str) \
-> UbuntuAdvantageCheckTokenAnswer: -> UbuntuProCheckTokenAnswer:
""" Handle a GET request asking whether the contract token is valid or """ Handle a GET request asking whether the contract token is valid or
not. If it is valid, we provide the list of activable services not. If it is valid, we provide the list of activable services
associated with the subscription. associated with the subscription.
@ -128,14 +129,13 @@ class UbuntuAdvantageController(SubiquityController):
try: try:
services = await \ services = await \
self.ua_interface.get_activable_services(token=token) self.ua_interface.get_activable_services(token=token)
except InvalidUATokenError: except InvalidTokenError:
status = UbuntuAdvantageCheckTokenStatus.INVALID_TOKEN status = UbuntuProCheckTokenStatus.INVALID_TOKEN
except ExpiredUATokenError: except ExpiredTokenError:
status = UbuntuAdvantageCheckTokenStatus.EXPIRED_TOKEN status = UbuntuProCheckTokenStatus.EXPIRED_TOKEN
except CheckSubscriptionError: except CheckSubscriptionError:
status = UbuntuAdvantageCheckTokenStatus.UNKNOWN_ERROR status = UbuntuProCheckTokenStatus.UNKNOWN_ERROR
else: else:
status = UbuntuAdvantageCheckTokenStatus.VALID_TOKEN status = UbuntuProCheckTokenStatus.VALID_TOKEN
return UbuntuAdvantageCheckTokenAnswer(status=status, return UbuntuProCheckTokenAnswer(status=status, services=services)
services=services)

View File

@ -207,7 +207,7 @@ POSTINSTALL_MODEL_NAMES = ModelNames({
"packages", "packages",
"snaplist", "snaplist",
"ssh", "ssh",
"ubuntu_advantage", "ubuntu_pro",
"userdata", "userdata",
}, },
desktop={"timezone"}) desktop={"timezone"})
@ -246,7 +246,7 @@ class SubiquityServer(Application):
"Zdev", "Zdev",
"Source", "Source",
"Network", "Network",
"UbuntuAdvantage", "UbuntuPro",
"Proxy", "Proxy",
"Mirror", "Mirror",
"Filesystem", "Filesystem",

View File

@ -0,0 +1,62 @@
# Copyright 2022 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 contextlib
from unittest.mock import patch
from subiquitycore.tests import SubiTestCase
from subiquitycore.tests.mocks import make_app
from subiquity.server.controller import SubiquityController
class TestController(SubiTestCase):
def setUp(self):
self.controller = SubiquityController(make_app())
self.controller.context.child = contextlib.nullcontext
@patch.object(SubiquityController, "load_autoinstall_data")
def test_setup_autoinstall(self, mock_load):
# No autoinstall data
self.controller.app.autoinstall_config = None
self.controller.setup_autoinstall()
mock_load.assert_not_called()
# Make sure the autoinstall_key has precedence over
# autoinstall_key_alias if both are present.
self.controller.app.autoinstall_config = {
"dummy": "some-dummy-data",
"dummy-alias": "some-dummy-alias-data",
}
self.controller.autoinstall_key = "dummy"
self.controller.autoinstall_key_alias = "dummy-alias"
self.controller.autoinstall_default = "default-data"
self.controller.setup_autoinstall()
mock_load.assert_called_once_with("some-dummy-data")
# Make sure we failover to autoinstall_key_alias if autoinstall_key is
# not present
mock_load.reset_mock()
self.controller.autoinstall_key = "inexistent"
self.controller.setup_autoinstall()
mock_load.assert_called_once_with("some-dummy-alias-data")
# Make sure we failover to autoinstall_default otherwise
mock_load.reset_mock()
self.controller.autoinstall_key = "inexistent"
self.controller.autoinstall_key_alias = "inexistent"
self.controller.setup_autoinstall()
mock_load.assert_called_once_with("default-data")

View File

@ -17,10 +17,10 @@ from subprocess import CalledProcessError, CompletedProcess
import unittest import unittest
from unittest.mock import patch, AsyncMock from unittest.mock import patch, AsyncMock
from subiquity.common.types import UbuntuAdvantageService from subiquity.common.types import UbuntuProService
from subiquity.server.ubuntu_advantage import ( from subiquity.server.ubuntu_advantage import (
InvalidUATokenError, InvalidTokenError,
ExpiredUATokenError, ExpiredTokenError,
CheckSubscriptionError, CheckSubscriptionError,
UAInterface, UAInterface,
MockedUAInterfaceStrategy, MockedUAInterfaceStrategy,
@ -36,7 +36,7 @@ class TestMockedUAInterfaceStrategy(unittest.TestCase):
def test_query_info_invalid(self): def test_query_info_invalid(self):
# Tokens starting with "i" in dry-run mode cause the token to be # Tokens starting with "i" in dry-run mode cause the token to be
# reported as invalid. # reported as invalid.
with self.assertRaises(InvalidUATokenError): with self.assertRaises(InvalidTokenError):
run_coro(self.strategy.query_info(token="invalidToken")) run_coro(self.strategy.query_info(token="invalidToken"))
def test_query_info_failure(self): def test_query_info_failure(self):
@ -130,7 +130,7 @@ class TestUAInterface(unittest.TestCase):
strategy = MockedUAInterfaceStrategy(scale_factor=1_000_000) strategy = MockedUAInterfaceStrategy(scale_factor=1_000_000)
interface = UAInterface(strategy) interface = UAInterface(strategy)
with self.assertRaises(InvalidUATokenError): with self.assertRaises(InvalidTokenError):
run_coro(interface.get_activable_services(token="invalidToken")) run_coro(interface.get_activable_services(token="invalidToken"))
# Tokens starting with "f" in dry-run mode simulate an "internal" # Tokens starting with "f" in dry-run mode simulate an "internal"
# error. # error.
@ -138,7 +138,7 @@ class TestUAInterface(unittest.TestCase):
run_coro(interface.get_activable_services(token="failure")) run_coro(interface.get_activable_services(token="failure"))
# Tokens starting with "x" is dry-run mode simulate an expired token. # Tokens starting with "x" is dry-run mode simulate an expired token.
with self.assertRaises(ExpiredUATokenError): with self.assertRaises(ExpiredTokenError):
run_coro(interface.get_activable_services(token="xpiredToken")) run_coro(interface.get_activable_services(token="xpiredToken"))
# Other tokens are considered valid in dry-run mode. # Other tokens are considered valid in dry-run mode.
@ -188,19 +188,19 @@ class TestUAInterface(unittest.TestCase):
services = run_coro( services = run_coro(
interface.get_activable_services(token="XXX")) interface.get_activable_services(token="XXX"))
self.assertIn(UbuntuAdvantageService( self.assertIn(UbuntuProService(
name="esm-infra", name="esm-infra",
description="UA Infra: Extended Security Maintenance (ESM)", description="UA Infra: Extended Security Maintenance (ESM)",
), services) ), services)
self.assertIn(UbuntuAdvantageService( self.assertIn(UbuntuProService(
name="fips", name="fips",
description="NIST-certified core packages", description="NIST-certified core packages",
), services) ), services)
self.assertNotIn(UbuntuAdvantageService( self.assertNotIn(UbuntuProService(
name="esm-apps", name="esm-apps",
description="UA Apps: Extended Security Maintenance (ESM)", description="UA Apps: Extended Security Maintenance (ESM)",
), services) ), services)
self.assertNotIn(UbuntuAdvantageService( self.assertNotIn(UbuntuProService(
name="cis", name="cis",
description="Center for Internet Security Audit Tools", description="Center for Internet Security Audit Tools",
), services) ), services)

View File

@ -12,8 +12,8 @@
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" This module defines utilities to interface with Ubuntu Advantage """ This module defines utilities to interface with the ubuntu-advantage-tools
subscriptions. """ helper. """
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from datetime import datetime as dt from datetime import datetime as dt
@ -23,14 +23,14 @@ from subprocess import CalledProcessError, CompletedProcess
from typing import List, Sequence, Union from typing import List, Sequence, Union
import asyncio import asyncio
from subiquity.common.types import UbuntuAdvantageService from subiquity.common.types import UbuntuProService
from subiquitycore import utils from subiquitycore import utils
log = logging.getLogger("subiquity.server.ubuntu_advantage") log = logging.getLogger("subiquity.server.ubuntu_advantage")
class InvalidUATokenError(Exception): class InvalidTokenError(Exception):
""" Exception to be raised when the supplied token is invalid. """ """ Exception to be raised when the supplied token is invalid. """
def __init__(self, token: str, message: str = "") -> None: def __init__(self, token: str, message: str = "") -> None:
self.token = token self.token = token
@ -38,7 +38,7 @@ class InvalidUATokenError(Exception):
super().__init__(message) super().__init__(message)
class ExpiredUATokenError(Exception): class ExpiredTokenError(Exception):
""" Exception to be raised when the supplied token has expired. """ """ Exception to be raised when the supplied token has expired. """
def __init__(self, token: str, expires: str, message: str = "") -> None: def __init__(self, token: str, expires: str, message: str = "") -> None:
self.token = token self.token = token
@ -85,7 +85,7 @@ class MockedUAInterfaceStrategy(UAInterfaceStrategy):
if token[0] == "x": if token[0] == "x":
path = "examples/uaclient-status-expired.json" path = "examples/uaclient-status-expired.json"
elif token[0] == "i": elif token[0] == "i":
raise InvalidUATokenError(token) raise InvalidTokenError(token)
elif token[0] == "f": elif token[0] == "f":
raise CheckSubscriptionError(token) raise CheckSubscriptionError(token)
else: else:
@ -149,7 +149,7 @@ class UAInterface:
return await self.strategy.query_info(token) return await self.strategy.query_info(token)
async def get_activable_services(self, token: str) \ async def get_activable_services(self, token: str) \
-> List[UbuntuAdvantageService]: -> List[UbuntuProService]:
""" Return a list of activable services (i.e. services that are """ Return a list of activable services (i.e. services that are
entitled to the subscription and available on the current hardware). entitled to the subscription and available on the current hardware).
""" """
@ -160,7 +160,7 @@ class UAInterface:
# See https://bugs.python.org/issue35829 # See https://bugs.python.org/issue35829
expiration = dt.fromisoformat(info["expires"].replace("Z", "+00:00")) expiration = dt.fromisoformat(info["expires"].replace("Z", "+00:00"))
if expiration.timestamp() <= dt.utcnow().timestamp(): if expiration.timestamp() <= dt.utcnow().timestamp():
raise ExpiredUATokenError(token, expires=info["expires"]) raise ExpiredTokenError(token, expires=info["expires"])
def is_activable_service(service: dict) -> bool: def is_activable_service(service: dict) -> bool:
# - the available field for a service refers to its availability on # - the available field for a service refers to its availability on
@ -170,13 +170,13 @@ class UAInterface:
return service["available"] == "yes" \ return service["available"] == "yes" \
and service["entitled"] == "yes" and service["entitled"] == "yes"
def service_from_dict(service: dict) -> UbuntuAdvantageService: def service_from_dict(service: dict) -> UbuntuProService:
return UbuntuAdvantageService( return UbuntuProService(
name=service["name"], name=service["name"],
description=service["description"], description=service["description"],
) )
activable_services: List[UbuntuAdvantageService] = [] activable_services: List[UbuntuProService] = []
for service in info["services"]: for service in info["services"]:
if not is_activable_service(service): if not is_activable_service(service):

View File

@ -235,7 +235,7 @@ class TestFlow(TestAPI):
ua_params = { ua_params = {
"token": "a1b2c3d4e6f7g8h9I0K1", "token": "a1b2c3d4e6f7g8h9I0K1",
} }
await inst.post('/ubuntu_advantage', ua_params) await inst.post('/ubuntu_pro', ua_params)
for state in 'RUNNING', 'POST_WAIT', 'POST_RUNNING', 'UU_RUNNING': for state in 'RUNNING', 'POST_WAIT', 'POST_RUNNING', 'UU_RUNNING':
await inst.get('/meta/status', cur=state) await inst.get('/meta/status', cur=state)

View File

@ -12,7 +12,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
""" Module that defines the view class for Ubuntu Advantage configuration. """ """ Module that defines the view class for Ubuntu Pro configuration. """
import logging import logging
import re import re
@ -25,7 +25,7 @@ from urwid import (
Widget, Widget,
) )
from subiquity.common.types import UbuntuAdvantageService as UAService from subiquity.common.types import UbuntuProService
from subiquitycore.view import BaseView from subiquitycore.view import BaseView
from subiquitycore.ui.buttons import ( from subiquitycore.ui.buttons import (
back_btn, back_btn,
@ -56,15 +56,15 @@ from subiquitycore.ui.utils import (
from subiquitycore.ui.interactive import StringEditor from subiquitycore.ui.interactive import StringEditor
log = logging.getLogger('subiquity.ui.views.ubuntu_advantage') log = logging.getLogger('subiquity.ui.views.ubuntu_pro')
ua_help = _("If you want to enroll this system using your Ubuntu Advantage " ua_help = _("If you want to enroll this system using your Ubuntu Pro "
"subscription, enter your Ubuntu Advantage token here. " "subscription, enter your Ubuntu Pro token here. "
"Otherwise, leave this blank.") "Otherwise, leave this blank.")
class UATokenEditor(StringEditor, WantsToKnowFormField): class UATokenEditor(StringEditor, WantsToKnowFormField):
""" Represent a text-box editor for the Ubuntu Advantage Token. """ """ Represent a text-box editor for the Ubuntu Pro Token. """
def __init__(self): def __init__(self):
""" Initialize the text-field editor for UA token. """ """ Initialize the text-field editor for UA token. """
self.valid_char_pat = r"[a-zA-Z0-9]" self.valid_char_pat = r"[a-zA-Z0-9]"
@ -83,26 +83,26 @@ class UATokenEditor(StringEditor, WantsToKnowFormField):
return super().valid_char(ch) return super().valid_char(ch)
class UbuntuAdvantageForm(Form): class UbuntuProForm(Form):
""" """
Represents a form requesting Ubuntu Advantage information Represents a form requesting Ubuntu Pro information
""" """
cancel_label = _("Back") cancel_label = _("Back")
UATokenField = simple_field(UATokenEditor) UATokenField = simple_field(UATokenEditor)
token = UATokenField(_("Ubuntu Advantage token:"), help=ua_help) token = UATokenField(_("Ubuntu Pro token:"), help=ua_help)
class CheckingUAToken(WidgetWrap): class CheckingUAToken(WidgetWrap):
""" Widget displaying a loading animation while checking ubuntu advantage """ Widget displaying a loading animation while checking ubuntu pro
subscription. """ subscription. """
def __init__(self, parent: BaseView): def __init__(self, parent: BaseView):
""" Initializes the loading animation widget. """ """ Initializes the loading animation widget. """
self.parent = parent self.parent = parent
spinner = Spinner(parent.controller.app.aio_loop, style="dots") spinner = Spinner(parent.controller.app.aio_loop, style="dots")
spinner.start() spinner.start()
text = _("Checking Ubuntu Advantage subscription...") text = _("Checking Ubuntu Pro subscription...")
button = cancel_btn(label=_("Cancel"), on_press=self.cancel) button = cancel_btn(label=_("Cancel"), on_press=self.cancel)
self.width = len(text) + 4 self.width = len(text) + 4
super().__init__( super().__init__(
@ -119,20 +119,20 @@ class CheckingUAToken(WidgetWrap):
self.parent.remove_overlay() self.parent.remove_overlay()
class UbuntuAdvantageView(BaseView): class UbuntuProView(BaseView):
""" Represent the view of the Ubuntu Advantage configuration. """ """ Represent the view of the Ubuntu Pro configuration. """
title = _("Enable Ubuntu Advantage") title = _("Enable Ubuntu Pro")
excerpt = _("Enter your Ubuntu Advantage token if you want to enroll " excerpt = _("Enter your Ubuntu Pro token if you want to enroll "
"this system.") "this system.")
def __init__(self, controller, token: str): def __init__(self, controller, token: str):
""" Initialize the view with the default value for the token. """ """ Initialize the view with the default value for the token. """
self.controller = controller self.controller = controller
self.form = UbuntuAdvantageForm(initial={"token": token}) self.form = UbuntuProForm(initial={"token": token})
def on_cancel(_: UbuntuAdvantageForm): def on_cancel(_: UbuntuProForm):
self.cancel() self.cancel()
connect_signal(self.form, 'submit', self.done) connect_signal(self.form, 'submit', self.done)
@ -140,7 +140,7 @@ class UbuntuAdvantageView(BaseView):
super().__init__(self.form.as_screen(excerpt=_(self.excerpt))) super().__init__(self.form.as_screen(excerpt=_(self.excerpt)))
def done(self, form: UbuntuAdvantageForm) -> None: def done(self, form: UbuntuProForm) -> None:
""" If no token was supplied, move on to the next screen. """ If no token was supplied, move on to the next screen.
If a token was provided, open the loading dialog and If a token was provided, open the loading dialog and
asynchronously check if the token is valid. """ asynchronously check if the token is valid. """
@ -165,7 +165,7 @@ class UbuntuAdvantageView(BaseView):
self.show_stretchy_overlay( self.show_stretchy_overlay(
SomethingFailed(self, SomethingFailed(self,
"Invalid token.", "Invalid token.",
"The Ubuntu Advantage token that you provided" "The Ubuntu Pro token that you provided"
" is invalid. Please make sure that you typed" " is invalid. Please make sure that you typed"
" your token correctly.")) " your token correctly."))
@ -176,7 +176,7 @@ class UbuntuAdvantageView(BaseView):
self.show_stretchy_overlay( self.show_stretchy_overlay(
SomethingFailed(self, SomethingFailed(self,
"Token expired.", "Token expired.",
"The Ubuntu Advantage token that you provided" "The Ubuntu Pro token that you provided"
" has expired. Please use a different token.")) " has expired. Please use a different token."))
def show_unknown_error(self) -> None: def show_unknown_error(self) -> None:
@ -188,9 +188,10 @@ class UbuntuAdvantageView(BaseView):
self.remove_overlay() self.remove_overlay()
self.show_stretchy_overlay(ContinueAnywayWidget(self)) self.show_stretchy_overlay(ContinueAnywayWidget(self))
def show_activable_services(self, services: List[UAService]) -> None: def show_activable_services(self,
services: List[UbuntuProService]) -> None:
""" Display an overlay with the list of services that can be enabled """ Display an overlay with the list of services that can be enabled
via Ubuntu Advantage subscription. After the user confirms, we will via Ubuntu Pro subscription. After the user confirms, we will
quit the current view and move on. """ quit the current view and move on. """
self.remove_overlay() self.remove_overlay()
self.show_stretchy_overlay(ShowServicesWidget(self, services)) self.show_stretchy_overlay(ShowServicesWidget(self, services))
@ -198,8 +199,8 @@ class UbuntuAdvantageView(BaseView):
class ShowServicesWidget(Stretchy): class ShowServicesWidget(Stretchy):
""" Widget to show the activable services for UA subscription. """ """ Widget to show the activable services for UA subscription. """
def __init__(self, parent: UbuntuAdvantageView, def __init__(self, parent: UbuntuProView,
services: List[UAService]) -> None: services: List[UbuntuProService]) -> None:
""" Initializes the widget by including the list of services as a """ Initializes the widget by including the list of services as a
bullet-point list. """ bullet-point list. """
self.parent = parent self.parent = parent
@ -208,7 +209,7 @@ class ShowServicesWidget(Stretchy):
title = _("Activable Services") title = _("Activable Services")
header = _("List of services that are activable through your " header = _("List of services that are activable through your "
"Ubuntu Advantage subscription:") "Ubuntu Pro subscription:")
widgets: List[Widget] = [ widgets: List[Widget] = [
Text(header), Text(header),
@ -231,7 +232,7 @@ class ShowServicesWidget(Stretchy):
class ContinueAnywayWidget(Stretchy): class ContinueAnywayWidget(Stretchy):
""" Widget that requests the user if he wants to go back or continue """ Widget that requests the user if he wants to go back or continue
anyway. """ anyway. """
def __init__(self, parent: UbuntuAdvantageView) -> None: def __init__(self, parent: UbuntuProView) -> None:
""" Initializes the widget by showing two buttons, one to go back and """ Initializes the widget by showing two buttons, one to go back and
one to move forward anyway. """ one to move forward anyway. """
self.parent = parent self.parent = parent