1
0
Fork 0
operator-repo/src/operator_repo/cli.py

201 lines
6.2 KiB
Python
Raw Normal View History

2023-08-03 08:52:14 +00:00
#!/usr/bin/env python3
"""
CLI tool to handle operator repositories
"""
import argparse
import logging
import sys
from collections.abc import Iterator
from itertools import chain
2023-08-03 08:52:14 +00:00
from pathlib import Path
2023-08-17 17:57:35 +00:00
from typing import Optional, Union
2023-08-03 08:52:14 +00:00
from .checks import get_checks, run_suite
2023-08-07 09:17:56 +00:00
from .classes import Bundle, Operator, Repo
from .exceptions import OperatorRepoException
2023-08-03 08:52:14 +00:00
def parse_target(repo: Repo, target: str) -> Union[Operator, Bundle]:
if "/" in target:
operator_name, operator_version = target.split("/", 1)
return repo.operator(operator_name).bundle(operator_version)
return repo.operator(target)
def indent(depth: int) -> str:
return " " * depth
2023-08-12 16:01:30 +00:00
def show_repo(repo: Repo, recursive: bool = False, depth: int = 0) -> None:
print(indent(depth) + str(repo))
for operator in repo:
if recursive:
show_operator(operator, recursive, depth + 1)
else:
print(indent(depth + 1) + str(operator))
def show_operator(operator: Operator, recursive: bool = False, depth: int = 0) -> None:
print(indent(depth) + str(operator))
for bundle in operator:
if recursive:
show_bundle(bundle, depth + 1)
2023-08-12 16:01:30 +00:00
else:
print(indent(depth + 1) + str(bundle))
def show_bundle(bundle: Bundle, depth: int = 0) -> None:
2023-08-12 16:01:30 +00:00
print(indent(depth) + str(bundle))
csv_annotations = bundle.csv.get("metadata", {}).get("annotations", {})
info = [
("Description", csv_annotations.get("description", "")),
("Name", f"{bundle.csv_operator_name}.v{bundle.csv_operator_version}"),
("Channels", ", ".join(bundle.channels)),
("Default channel", bundle.default_channel),
("Container image", csv_annotations.get("containerImage", "")),
("Replaces", bundle.csv.get("spec", {}).get("replaces", "")),
("Skips", bundle.csv.get("spec", {}).get("skips", [])),
]
max_width = max(len(key) for key, _ in info)
for key, value in info:
message = f"{key.ljust(max_width + 1)}: {value}"
print(indent(depth + 1) + message)
def show(target: Union[Repo, Operator, Bundle], recursive: bool = False) -> None:
2023-08-03 08:52:14 +00:00
if isinstance(target, Repo):
2023-08-12 16:01:30 +00:00
show_repo(target, recursive, 0)
2023-08-03 08:52:14 +00:00
elif isinstance(target, Operator):
2023-08-12 16:01:30 +00:00
show_operator(target, recursive, 1 * recursive)
2023-08-03 08:52:14 +00:00
elif isinstance(target, Bundle):
show_bundle(target, 2 * recursive)
2023-08-03 08:52:14 +00:00
2023-08-18 10:44:43 +00:00
def action_list(repo: Repo, *what: str, recursive: bool = False) -> None:
2023-08-12 16:01:30 +00:00
if what:
2023-08-18 10:44:43 +00:00
targets: Iterator[Union[Repo, Operator, Bundle]] = (
parse_target(repo, x) for x in what
)
2023-08-03 08:52:14 +00:00
else:
2023-08-18 10:44:43 +00:00
targets = iter([repo])
2023-08-12 16:01:30 +00:00
for target in targets:
show(target, recursive)
2023-08-03 08:52:14 +00:00
2023-08-18 15:45:28 +00:00
def _walk(target: Union[Operator, Bundle]) -> Iterator[Union[Operator, Bundle]]:
yield target
2023-08-18 15:45:28 +00:00
if isinstance(target, Operator):
yield from target.all_bundles()
2023-08-18 10:44:43 +00:00
def action_check(repo: Repo, suite: str, *what: str, recursive: bool = False) -> None:
2023-08-12 16:56:18 +00:00
if what:
2023-08-18 15:45:28 +00:00
targets: Iterator[Union[Operator, Bundle]] = (
2023-08-18 10:44:43 +00:00
parse_target(repo, x) for x in what
)
2023-08-12 16:56:18 +00:00
else:
targets = repo.all_operators()
if recursive:
2023-08-18 15:45:28 +00:00
all_targets: Iterator[Union[Operator, Bundle]] = chain.from_iterable(
2023-08-18 10:44:43 +00:00
_walk(x) for x in targets
)
else:
2023-08-12 16:56:18 +00:00
all_targets = targets
for result in run_suite(all_targets, suite_name=suite):
print(result)
2023-08-03 08:52:14 +00:00
def action_check_list(suite: str) -> None:
for check_type_name, checks in get_checks(suite).items():
print(f"{check_type_name} checks:")
for check in checks:
display_name = check.__name__.removeprefix("check_")
print(f" - {display_name}: {check.__doc__}")
2023-08-18 15:45:28 +00:00
def _get_repo(path: Optional[Path] = None) -> Repo:
if not path:
path = Path.cwd()
try:
return Repo(path)
except OperatorRepoException:
print(f"{path} is not a valid operator repository")
sys.exit(1)
2023-08-03 08:52:14 +00:00
def main() -> None:
main_parser = argparse.ArgumentParser(
description="Operator repository manipulation tool",
)
main_parser.add_argument(
"-r", "--repo", help="path to the root of the operator repository", type=Path
)
main_parser.add_argument(
"-v", "--verbose", action="count", default=0, help="increase log verbosity"
)
main_subparsers = main_parser.add_subparsers(dest="action")
# list
list_parser = main_subparsers.add_parser(
2023-08-03 17:23:45 +00:00
"list", aliases=["ls"], help="list contents of repo, operators or bundles"
2023-08-03 08:52:14 +00:00
)
list_parser.add_argument(
"-R", "--recursive", action="store_true", help="descend the tree"
)
list_parser.add_argument(
"target",
nargs="*",
help="name of the repos or bundles to list; if omitted, list the contents of the repo",
)
# check_bundle
check_parser = main_subparsers.add_parser(
2023-08-03 17:23:45 +00:00
"check",
help="check validity of an operator or bundle",
)
check_parser.add_argument(
"-s", "--suite", default="operator_repo.checks", help="check suite to use"
)
check_parser.add_argument(
"-l", "--list", action="store_true", help="list available checks"
)
2023-08-03 17:23:45 +00:00
check_parser.add_argument(
"-R", "--recursive", action="store_true", help="descend the tree"
2023-08-03 08:52:14 +00:00
)
check_parser.add_argument(
2023-08-03 08:52:14 +00:00
"target",
nargs="*",
help="name of the operators or bundles to check",
2023-08-03 08:52:14 +00:00
)
args = main_parser.parse_args()
verbosity = {0: logging.ERROR, 1: logging.WARNING, 2: logging.INFO}
log = logging.getLogger(__package__)
log.setLevel(verbosity.get(args.verbose, logging.DEBUG))
handler = logging.StreamHandler()
handler.setFormatter(
logging.Formatter("%(asctime)s %(name)-12s %(levelname)-8s %(message)s")
)
log.addHandler(handler)
2023-08-03 17:23:45 +00:00
if args.action in ("list", "ls"):
action_list(_get_repo(args.repo), *args.target, recursive=args.recursive)
elif args.action == "check":
if args.list:
action_check_list(args.suite)
else:
action_check(
_get_repo(args.repo),
args.suite,
*args.target,
recursive=args.recursive,
)
2023-08-03 08:52:14 +00:00
else:
main_parser.print_help()
2023-08-18 15:45:28 +00:00
if __name__ == "__main__": # pragma: no cover
2023-08-03 08:52:14 +00:00
main()