Merge pull request #1342 from ogayot/u-a-c-distinguish-error-invalid

ubuntu-advantage: determine on error if token was invalid
This commit is contained in:
Olivier Gayot 2022-07-06 17:04:10 +02:00 committed by GitHub
commit 746e559c47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 20 deletions

View File

@ -13,7 +13,7 @@
# 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/>.
from subprocess import CalledProcessError, CompletedProcess
from subprocess import CompletedProcess
import unittest
from unittest.mock import patch, AsyncMock
@ -87,9 +87,9 @@ class TestUAClientUAInterfaceStrategy(unittest.IsolatedAsyncioTestCase):
mock_arun.return_value = CompletedProcess([], 0)
mock_arun.return_value.stdout = "{}"
await strategy.query_info(token="123456789")
mock_arun.assert_called_once_with(command, check=True)
mock_arun.assert_called_once_with(command, check=False)
async def test_query_info_failed(self):
async def test_query_info_unknown_error(self):
strategy = UAClientUAInterfaceStrategy()
command = (
"ubuntu-advantage",
@ -99,12 +99,42 @@ class TestUAClientUAInterfaceStrategy(unittest.IsolatedAsyncioTestCase):
)
with patch(self.arun_command_sym) as mock_arun:
mock_arun.side_effect = CalledProcessError(returncode=1,
cmd=command)
mock_arun.return_value.returncode = 2
mock_arun.return_value.stdout = "{}"
with self.assertRaises(CheckSubscriptionError):
await strategy.query_info(token="123456789")
mock_arun.assert_called_once_with(command, check=True)
mock_arun.assert_called_once_with(command, check=False)
async def test_query_info_invalid_token(self):
strategy = UAClientUAInterfaceStrategy()
command = (
"ubuntu-advantage",
"status",
"--format", "json",
"--simulate-with-token", "123456789",
)
with patch(self.arun_command_sym) as mock_arun:
mock_arun.return_value.returncode = 1
mock_arun.return_value.stdout = """\
{
"environment_vars": [],
"errors": [
{
"message": "Invalid token. See https://ubuntu.com/advantage",
"message_code": "attach-invalid-token",
"service": null,
"type": "system"
}
],
"result": "failure",
"services": [],
"warnings": []
}
"""
with self.assertRaises(InvalidTokenError):
await strategy.query_info(token="123456789")
mock_arun.assert_called_once_with(command, check=False)
async def test_query_info_invalid_json(self):
strategy = UAClientUAInterfaceStrategy()
@ -120,7 +150,7 @@ class TestUAClientUAInterfaceStrategy(unittest.IsolatedAsyncioTestCase):
mock_arun.return_value.stdout = "invalid-json"
with self.assertRaises(CheckSubscriptionError):
await strategy.query_info(token="123456789")
mock_arun.assert_called_once_with(command, check=True)
mock_arun.assert_called_once_with(command, check=False)
class TestUAInterface(unittest.IsolatedAsyncioTestCase):

View File

@ -17,9 +17,10 @@ helper. """
from abc import ABC, abstractmethod
from datetime import datetime as dt
import contextlib
import json
import logging
from subprocess import CalledProcessError, CompletedProcess
from subprocess import CompletedProcess
from typing import List, Sequence, Union
import asyncio
@ -132,20 +133,36 @@ class UAClientUAInterfaceStrategy(UAInterfaceStrategy):
"--format", "json",
"--simulate-with-token", token,
)
try:
proc: CompletedProcess = await utils.arun_command(command,
check=True)
# On error, the command will exit with status 1. When that happens, the
# output should still be formatted as a JSON object and we can inspect
# it to know the reason of the failure. This is how we figure out if
# the contract token was invalid.
proc: CompletedProcess = await utils.arun_command(command, check=False)
if proc.returncode == 0:
# TODO check if we're not returning a string or a list
return json.loads(proc.stdout)
except CalledProcessError:
try:
return json.loads(proc.stdout)
except json.JSONDecodeError:
log.exception("Failed to parse output of command %r", command)
elif proc.returncode == 1:
try:
data = json.loads(proc.stdout)
except json.JSONDecodeError:
log.exception("Failed to parse output of command %r", command)
else:
token_invalid = False
with contextlib.suppress(KeyError):
for error in data["errors"]:
if error["message_code"] == "attach-invalid-token":
token_invalid = True
log.debug("error reported by u-a-c: %s: %s",
error["message_code"], error["message"])
if token_invalid:
raise InvalidTokenError(token)
else:
log.exception("Failed to execute command %r", command)
# TODO Check if the command failed because the token is invalid.
# Currently, ubuntu-advantage fails with the following error when
# the token is invalid:
# * Failed to connect to authentication server
# * Check your Internet connection and try again.
except json.JSONDecodeError:
log.exception("Failed to parse output of command %r", command)
message = "Unable to retrieve subscription information."
raise CheckSubscriptionError(token, message=message)