operator-repo/src/operator_repo/cli.py

192 lines
6.0 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
from collections.abc import Iterator
from itertools import chain
2023-08-03 08:52:14 +00:00
from pathlib import Path
from typing import 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
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, recursive, depth + 1)
else:
print(indent(depth + 1) + str(bundle))
def show_bundle(bundle: Bundle, recursive: bool = False, depth: int = 0) -> None:
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):
2023-08-12 16:01:30 +00:00
show_bundle(target, recursive, 2 * recursive)
2023-08-03 08:52:14 +00:00
def action_list(repo_path, *what: str, recursive: bool = False) -> None:
repo = Repo(repo_path)
2023-08-12 16:01:30 +00:00
if what:
targets = (parse_target(repo, x) for x in what)
2023-08-03 08:52:14 +00:00
else:
2023-08-12 16:01:30 +00:00
targets = [repo]
for target in targets:
show(target, recursive)
2023-08-03 08:52:14 +00:00
def _walk(
target: Union[Repo, Operator, Bundle]
) -> Iterator[Union[Repo, Operator, Bundle]]:
yield target
if isinstance(target, Repo):
for operator in target:
yield from _walk(operator)
elif isinstance(target, Operator):
yield from target.all_bundles()
def action_check(
repo_path: Path, suite: str, *what: str, recursive: bool = False
) -> None:
repo = Repo(repo_path)
if recursive:
if what:
targets = chain(_walk(parse_target(repo, x)) for x in what)
else:
targets = chain(_walk(x) for x in repo)
else:
targets = [parse_target(repo, x) for x in what] or repo.all_operators()
for result in run_suite(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-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()
# print(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"):
2023-08-03 08:52:14 +00:00
action_list(args.repo or Path.cwd(), *args.target, recursive=args.recursive)
elif args.action == "check":
if args.list:
action_check_list(args.suite)
else:
action_check(
args.repo or Path.cwd(),
args.suite,
*args.target,
recursive=args.recursive,
)
2023-08-03 08:52:14 +00:00
else:
main_parser.print_help()
if __name__ == "__main__":
main()