diff --git a/otava/config.py b/otava/config.py index 126fe0ea..d9cd9866 100644 --- a/otava/config.py +++ b/otava/config.py @@ -182,6 +182,14 @@ def add_service_option_groups(parser) -> None: BigQueryConfig.add_parser_args(parser.add_argument_group('BigQuery Options', 'Options for BigQuery configuration')) +def create_subparser_parent() -> configargparse.ArgumentParser: + """Create a parent parser for subparsers that accepts --config-file and service options.""" + parent = configargparse.ArgumentParser(add_help=False) + parent.add_argument('--config-file', help='Otava config file path', env_var="OTAVA_CONFIG") + add_service_option_groups(parent) + return parent + + def create_config_parser() -> configargparse.ArgumentParser: parser = configargparse.ArgumentParser( add_help=False, diff --git a/otava/main.py b/otava/main.py index a0171237..8748ae27 100644 --- a/otava/main.py +++ b/otava/main.py @@ -457,33 +457,35 @@ def create_otava_cli_parser() -> argparse.ArgumentParser: allow_abbrev=False, # required for correct parsing of nested values from config file ) + subparser_parent = config.create_subparser_parent() + subparsers = parser.add_subparsers(dest="command") list_tests_parser = subparsers.add_parser( "list-tests", help="list available tests", + parents=[subparser_parent], ) - config.add_service_option_groups(list_tests_parser) list_tests_parser.add_argument("group", help="name of the group of the tests", nargs="*") list_metrics_parser = subparsers.add_parser( "list-metrics", help="list available metrics for a test", + parents=[subparser_parent], ) - config.add_service_option_groups(list_metrics_parser) list_metrics_parser.add_argument("test", help="name of the test") - list_groups_parser = subparsers.add_parser( + subparsers.add_parser( "list-groups", help="list available groups of tests", + parents=[subparser_parent], ) - config.add_service_option_groups(list_groups_parser) analyze_parser = subparsers.add_parser( "analyze", help="analyze performance test results", + parents=[subparser_parent], ) analyze_parser.add_argument("tests", help="name of the test or group of the tests", nargs="+") - config.add_service_option_groups(analyze_parser) analyze_parser.add_argument( "--update-grafana", help="Update Grafana dashboards with appropriate annotations of change points", @@ -523,8 +525,8 @@ def create_otava_cli_parser() -> argparse.ArgumentParser: remove_annotations_parser = subparsers.add_parser( "remove-annotations", + parents=[subparser_parent], ) - config.add_service_option_groups(remove_annotations_parser) remove_annotations_parser.add_argument( "tests", help="name of the test or test group", nargs="*" ) @@ -532,11 +534,11 @@ def create_otava_cli_parser() -> argparse.ArgumentParser: "--force", help="don't ask questions, just do it", dest="force", action="store_true" ) - validate_parser = subparsers.add_parser( + subparsers.add_parser( "validate", help="validates the tests and metrics defined in the configuration", + parents=[subparser_parent], ) - config.add_service_option_groups(validate_parser) return parser diff --git a/tests/cli_help_test.py b/tests/cli_help_test.py index c89724ac..a0dfa428 100644 --- a/tests/cli_help_test.py +++ b/tests/cli_help_test.py @@ -133,70 +133,25 @@ def test_otava_analyze_help_output(): f"Expected exit code 0, got {result.returncode}. stderr:\n{result.stderr}" ) - # Python 3.13+ formats usage lines and option aliases differently + # Python 3.13+ formats mutually exclusive group usage and option aliases differently if IS_PYTHON_313_PLUS: - usage_and_options = """\ -usage: otava analyze [-h] [--graphite-url GRAPHITE_URL] [--grafana-url GRAFANA_URL] - [--grafana-user GRAFANA_USER] [--grafana-password GRAFANA_PASSWORD] - [--slack-token SLACK_TOKEN] [--postgres-hostname POSTGRES_HOSTNAME] - [--postgres-port POSTGRES_PORT] [--postgres-username POSTGRES_USERNAME] - [--postgres-password POSTGRES_PASSWORD] - [--postgres-database POSTGRES_DATABASE] - [--bigquery-project-id BIGQUERY_PROJECT_ID] - [--bigquery-dataset BIGQUERY_DATASET] - [--bigquery-credentials BIGQUERY_CREDENTIALS] [--update-grafana] - [--update-postgres] [--update-bigquery] - [--notify-slack NOTIFY_SLACK [NOTIFY_SLACK ...]] [--cph-report-since DATE] - [--output {log,json,regressions_only}] [--branch [STRING]] [--metrics LIST] + usage_filter_lines = """\ [--attrs LIST] [--since-commit STRING | --since-version STRING | - --since DATE] [--until-commit STRING | --until-version STRING | --until DATE] - [--last COUNT] [-P, --p-value PVALUE] [-M MAGNITUDE] [--window WINDOW] - [--orig-edivisive ORIG_EDIVISIVE] - tests [tests ...] - -positional arguments: - tests name of the test or group of the tests - -options: - -h, --help show this help message and exit - --update-grafana Update Grafana dashboards with appropriate annotations of change points - --update-postgres Update PostgreSQL database results with change points - --update-bigquery Update BigQuery database results with change points - --notify-slack NOTIFY_SLACK [NOTIFY_SLACK ...] - Send notification containing a summary of change points to given Slack - channels - --cph-report-since DATE - Sets a limit on the date range of the Change Point History reported to - Slack. Same syntax as --since. - --output {log,json,regressions_only} - Output format for the generated report. - --branch [STRING] name of the branch [env var: BRANCH] - --metrics LIST a comma-separated list of metrics to analyze - --attrs LIST a comma-separated list of attribute names associated with the runs (e.g. - commit, branch, version); if not specified, it will be automatically - filled based on available information - --since-commit STRING - the commit at the start of the time span to analyze - --since-version STRING - the version at the start of the time span to analyze - --since DATE the start of the time span to analyze; accepts ISO, and human-readable - dates like '10 weeks ago' - --until-commit STRING - the commit at the end of the time span to analyze - --until-version STRING - the version at the end of the time span to analyze - --until DATE the end of the time span to analyze; same syntax as --since - --last COUNT the number of data points to take from the end of the series - -P, --p-value PVALUE maximum accepted P-value of a change-point; P denotes the probability that - the change-point has been found by a random coincidence, rather than a - real difference between the data distributions - -M, --magnitude MAGNITUDE""" + --since DATE] [--until-commit STRING | --until-version STRING | --until DATE]""" + magnitude_option = " -M, --magnitude MAGNITUDE" else: - usage_and_options = """\ -usage: otava analyze [-h] [--graphite-url GRAPHITE_URL] [--grafana-url GRAFANA_URL] - [--grafana-user GRAFANA_USER] [--grafana-password GRAFANA_PASSWORD] - [--slack-token SLACK_TOKEN] [--postgres-hostname POSTGRES_HOSTNAME] - [--postgres-port POSTGRES_PORT] [--postgres-username POSTGRES_USERNAME] + usage_filter_lines = """\ + [--attrs LIST] + [--since-commit STRING | --since-version STRING | --since DATE] + [--until-commit STRING | --until-version STRING | --until DATE]""" + magnitude_option = " -M MAGNITUDE, --magnitude MAGNITUDE" + + usage_and_options = f"""\ +usage: otava analyze [-h] [--config-file CONFIG_FILE] [--graphite-url GRAPHITE_URL] + [--grafana-url GRAFANA_URL] [--grafana-user GRAFANA_USER] + [--grafana-password GRAFANA_PASSWORD] [--slack-token SLACK_TOKEN] + [--postgres-hostname POSTGRES_HOSTNAME] [--postgres-port POSTGRES_PORT] + [--postgres-username POSTGRES_USERNAME] [--postgres-password POSTGRES_PASSWORD] [--postgres-database POSTGRES_DATABASE] [--bigquery-project-id BIGQUERY_PROJECT_ID] @@ -204,10 +159,8 @@ def test_otava_analyze_help_output(): [--bigquery-credentials BIGQUERY_CREDENTIALS] [--update-grafana] [--update-postgres] [--update-bigquery] [--notify-slack NOTIFY_SLACK [NOTIFY_SLACK ...]] [--cph-report-since DATE] - [--output {log,json,regressions_only}] [--branch [STRING]] [--metrics LIST] - [--attrs LIST] - [--since-commit STRING | --since-version STRING | --since DATE] - [--until-commit STRING | --until-version STRING | --until DATE] + [--output {{log,json,regressions_only}}] [--branch [STRING]] [--metrics LIST] +{usage_filter_lines} [--last COUNT] [-P, --p-value PVALUE] [-M MAGNITUDE] [--window WINDOW] [--orig-edivisive ORIG_EDIVISIVE] tests [tests ...] @@ -217,6 +170,8 @@ def test_otava_analyze_help_output(): options: -h, --help show this help message and exit + --config-file CONFIG_FILE + Otava config file path [env var: OTAVA_CONFIG] --update-grafana Update Grafana dashboards with appropriate annotations of change points --update-postgres Update PostgreSQL database results with change points --update-bigquery Update BigQuery database results with change points @@ -226,7 +181,7 @@ def test_otava_analyze_help_output(): --cph-report-since DATE Sets a limit on the date range of the Change Point History reported to Slack. Same syntax as --since. - --output {log,json,regressions_only} + --output {{log,json,regressions_only}} Output format for the generated report. --branch [STRING] name of the branch [env var: BRANCH] --metrics LIST a comma-separated list of metrics to analyze @@ -248,7 +203,7 @@ def test_otava_analyze_help_output(): -P, --p-value PVALUE maximum accepted P-value of a change-point; P denotes the probability that the change-point has been found by a random coincidence, rather than a real difference between the data distributions - -M MAGNITUDE, --magnitude MAGNITUDE""" +{magnitude_option}""" assert ( result.stdout @@ -324,10 +279,11 @@ def test_otava_list_tests_help_output(): assert ( result.stdout == """\ -usage: otava list-tests [-h] [--graphite-url GRAPHITE_URL] [--grafana-url GRAFANA_URL] - [--grafana-user GRAFANA_USER] [--grafana-password GRAFANA_PASSWORD] - [--slack-token SLACK_TOKEN] [--postgres-hostname POSTGRES_HOSTNAME] - [--postgres-port POSTGRES_PORT] [--postgres-username POSTGRES_USERNAME] +usage: otava list-tests [-h] [--config-file CONFIG_FILE] [--graphite-url GRAPHITE_URL] + [--grafana-url GRAFANA_URL] [--grafana-user GRAFANA_USER] + [--grafana-password GRAFANA_PASSWORD] [--slack-token SLACK_TOKEN] + [--postgres-hostname POSTGRES_HOSTNAME] [--postgres-port POSTGRES_PORT] + [--postgres-username POSTGRES_USERNAME] [--postgres-password POSTGRES_PASSWORD] [--postgres-database POSTGRES_DATABASE] [--bigquery-project-id BIGQUERY_PROJECT_ID] @@ -340,6 +296,8 @@ def test_otava_list_tests_help_output(): options: -h, --help show this help message and exit + --config-file CONFIG_FILE + Otava config file path [env var: OTAVA_CONFIG] Graphite Options: Options for Graphite configuration @@ -401,10 +359,11 @@ def test_otava_list_metrics_help_output(): assert ( result.stdout == """\ -usage: otava list-metrics [-h] [--graphite-url GRAPHITE_URL] [--grafana-url GRAFANA_URL] - [--grafana-user GRAFANA_USER] [--grafana-password GRAFANA_PASSWORD] - [--slack-token SLACK_TOKEN] [--postgres-hostname POSTGRES_HOSTNAME] - [--postgres-port POSTGRES_PORT] [--postgres-username POSTGRES_USERNAME] +usage: otava list-metrics [-h] [--config-file CONFIG_FILE] [--graphite-url GRAPHITE_URL] + [--grafana-url GRAFANA_URL] [--grafana-user GRAFANA_USER] + [--grafana-password GRAFANA_PASSWORD] [--slack-token SLACK_TOKEN] + [--postgres-hostname POSTGRES_HOSTNAME] [--postgres-port POSTGRES_PORT] + [--postgres-username POSTGRES_USERNAME] [--postgres-password POSTGRES_PASSWORD] [--postgres-database POSTGRES_DATABASE] [--bigquery-project-id BIGQUERY_PROJECT_ID] @@ -417,6 +376,8 @@ def test_otava_list_metrics_help_output(): options: -h, --help show this help message and exit + --config-file CONFIG_FILE + Otava config file path [env var: OTAVA_CONFIG] Graphite Options: Options for Graphite configuration @@ -479,10 +440,11 @@ def test_otava_list_groups_help_output(): assert ( result.stdout == """\ -usage: otava list-groups [-h] [--graphite-url GRAPHITE_URL] [--grafana-url GRAFANA_URL] - [--grafana-user GRAFANA_USER] [--grafana-password GRAFANA_PASSWORD] - [--slack-token SLACK_TOKEN] [--postgres-hostname POSTGRES_HOSTNAME] - [--postgres-port POSTGRES_PORT] [--postgres-username POSTGRES_USERNAME] +usage: otava list-groups [-h] [--config-file CONFIG_FILE] [--graphite-url GRAPHITE_URL] + [--grafana-url GRAFANA_URL] [--grafana-user GRAFANA_USER] + [--grafana-password GRAFANA_PASSWORD] [--slack-token SLACK_TOKEN] + [--postgres-hostname POSTGRES_HOSTNAME] [--postgres-port POSTGRES_PORT] + [--postgres-username POSTGRES_USERNAME] [--postgres-password POSTGRES_PASSWORD] [--postgres-database POSTGRES_DATABASE] [--bigquery-project-id BIGQUERY_PROJECT_ID] @@ -491,6 +453,8 @@ def test_otava_list_groups_help_output(): options: -h, --help show this help message and exit + --config-file CONFIG_FILE + Otava config file path [env var: OTAVA_CONFIG] Graphite Options: Options for Graphite configuration @@ -552,8 +516,8 @@ def test_otava_remove_annotations_help_output(): assert ( result.stdout == """\ -usage: otava remove-annotations [-h] [--graphite-url GRAPHITE_URL] [--grafana-url GRAFANA_URL] - [--grafana-user GRAFANA_USER] +usage: otava remove-annotations [-h] [--config-file CONFIG_FILE] [--graphite-url GRAPHITE_URL] + [--grafana-url GRAFANA_URL] [--grafana-user GRAFANA_USER] [--grafana-password GRAFANA_PASSWORD] [--slack-token SLACK_TOKEN] [--postgres-hostname POSTGRES_HOSTNAME] [--postgres-port POSTGRES_PORT] @@ -570,6 +534,8 @@ def test_otava_remove_annotations_help_output(): options: -h, --help show this help message and exit + --config-file CONFIG_FILE + Otava config file path [env var: OTAVA_CONFIG] --force don't ask questions, just do it Graphite Options: @@ -632,10 +598,11 @@ def test_otava_validate_help_output(): assert ( result.stdout == """\ -usage: otava validate [-h] [--graphite-url GRAPHITE_URL] [--grafana-url GRAFANA_URL] - [--grafana-user GRAFANA_USER] [--grafana-password GRAFANA_PASSWORD] - [--slack-token SLACK_TOKEN] [--postgres-hostname POSTGRES_HOSTNAME] - [--postgres-port POSTGRES_PORT] [--postgres-username POSTGRES_USERNAME] +usage: otava validate [-h] [--config-file CONFIG_FILE] [--graphite-url GRAPHITE_URL] + [--grafana-url GRAFANA_URL] [--grafana-user GRAFANA_USER] + [--grafana-password GRAFANA_PASSWORD] [--slack-token SLACK_TOKEN] + [--postgres-hostname POSTGRES_HOSTNAME] [--postgres-port POSTGRES_PORT] + [--postgres-username POSTGRES_USERNAME] [--postgres-password POSTGRES_PASSWORD] [--postgres-database POSTGRES_DATABASE] [--bigquery-project-id BIGQUERY_PROJECT_ID] @@ -644,6 +611,8 @@ def test_otava_validate_help_output(): options: -h, --help show this help message and exit + --config-file CONFIG_FILE + Otava config file path [env var: OTAVA_CONFIG] Graphite Options: Options for Graphite configuration