Skip to content

Commit

Permalink
cli: refactor auto-attach to the new approach
Browse files Browse the repository at this point in the history
Signed-off-by: Renan Rodrigo <[email protected]>
  • Loading branch information
renanrodrigo committed Jul 3, 2024
1 parent 84a68ca commit d467ad6
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 95 deletions.
10 changes: 10 additions & 0 deletions features/help.feature
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ Feature: Pro Client help text
the token on the cli
--format \{cli,json\} output in the specified format \(default: cli\)
"""
When I run `pro auto-attach --help` as non-root
Then stdout matches regexp:
"""
usage: pro auto-attach \[flags\]
Automatically attach on an Ubuntu Pro cloud instance.
(optional arguments|options):
-h, --help show this help message and exit
"""

Examples: ubuntu release
| release | machine_type |
Expand Down
37 changes: 2 additions & 35 deletions uaclient/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,14 @@
util,
version,
)
from uaclient.api.u.pro.attach.auto.full_auto_attach.v1 import (
FullAutoAttachOptions,
_full_auto_attach,
)
from uaclient.api.u.pro.security.status.reboot_required.v1 import (
_reboot_required,
)
from uaclient.apt import AptProxyScope, setup_apt_proxy
from uaclient.cli import cli_util, fix
from uaclient.cli.api import api_command
from uaclient.cli.attach import attach_command
from uaclient.cli.auto_attach import auto_attach_command
from uaclient.cli.collect_logs import collect_logs_command
from uaclient.cli.constants import NAME, USAGE_TMPL
from uaclient.cli.disable import disable_command, perform_disable
Expand All @@ -60,6 +57,7 @@
COMMANDS = [
api_command,
attach_command,
auto_attach_command,
collect_logs_command,
disable_command,
enable_command,
Expand All @@ -84,15 +82,6 @@ def error(self, message):
self.exit(2, message + "\n")


def auto_attach_parser(parser):
"""Build or extend an arg parser for auto-attach subcommand."""
parser.prog = "auto-attach"
parser.description = messages.CLI_AUTO_ATTACH_DESC
parser.usage = USAGE_TMPL.format(name=NAME, command=parser.prog)
parser._optionals.title = messages.CLI_FLAGS
return parser


def config_show_parser(parser, parent_command: str):
"""Build or extend an arg parser for 'config show' subcommand."""
parser.usage = USAGE_TMPL.format(
Expand Down Expand Up @@ -679,22 +668,6 @@ def _detach(cfg: config.UAConfig, assume_yes: bool, json_output: bool) -> int:
return 0


@cli_util.assert_root
def action_auto_attach(args, *, cfg: config.UAConfig, **kwargs) -> int:
try:
_full_auto_attach(
FullAutoAttachOptions(),
cfg=cfg,
mode=event_logger.EventLoggerMode.CLI,
)
except exceptions.ConnectivityError:
event.info(messages.E_ATTACH_FAILURE.msg)
return 1
else:
cli_util.post_cli_attach(cfg)
return 0


def get_parser(cfg: config.UAConfig):
parser = UAArgumentParser(
prog=NAME,
Expand All @@ -721,12 +694,6 @@ def get_parser(cfg: config.UAConfig):
for command in COMMANDS:
command.register(subparsers)

parser_auto_attach = subparsers.add_parser(
"auto-attach", help=messages.CLI_ROOT_AUTO_ATTACH
)
auto_attach_parser(parser_auto_attach)
parser_auto_attach.set_defaults(action=action_auto_attach)

parser_config = subparsers.add_parser(
"config", help=messages.CLI_ROOT_CONFIG
)
Expand Down
33 changes: 33 additions & 0 deletions uaclient/cli/auto_attach.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from uaclient import event_logger, exceptions, messages
from uaclient.api.u.pro.attach.auto.full_auto_attach.v1 import (
FullAutoAttachOptions,
_full_auto_attach,
)
from uaclient.cli import cli_util
from uaclient.cli.commands import ProCommand

event = event_logger.get_event_logger()


@cli_util.assert_root
def action_auto_attach(args, *, cfg, **kwargs) -> int:
try:
_full_auto_attach(
FullAutoAttachOptions(),
cfg=cfg,
mode=event_logger.EventLoggerMode.CLI,
)
except exceptions.ConnectivityError:
event.info(messages.E_ATTACH_FAILURE.msg)
return 1
else:
cli_util.post_cli_attach(cfg)
return 0


auto_attach_command = ProCommand(
"auto-attach",
help=messages.CLI_ROOT_AUTO_ATTACH,
description=messages.CLI_AUTO_ATTACH_DESC,
action=action_auto_attach,
)
72 changes: 12 additions & 60 deletions uaclient/cli/tests/test_cli_auto_attach.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import logging
import textwrap

import mock
import pytest
Expand All @@ -9,57 +8,24 @@
from uaclient.api.u.pro.attach.auto.full_auto_attach.v1 import (
FullAutoAttachOptions,
)
from uaclient.cli import (
action_auto_attach,
auto_attach_parser,
get_parser,
main,
main_error_handler,
)
from uaclient.cli import main_error_handler
from uaclient.cli.auto_attach import auto_attach_command
from uaclient.testing import fakes

M_PATH = "uaclient.cli."
M_PATH = "uaclient.cli.auto_attach."
M_ID_PATH = "uaclient.clouds.identity."

HELP_OUTPUT = textwrap.dedent(
"""\
usage: pro auto-attach [flags]
Automatically attach on an Ubuntu Pro cloud instance.
Flags:
-h, --help show this help message and exit
"""
)


@mock.patch(M_PATH + "util.we_are_currently_root", return_value=False)
@mock.patch(M_PATH + "cli_util.util.we_are_currently_root", return_value=False)
def test_non_root_users_are_rejected(we_are_currently_root, FakeConfig):
"""Check that a UID != 0 will receive a message and exit non-zero"""

cfg = FakeConfig()
with pytest.raises(exceptions.NonRootUserError):
action_auto_attach(mock.MagicMock(), cfg=cfg)
auto_attach_command.action(mock.MagicMock(), cfg=cfg)


class TestActionAutoAttach:
@mock.patch("uaclient.log.setup_cli_logging")
@mock.patch(M_PATH + "contract.get_available_resources")
def test_auto_attach_help(
self, _m_resources, _m_setup_logging, capsys, FakeConfig
):
with pytest.raises(SystemExit):
with mock.patch(
"sys.argv", ["/usr/bin/ua", "auto-attach", "--help"]
):
with mock.patch(
"uaclient.config.UAConfig",
return_value=FakeConfig(),
):
main()
out, _err = capsys.readouterr()
assert HELP_OUTPUT == out

@mock.patch(M_PATH + "cli_util.post_cli_attach")
@mock.patch(M_PATH + "_full_auto_attach")
def test_happy_path(
Expand All @@ -70,7 +36,7 @@ def test_happy_path(
):
cfg = FakeConfig()

assert 0 == action_auto_attach(mock.MagicMock(), cfg=cfg)
assert 0 == auto_attach_command.action(mock.MagicMock(), cfg=cfg)

assert [
mock.call(
Expand Down Expand Up @@ -109,7 +75,9 @@ def test_handle_full_auto_attach_errors(
m_full_auto_attach.side_effect = (api_side_effect,)
cfg = FakeConfig()

assert expected_ret == action_auto_attach(mock.MagicMock(), cfg=cfg)
assert expected_ret == auto_attach_command.action(
mock.MagicMock(), cfg=cfg
)

assert [mock.call(expected_err)] == m_event.info.call_args_list
assert [] == m_post_cli_attach.call_args_list
Expand Down Expand Up @@ -160,26 +128,10 @@ def test_uncaught_errors_are_handled(
m_full_auto_attach.side_effect = api_side_effect
cfg = FakeConfig()
with pytest.raises(SystemExit) as excinfo:
main_error_handler(action_auto_attach)(mock.MagicMock(), cfg=cfg)
main_error_handler(auto_attach_command.action)(
mock.MagicMock(), cfg=cfg
)
assert expected_ret == excinfo.value.code
_, err = capsys.readouterr()
assert expected_err == err
assert [] == m_post_cli_attach.call_args_list


class TestParser:
@mock.patch(M_PATH + "contract.get_available_resources")
def test_auto_attach_parser_updates_parser_config(
self, _m_resources, FakeConfig
):
"""Update the parser configuration for 'auto-attach'."""
m_parser = auto_attach_parser(mock.Mock())
assert "pro auto-attach [flags]" == m_parser.usage
assert "auto-attach" == m_parser.prog
assert "Flags" == m_parser._optionals.title

full_parser = get_parser(FakeConfig())
with mock.patch("sys.argv", ["pro", "auto-attach"]):
args = full_parser.parse_args()
assert "auto-attach" == args.command
assert "action_auto_attach" == args.action.__name__

0 comments on commit d467ad6

Please sign in to comment.