diff --git a/playbooks/tasks/configure_operator.yaml b/playbooks/tasks/configure_operator.yaml index c5740945..0d70b968 100644 --- a/playbooks/tasks/configure_operator.yaml +++ b/playbooks/tasks/configure_operator.yaml @@ -147,8 +147,7 @@ --name "{{ operator.name }}" \ --version "{{ operator.version }}" \ --namespace "{{ operator.namespace }}" \ - {% for csv in (operator.csvNames | default([operator.name])) %}--csv-name "{{ csv }}" {% endfor %}\ - --no-dry-run + {% for csv in (operator.csvNames | default([operator.name])) %}--csv-name "{{ csv }}" {% endfor %} register: operator_update_result - name: Print the output diff --git a/pyproject.toml b/pyproject.toml index c5520be0..7c072fb3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -124,6 +124,9 @@ preview = true "src/tests/fixtures.py" = [ "ANN401", # anymarkup.parse returns Any; no tighter type available ] +"src/enclave/utils.py" = [ + "ANN401", # HelpGroup mirrors click.Group's *args/**kwargs interface +] [tool.ruff.lint.isort] combine-as-imports = true diff --git a/src/enclave/cli.py b/src/enclave/cli.py index ff504f08..89049b42 100644 --- a/src/enclave/cli.py +++ b/src/enclave/cli.py @@ -2,18 +2,23 @@ from enclave.reconcile.cli import cli as reconcile_cli from enclave.tools.cli import cli as tools_cli -from enclave.utils import LOG_LEVELS, configure_logging +from enclave.utils import LOG_LEVELS, HelpGroup, configure_logging -@click.group() +@click.group(cls=HelpGroup) @click.option( "--log-level", + "-l", default="INFO", type=click.Choice(LOG_LEVELS, case_sensitive=False), help="Set the logging level.", ) def cli(log_level: str) -> None: - """Enclave CLI.""" + """Management CLI for Red Hat Sovereign Enclave (RHSE). + + Provides subcommands for reconciling cluster state and running + operational tools against the management cluster. + """ configure_logging(log_level) diff --git a/src/enclave/reconcile/cli.py b/src/enclave/reconcile/cli.py index 1293c98e..232e6bc5 100644 --- a/src/enclave/reconcile/cli.py +++ b/src/enclave/reconcile/cli.py @@ -9,7 +9,7 @@ reconcile as cluster_upgrade_reconcile, ) from enclave.reconcile.operator_versions import reconcile as operator_versions_reconcile -from enclave.utils import LOG_LEVELS, configure_logging +from enclave.utils import LOG_LEVELS, HelpGroup, configure_logging def defaults_path(filename: str) -> Path: @@ -22,36 +22,49 @@ def defaults_path(filename: str) -> Path: return path -@click.group() +@click.group(cls=HelpGroup) @click.option( "--log-level", + "-l", default="INFO", type=click.Choice(LOG_LEVELS, case_sensitive=False), help="Set the logging level.", ) def cli(log_level: str) -> None: - """Reconcile CLI.""" + """Reconcile management cluster state against desired configuration. + + Commands compare the current cluster state with expected values + and apply changes to bring them in sync. + """ # Configure logging only if not already configured by the parent enclave CLI configure_logging(log_level) @cli.command() -@click.option("--name", help="Operator package name") -@click.option("--version", help="Operator version") -@click.option("--namespace", help="Operator namespace") +@click.option("--name", "-n", help="Operator package name") +@click.option("--version", "-v", help="Operator version") +@click.option("--namespace", "-N", help="Operator namespace") @click.option( "--csv-name", + "-c", "csv_names", multiple=True, help="CSV name(s); defaults to operator name if omitted", ) @click.option( "--use-defaults", + "-u", is_flag=True, default=False, help="Load all operators from defaults/operators.yaml (mutually exclusive with --name, --version, --namespace, --csv-name)", ) -@click.option("--dry-run/--no-dry-run", default=False) +@click.option( + "--dry-run", + "-d", + is_flag=True, + default=False, + help="Print what would be done without applying any changes.", +) def operator_versions( name: str, version: str, @@ -60,6 +73,7 @@ def operator_versions( use_defaults: bool, dry_run: bool, ) -> None: + """Reconcile operator CSV versions on the management cluster.""" if use_defaults and any([name, version, namespace, csv_names]): raise click.UsageError( "--use-defaults is mutually exclusive with --name, --version, --namespace, --csv-name" @@ -100,23 +114,32 @@ def operator_versions( @cli.command() @click.option( - "--version", "version", default=None, help="OpenShift version to upgrade to" + "--version", "-v", "version", default=None, help="OpenShift version to upgrade to" ) @click.option( "--use-defaults", + "-u", is_flag=True, default=False, help="Load the default version from defaults/platforms.yaml (mutually exclusive with --version)", ) -@click.option("--dry-run/--no-dry-run", default=False) +@click.option( + "--dry-run", + "-d", + is_flag=True, + default=False, + help="Print what would be done without applying any changes.", +) @click.option( "--timeout-minutes", + "-t", default=180, type=click.IntRange(min=1), help="Timeout for waiting operations in minutes (default: 180 = 3 hours)", ) @click.option( "--sleep-interval", + "-s", default=60, type=click.IntRange(min=1), help="Sleep interval between polling attempts in seconds (default: 60)", @@ -128,6 +151,11 @@ def mgmt_cluster_version( timeout_minutes: int, sleep_interval: int, ) -> None: + """Reconcile the management cluster's OpenShift version. + + Triggers an upgrade if the cluster is not already at the target + version, then waits for the rollout to complete. + """ if use_defaults and version: raise click.UsageError("--use-defaults is mutually exclusive with --version") diff --git a/src/enclave/tools/cli.py b/src/enclave/tools/cli.py index 62f584d1..e1bf9dbd 100644 --- a/src/enclave/tools/cli.py +++ b/src/enclave/tools/cli.py @@ -2,15 +2,16 @@ from enclave.tools.node_image_digests import main as collect_node_image_digests_main from enclave.tools.quay_registry_ca import main as quay_registry_ca_main +from enclave.utils import HelpGroup -@click.group() +@click.group(cls=HelpGroup) def cli() -> None: - """Enclave tools CLI.""" + """Utility tools for enclave operations.""" @cli.command("resolve-quay-registry-ca") -@click.option("--hostname", required=True, help="Quay registry route hostname.") +@click.option("--hostname", "-H", required=True, help="Quay registry route hostname.") @click.option( "--oc", default="oc", @@ -26,7 +27,7 @@ def resolve_quay_registry_ca(hostname: str, oc: str) -> None: @cli.command("collect-node-image-digests") -@click.option("--node", required=True, help="Node name.") +@click.option("--node", "-n", required=True, help="Node name.") @click.option( "--oc", default="oc", @@ -35,11 +36,13 @@ def resolve_quay_registry_ca(hostname: str, oc: str) -> None: ) @click.option( "--exclude-contains", + "-e", default=None, help="JSON array of substrings; matching digest refs are skipped.", ) @click.option( "--raw-output-file", + "-r", default=None, help="File path for raw oc debug/crictl output when no digest refs are collected.", ) diff --git a/src/enclave/utils.py b/src/enclave/utils.py index 31fdcaa8..57e68fc2 100644 --- a/src/enclave/utils.py +++ b/src/enclave/utils.py @@ -3,10 +3,30 @@ import subprocess import sys import time +from typing import Any + +import click logger = logging.getLogger(__name__) LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] +_HELP_CONTEXT = {"help_option_names": ["-h", "--help"]} + + +class HelpGroup(click.Group): + """click.Group that propagates -h/--help to every subcommand automatically.""" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + kwargs.setdefault("context_settings", _HELP_CONTEXT) + super().__init__(*args, **kwargs) + + def command(self, *args: Any, **kwargs: Any) -> Any: + kwargs.setdefault("context_settings", _HELP_CONTEXT) + return super().command(*args, **kwargs) + + def group(self, *args: Any, **kwargs: Any) -> Any: + kwargs.setdefault("context_settings", _HELP_CONTEXT) + return super().group(*args, **kwargs) def configure_logging(log_level: str) -> None: diff --git a/src/tests/test_cli.py b/src/tests/test_cli.py index 681290a6..77e0dc5b 100644 --- a/src/tests/test_cli.py +++ b/src/tests/test_cli.py @@ -8,7 +8,7 @@ def test_cli_help() -> None: result = CliRunner().invoke(cli, ["--help"]) assert result.exit_code == 0 - assert "Reconcile CLI" in result.output + assert "Reconcile management cluster state" in result.output assert "resolve-quay-registry-ca" not in result.output assert "collect-node-image-digests" not in result.output diff --git a/src/tests/test_tools_cli.py b/src/tests/test_tools_cli.py index 7ab3c28d..d4c896b1 100644 --- a/src/tests/test_tools_cli.py +++ b/src/tests/test_tools_cli.py @@ -9,7 +9,7 @@ def test_tools_cli_help() -> None: result = CliRunner().invoke(cli, ["--help"]) assert result.exit_code == 0 - assert "Enclave tools CLI" in result.output + assert "Utility tools for enclave operations" in result.output def test_resolve_quay_registry_ca_help() -> None: