ubuntu-pro: use checkboxes to skip or enter token

Instead of relying on the contract token empty to skip Ubuntu Pro, we
now explicitly ask the user to decide if he wants to input a token or
not. This makes a small difference in that the user is now able to
supply an empty contract token. Our different implementations (i.e.,
mock & based on u-a-c) don't handle empty tokens properly, so we now
reject empty tokens.

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-06-01 19:58:34 +02:00
parent d6f121329f
commit 329d37a19c
2 changed files with 88 additions and 15 deletions

View File

@ -79,12 +79,16 @@ class MockedUAInterfaceStrategy(UAInterfaceStrategy):
UA token. No actual query is done to the UA servers in this UA token. No actual query is done to the UA servers in this
implementation. Instead, we create a response based on the following implementation. Instead, we create a response based on the following
rules: rules:
* Empty tokens are considered invalid.
* Tokens starting with "x" will be considered expired. * Tokens starting with "x" will be considered expired.
* Tokens starting with "i" will be considered invalid. * Tokens starting with "i" will be considered invalid.
* Tokens starting with "f" will generate an internal error. * Tokens starting with "f" will generate an internal error.
""" """
await asyncio.sleep(1 / self.scale_factor) await asyncio.sleep(1 / self.scale_factor)
if not token:
raise InvalidTokenError(token)
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":
@ -118,6 +122,11 @@ class UAClientUAInterfaceStrategy(UAInterfaceStrategy):
UA token. The information will be queried using the UA client UA token. The information will be queried using the UA client
executable passed to the initializer. executable passed to the initializer.
""" """
if not token:
# u-a-c does not produce the expected output when the contract
# token is empty ; so let's not call it at all.
raise InvalidTokenError(token)
command = tuple(self.executable) + ( command = tuple(self.executable) + (
"status", "status",
"--format", "json", "--format", "json",

View File

@ -43,7 +43,11 @@ from subiquitycore.ui.container import (
) )
from subiquitycore.ui.form import ( from subiquitycore.ui.form import (
Form, Form,
SubForm,
SubFormField,
NO_HELP,
simple_field, simple_field,
RadioButtonField,
WantsToKnowFormField, WantsToKnowFormField,
) )
from subiquitycore.ui.spinner import ( from subiquitycore.ui.spinner import (
@ -62,10 +66,6 @@ from subiquitycore.ui.interactive import StringEditor
log = logging.getLogger('subiquity.ui.views.ubuntu_pro') log = logging.getLogger('subiquity.ui.views.ubuntu_pro')
ua_help = _("If you want to enroll this system using your Ubuntu Pro "
"subscription, enter your Ubuntu Pro token here. "
"Otherwise, leave this blank.")
class UATokenEditor(StringEditor, WantsToKnowFormField): class UATokenEditor(StringEditor, WantsToKnowFormField):
""" Represent a text-box editor for the Ubuntu Pro Token. """ """ Represent a text-box editor for the Ubuntu Pro Token. """
@ -87,15 +87,56 @@ class UATokenEditor(StringEditor, WantsToKnowFormField):
return super().valid_char(ch) return super().valid_char(ch)
class UbuntuProTokenForm(SubForm):
""" Represents a sub-form requesting Ubuntu Pro token.
+---------------------------------------------------------+
| Contract token: C123456789ABCDEF |
+---------------------------------------------------------+
"""
UATokenField = simple_field(UATokenEditor)
token = UATokenField(_("Contract token:"), help=NO_HELP)
class UbuntuProForm(Form): class UbuntuProForm(Form):
""" """
Represents a form requesting Ubuntu Pro information Represents a form requesting Ubuntu Pro information
+---------------------------------------------------------+
| (X) Enable now with my contract token |
| |
| Contract token: C123456789ABCDEF |
| |
| ( ) Skip Ubuntu Pro for now |
| |
| [ Done ] |
| [ Back ] |
+---------------------------------------------------------+
""" """
cancel_label = _("Back") cancel_label = _("Back")
group = []
UATokenField = simple_field(UATokenEditor) with_token = RadioButtonField(
group,
_("Enable now with my contract token"), help=NO_HELP)
token_form = SubFormField(UbuntuProTokenForm, "", help=NO_HELP)
skip_ua = RadioButtonField(
group,
_("Skip Ubuntu Pro for now"), help=NO_HELP)
token = UATokenField(_("Ubuntu Pro token:"), help=ua_help) def __init__(self, initial):
super().__init__(initial)
connect_signal(self.with_token.widget,
'change', self._toggle_token_input)
if not initial["token_form"]["token"]:
self.skip_ua.widget.state = True
self.with_token.widget.state = False
else:
self.skip_ua.widget.state = False
self.with_token.widget.state = True
def _toggle_token_input(self, sender, new_value):
self.token_form.enabled = new_value
class CheckingUAToken(WidgetWrap): class CheckingUAToken(WidgetWrap):
@ -124,17 +165,37 @@ class CheckingUAToken(WidgetWrap):
class UbuntuProView(BaseView): class UbuntuProView(BaseView):
""" Represent the view of the Ubuntu Pro configuration. """ """ Represent the view of the Ubuntu Pro configuration.
+---------------------------------------------------------+
| Enable Ubuntu Pro [ Help ] |
+---------------------------------------------------------+
| If you want to enable Ubuntu Pro, you can do it now |
| with your contract token. Otherwise, you can skip this |
| step and enable Ubuntu Pro later using the command |
| 'ua attach'. |
| |
| (X) Enable now with my contract token |
| |
| Contract token: C123456789ABCDEF |
| |
| ( ) Skip Ubuntu Pro for now |
| |
| [ Done ] |
| [ Back ] |
+---------------------------------------------------------+
"""
title = _("Enable Ubuntu Pro") title = _("Enable Ubuntu Pro")
excerpt = _("Enter your Ubuntu Pro token if you want to enroll " excerpt = _("If you want to enable Ubuntu Pro, you can do it now with"
"this system.") " your contract token. "
"Otherwise, you can skip this step and enable Ubuntu Pro"
" later using the command `ua attach`.")
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 = UbuntuProForm(initial={"token": token}) self.form = UbuntuProForm(initial={"token_form": {"token": token}})
def on_cancel(_: UbuntuProForm): def on_cancel(_: UbuntuProForm):
self.cancel() self.cancel()
@ -148,8 +209,8 @@ class UbuntuProView(BaseView):
""" 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. """
token: str = form.token.value results = form.as_data()
if token: if not results["skip_ua"]:
def on_success(services: List[UbuntuProService]) -> None: def on_success(services: List[UbuntuProService]) -> None:
self.remove_overlay() self.remove_overlay()
self.show_activable_services(services) self.show_activable_services(services)
@ -163,6 +224,7 @@ class UbuntuProView(BaseView):
elif status == UbuntuProCheckTokenStatus.UNKNOWN_ERROR: elif status == UbuntuProCheckTokenStatus.UNKNOWN_ERROR:
self.show_unknown_error() self.show_unknown_error()
token: str = results["token_form"]["token"]
checking_token_overlay = CheckingUAToken(self) checking_token_overlay = CheckingUAToken(self)
self.show_overlay(checking_token_overlay, self.show_overlay(checking_token_overlay,
width=checking_token_overlay.width, width=checking_token_overlay.width,
@ -172,7 +234,7 @@ class UbuntuProView(BaseView):
on_success=on_success, on_success=on_success,
on_failure=on_failure) on_failure=on_failure)
else: else:
self.controller.done(token) self.controller.done("")
def cancel(self) -> None: def cancel(self) -> None:
""" Called when the user presses the Back button. """ """ Called when the user presses the Back button. """
@ -326,7 +388,8 @@ class ShowServicesWidget(Stretchy):
def ok(self, sender) -> None: def ok(self, sender) -> None:
""" Close the overlay and submit the token. """ """ Close the overlay and submit the token. """
self.parent.controller.done(self.parent.form.token.value) token = self.parent.form.as_data()["token_form"]["token"]
self.parent.controller.done(token)
class ContinueAnywayWidget(Stretchy): class ContinueAnywayWidget(Stretchy):
@ -361,4 +424,5 @@ class ContinueAnywayWidget(Stretchy):
def cont(self, sender) -> None: def cont(self, sender) -> None:
""" Move on to the next screen. """ """ Move on to the next screen. """
self.parent.controller.done(self.parent.form.token.value) token = self.parent.form.as_data()["token_form"]["token"]
self.parent.controller.done(token)