Skip to content

Commit

Permalink
cli: --show-progress flag for api subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
orndorffgrant committed Mar 26, 2024
1 parent 132de03 commit 2bedc22
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 8 deletions.
76 changes: 76 additions & 0 deletions features/api_enable.feature
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,79 @@ Feature: u.pro.services.enable
Examples:
| release | machine_type |
| jammy | lxd-vm |

Scenario Outline: u.pro.services.enable.v1 with progress
Given a `<release>` `<machine_type>` machine with ubuntu-advantage-tools installed
When I run `apt-get update` with sudo
And I attach `contract_token` with sudo and options `--no-auto-enable`
# Basic enable
And I run shell command `pro api u.pro.services.enable.v1 --show-progress --args service=esm-infra` with sudo
Then stdout contains substring:
"""
{"total_steps": 3, "done_steps": 0, "previous_step_message": null, "current_step_message": "Acquiring Ubuntu Pro lock"}
{"total_steps": 3, "done_steps": 1, "previous_step_message": "Acquiring Ubuntu Pro lock", "current_step_message": "Configuring APT access to Ubuntu Pro: ESM Infra"}
{"total_steps": 3, "done_steps": 2, "previous_step_message": "Configuring APT access to Ubuntu Pro: ESM Infra", "current_step_message": "Updating Ubuntu Pro: ESM Infra package lists"}
{"total_steps": 3, "done_steps": 3, "previous_step_message": "Updating Ubuntu Pro: ESM Infra package lists", "current_step_message": null}
{"_schema_version": "v1", "data": {"attributes": {"disabled": [], "enabled": ["esm-infra"], "messages": [], "reboot_required": false}, "meta": {"environment_vars": []}, "type": "EnableService"}, "errors": [], "result": "success"
"""
# Enabling multiple services shows steps correctly
When I run shell command `pro api u.pro.services.enable.v1 --show-progress --args service=ros` with sudo
Then I will see the following on stdout:
"""
<<<<<<< HEAD
{}
=======
{"total_steps": 7, "done_steps": 0, "previous_step_message": null, "current_step_message": "Acquiring Ubuntu Pro lock"}
{"total_steps": 7, "done_steps": 1, "previous_step_message": "Acquiring Ubuntu Pro lock", "current_step_message": "Configuring APT access to Ubuntu Pro: ESM Apps"}
{"total_steps": 7, "done_steps": 2, "previous_step_message": "Configuring APT access to Ubuntu Pro: ESM Apps", "current_step_message": "Updating Ubuntu Pro: ESM Apps package lists"}
{"total_steps": 7, "done_steps": 3, "previous_step_message": "Updating Ubuntu Pro: ESM Apps package lists", "current_step_message": "Configuring APT access to ROS ESM Security Updates"}
{"total_steps": 7, "done_steps": 4, "previous_step_message": "Configuring APT access to ROS ESM Security Updates", "current_step_message": "Updating ROS ESM Security Updates package lists"}
{"total_steps": 7, "done_steps": 5, "previous_step_message": "Updating ROS ESM Security Updates package lists", "current_step_message": "Configuring APT access to ROS ESM All Updates"}
{"total_steps": 7, "done_steps": 6, "previous_step_message": "Configuring APT access to ROS ESM All Updates", "current_step_message": "Updating ROS ESM All Updates package lists"}
{"total_steps": 7, "done_steps": 7, "previous_step_message": "Updating ROS ESM All Updates package lists", "current_step_message": null}
{"_schema_version": "v1", "data": {"attributes": {"disabled": [], "enabled": ["esm-apps", "ros", "ros-updates"], "messages": [], "reboot_required": false}, "meta": {"environment_vars": []}, "type": "EnableService"}, "errors": [], "result": "success"
>>>>>>> 0b13f113 (wip: cli api progress)
"""

Examples:
| release | machine_type |
| xenial | lxd-container |

Scenario Outline: u.pro.services.enable.v1 vm services with progress
Given a `<release>` `<machine_type>` machine with ubuntu-advantage-tools installed
When I apt update
And I attach `contract_token` with sudo and options `--no-auto-enable`
And I run `pro api u.pro.services.enable.v1 --args service=livepatch --show-progress` with sudo
Then stdout contains substring:
"""
{"total_steps": 3, "done_steps": 0, "previous_step_message": null, "current_step_message": "Acquiring Ubuntu Pro lock"}
{"total_steps": 3, "done_steps": 1, "previous_step_message": "Acquiring Ubuntu Pro lock", "current_step_message": "Installing Livepatch"}
{"total_steps": 3, "done_steps": 2, "previous_step_message": "Installing Livepatch", "current_step_message": "Setting up Livepatch"}
{"total_steps": 3, "done_steps": 3, "previous_step_message": "Setting up Livepatch", "current_step_message": null}
{"_schema_version": "v1", "data": {"attributes": {"disabled": [], "enabled": ["livepatch"], "messages": [], "reboot_required": false}, "meta": {"environment_vars": []}, "type": "EnableService"}, "errors": [], "result": "success"
"""
# disables incompatible services and variant works
When I run `pro api u.pro.services.enable.v1 --show-progress --data '{"service": "realtime-kernel", "variant": "intel-iotg"}'` with sudo
Then stdout contains substring:
"""
{"total_steps": 5, "done_steps": 0, "previous_step_message": null, "current_step_message": "Acquiring Ubuntu Pro lock"}
{"total_steps": 5, "done_steps": 1, "previous_step_message": "Acquiring Ubuntu Pro lock", "current_step_message": "Disabling incompatible service: Livepatch"}
{"total_steps": 5, "done_steps": 2, "previous_step_message": "Disabling incompatible service: Livepatch", "current_step_message": "Configuring APT access to Real-time Intel IOTG Kernel"}
{"total_steps": 5, "done_steps": 3, "previous_step_message": "Configuring APT access to Real-time Intel IOTG Kernel", "current_step_message": "Updating Real-time Intel IOTG Kernel package lists"}
{"total_steps": 5, "done_steps": 4, "previous_step_message": "Updating Real-time Intel IOTG Kernel package lists", "current_step_message": "Installing Real-time Intel IOTG Kernel packages"}
{"total_steps": 5, "done_steps": 5, "previous_step_message": "Installing Real-time Intel IOTG Kernel packages", "current_step_message": null}
{"_schema_version": "v1", "data": {"attributes": {"disabled": ["livepatch"], "enabled": ["realtime-kernel"], "messages": [], "reboot_required": true}, "meta": {"environment_vars": []}, "type": "EnableService"}, "errors": [], "result": "success"
"""
When I run `pro api u.pro.status.enabled_services.v1` with sudo
Then API data field output matches regexp:
"""
\s*{
\s* "name": "realtime-kernel",
\s* "variant_enabled": true,
\s* "variant_name": "intel-iotg"
\s*}
"""

