Merge pull request #1340 from ogayot/ubuntu-pro-subscription-information
ubuntu-pro: add subscription info in response to /ubuntu_pro/check_token
This commit is contained in:
commit
a9b206dedd
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"version": "27.4.2~21.10.1",
|
||||
"effective": null,
|
||||
"contract": {
|
||||
"name": "UA Apps - Essential (Virtual)"
|
||||
},
|
||||
"account": {
|
||||
"name": "user@domain.com"
|
||||
},
|
||||
"expires": "2010-12-31T00:00:00+00:00",
|
||||
"services": [
|
||||
{
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
"version": "27.4.2~21.10.1",
|
||||
"effective": null,
|
||||
"expires": "2035-12-31T00:00:00+00:00",
|
||||
"contract": {
|
||||
"name": "UA Apps - Essential (Virtual)"
|
||||
},
|
||||
"account": {
|
||||
"name": "user@domain.com"
|
||||
},
|
||||
"services": [
|
||||
{
|
||||
"name": "cis",
|
||||
|
|
|
@ -70,7 +70,7 @@ class UbuntuProController(SubiquityTuiController):
|
|||
async def inner() -> None:
|
||||
answer = await self.endpoint.check_token.GET(token)
|
||||
if answer.status == TokenStatus.VALID_TOKEN:
|
||||
on_success(answer.services)
|
||||
on_success(answer.subscription.services)
|
||||
else:
|
||||
on_failure(answer.status)
|
||||
|
||||
|
|
|
@ -471,11 +471,18 @@ class UbuntuProService:
|
|||
auto_enabled: bool
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class UbuntuProSubscription:
|
||||
contract_name: str
|
||||
account_name: str
|
||||
services: List[UbuntuProService]
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class UbuntuProCheckTokenAnswer:
|
||||
status: UbuntuProCheckTokenStatus
|
||||
|
||||
services: Optional[List[UbuntuProService]]
|
||||
subscription: Optional[UbuntuProSubscription]
|
||||
|
||||
|
||||
class ShutdownMode(enum.Enum):
|
||||
|
|
|
@ -122,13 +122,12 @@ class UbuntuProController(SubiquityController):
|
|||
async def check_token_GET(self, token: str) \
|
||||
-> UbuntuProCheckTokenAnswer:
|
||||
""" Handle a GET request asking whether the contract token is valid or
|
||||
not. If it is valid, we provide the list of activable services
|
||||
associated with the subscription.
|
||||
not. If it is valid, we provide the information about the subscription.
|
||||
"""
|
||||
services = None
|
||||
subscription = None
|
||||
try:
|
||||
services = await \
|
||||
self.ua_interface.get_activable_services(token=token)
|
||||
subscription = await \
|
||||
self.ua_interface.get_subscription(token=token)
|
||||
except InvalidTokenError:
|
||||
status = UbuntuProCheckTokenStatus.INVALID_TOKEN
|
||||
except ExpiredTokenError:
|
||||
|
@ -138,4 +137,5 @@ class UbuntuProController(SubiquityController):
|
|||
else:
|
||||
status = UbuntuProCheckTokenStatus.VALID_TOKEN
|
||||
|
||||
return UbuntuProCheckTokenAnswer(status=status, services=services)
|
||||
return UbuntuProCheckTokenAnswer(status=status,
|
||||
subscription=subscription)
|
||||
|
|
|
@ -125,30 +125,36 @@ class TestUAClientUAInterfaceStrategy(unittest.IsolatedAsyncioTestCase):
|
|||
|
||||
class TestUAInterface(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
async def test_mocked_get_activable_services(self):
|
||||
async def test_mocked_get_subscription(self):
|
||||
strategy = MockedUAInterfaceStrategy(scale_factor=1_000_000)
|
||||
interface = UAInterface(strategy)
|
||||
|
||||
with self.assertRaises(InvalidTokenError):
|
||||
await interface.get_activable_services(token="invalidToken")
|
||||
await interface.get_subscription(token="invalidToken")
|
||||
# Tokens starting with "f" in dry-run mode simulate an "internal"
|
||||
# error.
|
||||
with self.assertRaises(CheckSubscriptionError):
|
||||
await interface.get_activable_services(token="failure")
|
||||
await interface.get_subscription(token="failure")
|
||||
|
||||
# Tokens starting with "x" is dry-run mode simulate an expired token.
|
||||
with self.assertRaises(ExpiredTokenError):
|
||||
await interface.get_activable_services(token="xpiredToken")
|
||||
await interface.get_subscription(token="xpiredToken")
|
||||
|
||||
# Other tokens are considered valid in dry-run mode.
|
||||
await interface.get_activable_services(token="validToken")
|
||||
await interface.get_subscription(token="validToken")
|
||||
|
||||
async def test_get_activable_services(self):
|
||||
async def test_get_subscription(self):
|
||||
# We use the standard strategy but don't actually run it
|
||||
strategy = UAClientUAInterfaceStrategy()
|
||||
interface = UAInterface(strategy)
|
||||
|
||||
subscription = {
|
||||
status = {
|
||||
"account": {
|
||||
"name": "user@domain.com",
|
||||
},
|
||||
"contract": {
|
||||
"name": "UA Apps - Essential (Virtual)",
|
||||
},
|
||||
"expires": "2035-12-31T00:00:00+00:00",
|
||||
"services": [
|
||||
{
|
||||
|
@ -183,30 +189,30 @@ class TestUAInterface(unittest.IsolatedAsyncioTestCase):
|
|||
},
|
||||
]
|
||||
}
|
||||
interface.get_subscription = AsyncMock(return_value=subscription)
|
||||
services = await interface.get_activable_services(token="XXX")
|
||||
interface.get_subscription_status = AsyncMock(return_value=status)
|
||||
subscription = await interface.get_subscription(token="XXX")
|
||||
|
||||
self.assertIn(UbuntuProService(
|
||||
name="esm-infra",
|
||||
description="UA Infra: Extended Security Maintenance (ESM)",
|
||||
auto_enabled=True,
|
||||
), services)
|
||||
), subscription.services)
|
||||
self.assertIn(UbuntuProService(
|
||||
name="fips",
|
||||
description="NIST-certified core packages",
|
||||
auto_enabled=False,
|
||||
), services)
|
||||
), subscription.services)
|
||||
self.assertNotIn(UbuntuProService(
|
||||
name="esm-apps",
|
||||
description="UA Apps: Extended Security Maintenance (ESM)",
|
||||
auto_enabled=True,
|
||||
), services)
|
||||
), subscription.services)
|
||||
self.assertNotIn(UbuntuProService(
|
||||
name="cis",
|
||||
description="Center for Internet Security Audit Tools",
|
||||
auto_enabled=False,
|
||||
), services)
|
||||
), subscription.services)
|
||||
|
||||
# Test with "Z" suffix for the expiration date.
|
||||
subscription["expires"] = "2035-12-31T00:00:00Z"
|
||||
services = await interface.get_activable_services(token="XXX")
|
||||
status["expires"] = "2035-12-31T00:00:00Z"
|
||||
subscription = await interface.get_subscription(token="XXX")
|
||||
|
|
|
@ -23,7 +23,10 @@ from subprocess import CalledProcessError, CompletedProcess
|
|||
from typing import List, Sequence, Union
|
||||
import asyncio
|
||||
|
||||
from subiquity.common.types import UbuntuProService
|
||||
from subiquity.common.types import (
|
||||
UbuntuProSubscription,
|
||||
UbuntuProService,
|
||||
)
|
||||
from subiquitycore import utils
|
||||
|
||||
|
||||
|
@ -144,16 +147,16 @@ class UAInterface:
|
|||
def __init__(self, strategy: UAInterfaceStrategy):
|
||||
self.strategy = strategy
|
||||
|
||||
async def get_subscription(self, token: str) -> dict:
|
||||
async def get_subscription_status(self, token: str) -> dict:
|
||||
""" Return a dictionary containing the subscription information. """
|
||||
return await self.strategy.query_info(token)
|
||||
|
||||
async def get_activable_services(self, token: str) \
|
||||
-> List[UbuntuProService]:
|
||||
""" Return a list of activable services (i.e. services that are
|
||||
entitled to the subscription and available on the current hardware).
|
||||
async def get_subscription(self, token: str) -> UbuntuProSubscription:
|
||||
""" Return the name of the contract, the name of the account and the
|
||||
list of activable services (i.e. services that are entitled to the
|
||||
subscription and available on the current hardware).
|
||||
"""
|
||||
info = await self.get_subscription(token)
|
||||
info = await self.get_subscription_status(token)
|
||||
|
||||
# Sometimes, a time zone offset of 0 is replaced by the letter Z. This
|
||||
# is specified in RFC 3339 but not supported by fromisoformat.
|
||||
|
@ -183,4 +186,8 @@ class UAInterface:
|
|||
if not is_activable_service(service):
|
||||
continue
|
||||
activable_services.append(service_from_dict(service))
|
||||
return activable_services
|
||||
|
||||
return UbuntuProSubscription(
|
||||
account_name=info["account"]["name"],
|
||||
contract_name=info["contract"]["name"],
|
||||
services=activable_services)
|
||||
|
|
Loading…
Reference in New Issue