Examples:
| release | machine_type |
| jammy | lxd-vm |
24 changes: 19 additions & 5 deletions uaclient/api/api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import json
from importlib import import_module
from typing import Any, Callable, Dict, List, Tuple
from typing import Any, Callable, Dict, List, Optional, Tuple

from uaclient.api import errors
from uaclient.api import AbstractProgress, errors
from uaclient.api.data_types import APIData, APIResponse, ErrorWarningObject
from uaclient.config import UAConfig
from uaclient.data_types import IncorrectFieldTypeError
Expand Down Expand Up @@ -91,7 +91,11 @@ def _process_data(


def call_api(
endpoint_path: str, options: List[str], data: str, cfg: UAConfig
endpoint_path: str,
options: List[str],
data: str,
cfg: UAConfig,
progress_object: Optional[AbstractProgress] = None,
) -> APIResponse:

if endpoint_path not in VALID_ENDPOINTS:
Expand Down Expand Up @@ -125,7 +129,12 @@ def call_api(
)

try:
result = endpoint.fn(options, cfg)
if endpoint.supports_progress:
result = endpoint.fn(
options, cfg, progress_object=progress_object
)
else:
result = endpoint.fn(options, cfg)
except Exception as e:
return errors.error_out(e)

Expand All @@ -135,7 +144,10 @@ def call_api(
errors.APINoArgsForEndpoint(endpoint=endpoint_path)
)
try:
result = endpoint.fn(cfg)
if endpoint.supports_progress:
result = endpoint.fn(cfg, progress_object=progress_object)
else:
result = endpoint.fn(cfg)
except Exception as e:
return errors.error_out(e)

Expand Down Expand Up @@ -169,8 +181,10 @@ def __init__(
name: str,
fn: Callable,
options_cls,
supports_progress: bool = False,
):
self.version = version
self.name = name
self.fn = fn
self.options_cls = options_cls
self.supports_progress = supports_progress
1 change: 1 addition & 0 deletions uaclient/api/u/pro/services/enable/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,5 @@ def _enable(
name="EnableService",
fn=_enable,
options_cls=EnableOptions,
supports_progress=True,
)
40 changes: 37 additions & 3 deletions uaclient/cli/cli_api.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
from uaclient import exceptions, messages
from uaclient.api import api
import json
from collections import OrderedDict
from typing import Optional

from uaclient import exceptions, messages, util
from uaclient.api import AbstractProgress
from uaclient.api.api import call_api


class CLIAPIProgress(AbstractProgress):
def progress(
self,
*,
total_steps: int,
done_steps: int,
previous_step_message: Optional[str],
current_step_message: Optional[str]
):
d = OrderedDict()
d["total_steps"] = total_steps
d["done_steps"] = done_steps
d["previous_step_message"] = previous_step_message
d["current_step_message"] = current_step_message
print(json.dumps(d))


def action_api(args, *, cfg, **kwargs):
if args.options and args.data:
raise exceptions.CLIAPIOptionsXORData()

result = api.call_api(args.endpoint_path, args.options, args.data, cfg)
if args.show_progress:
progress = CLIAPIProgress()
else:
progress = None

result = call_api(
args.endpoint_path, args.options, args.data, cfg, progress
)
print(result.to_json())
return 0 if result.result == "success" else 1

Expand All @@ -19,6 +48,11 @@ def add_parser(subparsers, cfg):
parser.add_argument(
"endpoint_path", metavar="endpoint", help=messages.CLI_API_ENDPOINT
)
parser.add_argument(
"--show-progress",
action="store_true",
help=messages.CLI_API_SHOW_PROGRESS,
)
parser.add_argument(
"--args",
dest="options",
Expand Down
4 changes: 4 additions & 0 deletions uaclient/messages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,10 @@ class TxtColor:

CLI_API_DESC = t.gettext("Calls the Client API endpoints.")
CLI_API_ENDPOINT = t.gettext("API endpoint to call")
CLI_API_SHOW_PROGRESS = t.gettext(
"For endpoints that support progress updates, show each progress update "
"on a new line in JSON format"
)
CLI_API_ARGS = t.gettext(
"Options to pass to the API endpoint, formatted as key=value"
)
Expand Down

0 comments on commit 2bedc22

Please sign in to comment.