Blame SOURCES/0023-Unify-command-line-options-and-configuration.patch

ec7001
From 71221179de4c5694a0032cc667891322d5016ee3 Mon Sep 17 00:00:00 2001
ec7001
From: Rob Crittenden <rcritten@redhat.com>
ec7001
Date: Tue Aug 31 15:55:04 2021 -0400
ec7001
Subject: Unify command-line options and configuration
ec7001
ec7001
This makes it possible to add command-line options to the
ec7001
configuration file.
ec7001
ec7001
The config file is loaded then the command-line options are
ec7001
merged in. The first one option set takes precedence. So if
ec7001
an option, say output_type, is in the configuration file then
ec7001
passing output-type on the command-line will not override it.
ec7001
The workaround is to pass --config= to ipa-healthcheck in order
ec7001
to not load the configuration file.
ec7001
ec7001
This will allow for greater flexibility when running this automatically
ec7001
without having to manually change test timer scripting directly.
ec7001
ec7001
https://bugzilla.redhat.com/show_bug.cgi?id=1872467
ec7001
ec7001
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
ec7001
---
ec7001
 man/man5/ipahealthcheck.conf.5    | 10 ++++++-
ec7001
 man/man8/ipa-healthcheck.8        |  3 +++
ec7001
 src/ipahealthcheck/core/config.py |  2 +-
ec7001
 src/ipahealthcheck/core/core.py   | 22 +++++++++++----
ec7001
 src/ipahealthcheck/core/output.py |  4 +--
ec7001
 tests/test_init.py                |  6 ++---
ec7001
 tests/test_options.py             | 45 +++++++++++++++++++++++++++++++
ec7001
 7 files changed, 80 insertions(+), 12 deletions(-)
ec7001
 create mode 100644 tests/test_options.py
ec7001
ec7001
diff --git a/man/man5/ipahealthcheck.conf.5 b/man/man5/ipahealthcheck.conf.5
ec7001
index 50d5506..8ff83c9 100644
ec7001
--- a/man/man5/ipahealthcheck.conf.5
ec7001
+++ b/man/man5/ipahealthcheck.conf.5
ec7001
@@ -36,6 +36,14 @@ The following options are relevant for the server:
ec7001
 .TP
ec7001
 .B cert_expiration_days\fR
ec7001
 The number of days left before a certificate expires to start displaying a warning. The default is 28.
ec7001
+.TP
ec7001
+All command\-line options may be included in the configuration file. Dashes must be converted to underscore for the configuration file, e.g. \-\-output\-type becomes output_type. All options, including those that don't make sense in a config file, like \-\-list\-sources, are allowed. Let the buyer beware.
ec7001
+.TP
ec7001
+The purpose of allowing command\-line options to be in the configuration file is for automation without having to tweak the automation script. For example, if you want the default output type to be human for the systemd timer automated runs, settting output_type=human in the configuration file will do this. When loading configuration the first option wins, so if any option is in the configuration file then it cannot be overridden by the command-line unless a different configuration file is specified (see \-\-config).
ec7001
+.TP
ec7001
+There may be conflicting exceptions. For example, if all=True is set in the configuration file, and the command\-line contains \-\-failures\-only, then only failures will be displayed because of the way the option evaluation is done.
ec7001
+.TP
ec7001
+Options that don't make sense for the configuration file include \-\-list\-sources and \-\-input\-file.
ec7001
 .SH "FILES"
ec7001
 .TP
ec7001
 .I /etc/ipahealthcheck/ipahealthcheck.conf
ec7001
@@ -49,4 +57,4 @@ configuration file
ec7001
  cert_expiration_days=7
ec7001
 
ec7001
 .SH "SEE ALSO"
ec7001
-.BR ipa-healthcheck (8)
ec7001
+.BR ipa\-healthcheck (8)
ec7001
diff --git a/man/man8/ipa-healthcheck.8 b/man/man8/ipa-healthcheck.8
ec7001
index 9c97cfe..148a08b 100644
ec7001
--- a/man/man8/ipa-healthcheck.8
ec7001
+++ b/man/man8/ipa-healthcheck.8
ec7001
@@ -30,6 +30,9 @@ Display a list of the available sources and the checks associated with those sou
ec7001
 
ec7001
 .SS "OPTIONAL ARGUMENTS"
ec7001
 .TP
ec7001
+\fB\-\-config\fR=\fIFILE\fR
ec7001
+The configuration file to use. If an empty string is passed in then no configuration file is loaded. The default is /etc/ipahealthcheck/ipahealthcheck.conf.
ec7001
+.TP
ec7001
 \fB\-\-source\fR=\fISOURCE\fR
ec7001
 Execute checks within the named source, or all sources in the given namespace.
ec7001
 .TP
ec7001
diff --git a/src/ipahealthcheck/core/config.py b/src/ipahealthcheck/core/config.py
ec7001
index 5b41cc3..14ae27f 100644
ec7001
--- a/src/ipahealthcheck/core/config.py
ec7001
+++ b/src/ipahealthcheck/core/config.py
ec7001
@@ -63,7 +63,7 @@ class Config:
ec7001
         """
ec7001
         Merge variables from dict ``d`` into the configuration
ec7001
 
ec7001
-        The last one wins.
ec7001
+        The first one wins.
ec7001
 
ec7001
         :param d: dict containing configuration
ec7001
         """
ec7001
diff --git a/src/ipahealthcheck/core/core.py b/src/ipahealthcheck/core/core.py
ec7001
index 19f7818..98ca1d2 100644
ec7001
--- a/src/ipahealthcheck/core/core.py
ec7001
+++ b/src/ipahealthcheck/core/core.py
ec7001
@@ -160,6 +160,8 @@ def list_sources(plugins):
ec7001
 def add_default_options(parser, output_registry, default_output):
ec7001
     output_names = [plugin.__name__.lower() for
ec7001
                     plugin in output_registry.plugins]
ec7001
+    parser.add_argument('--config', dest='config',
ec7001
+                        default=None, help='Config file to load')
ec7001
     parser.add_argument('--verbose', dest='verbose', action='store_true',
ec7001
                         default=False, help='Run in verbose mode')
ec7001
     parser.add_argument('--debug', dest='debug', action='store_true',
ec7001
@@ -173,9 +175,10 @@ def add_default_options(parser, output_registry, default_output):
ec7001
     parser.add_argument('--check', dest='check',
ec7001
                         default=None,
ec7001
                         help='Check to execute, e.g. BazCheck')
ec7001
-    parser.add_argument('--output-type', dest='output', choices=output_names,
ec7001
+    parser.add_argument('--output-type', dest='output_type',
ec7001
+                        choices=output_names,
ec7001
                         default=default_output, help='Output method')
ec7001
-    parser.add_argument('--output-file', dest='outfile', default=None,
ec7001
+    parser.add_argument('--output-file', dest='output_file', default=None,
ec7001
                         help='File to store output')
ec7001
 
ec7001
 
ec7001
@@ -262,7 +265,6 @@ class RunChecks:
ec7001
         add_output_options(self.parser, self.output_registry)
ec7001
         self.add_options()
ec7001
         options = parse_options(self.parser)
ec7001
-        self.options = options
ec7001
         rval = self.validate_options()
ec7001
         if rval:
ec7001
             return rval
ec7001
@@ -273,10 +275,20 @@ class RunChecks:
ec7001
         if options.debug:
ec7001
             logger.setLevel(logging.DEBUG)
ec7001
 
ec7001
-        config = read_config(self.configfile)
ec7001
+        if options.config is not None:
ec7001
+            config = read_config(options.config)
ec7001
+        else:
ec7001
+            config = read_config(self.configfile)
ec7001
         if config is None:
ec7001
             return 1
ec7001
 
ec7001
+        # Unify config and options. One of these variables will be
ec7001
+        # eventually deprecated in the future. This way all cli
ec7001
+        # options can be set in config instead.
ec7001
+        config.merge(vars(options))
ec7001
+        self.options = config
ec7001
+        options = config
ec7001
+
ec7001
         rval = self.pre_check()
ec7001
         if rval is not None:
ec7001
             return rval
ec7001
@@ -319,7 +331,7 @@ class RunChecks:
ec7001
                 plugins.append(plugin)
ec7001
 
ec7001
         for out in self.output_registry.plugins:
ec7001
-            if out.__name__.lower() == options.output:
ec7001
+            if out.__name__.lower() == options.output_type:
ec7001
                 output = out(options)
ec7001
                 break
ec7001
 
ec7001
diff --git a/src/ipahealthcheck/core/output.py b/src/ipahealthcheck/core/output.py
ec7001
index 784263d..61dffbe 100644
ec7001
--- a/src/ipahealthcheck/core/output.py
ec7001
+++ b/src/ipahealthcheck/core/output.py
ec7001
@@ -36,7 +36,7 @@ class Output:
ec7001
        which will render the results into a string for writing.
ec7001
     """
ec7001
     def __init__(self, options):
ec7001
-        self.filename = options.outfile
ec7001
+        self.filename = options.output_file
ec7001
 
ec7001
         # Non-required options in the framework, set logical defaults to
ec7001
         # pre 0.6 behavior with everything reported.
ec7001
@@ -110,7 +110,7 @@ class JSON(Output):
ec7001
 
ec7001
     def __init__(self, options):
ec7001
         super(JSON, self).__init__(options)
ec7001
-        self.indent = options.indent
ec7001
+        self.indent = int(options.indent)
ec7001
 
ec7001
     def generate(self, data):
ec7001
         output = json.dumps(data, indent=self.indent)
ec7001
diff --git a/tests/test_init.py b/tests/test_init.py
ec7001
index e18e03c..5f9e3e2 100644
ec7001
--- a/tests/test_init.py
ec7001
+++ b/tests/test_init.py
ec7001
@@ -10,12 +10,12 @@ from ipahealthcheck.core.output import output_registry
ec7001
 class RunChecks:
ec7001
     def run_healthcheck(self):
ec7001
         options = argparse.Namespace(check=None, debug=False, indent=2,
ec7001
-                                     list_sources=False, outfile=None,
ec7001
-                                     output='json', source=None,
ec7001
+                                     list_sources=False, output_file=None,
ec7001
+                                     output_type='json', source=None,
ec7001
                                      verbose=False)
ec7001
 
ec7001
         for out in output_registry.plugins:
ec7001
-            if out.__name__.lower() == options.output:
ec7001
+            if out.__name__.lower() == options.output_type:
ec7001
                 out(options)
ec7001
                 break
ec7001
 
ec7001
diff --git a/tests/test_options.py b/tests/test_options.py
ec7001
new file mode 100644
ec7001
index 0000000..da1866f
ec7001
--- /dev/null
ec7001
+++ b/tests/test_options.py
ec7001
@@ -0,0 +1,45 @@
ec7001
+#
ec7001
+# Copyright (C) 2022 FreeIPA Contributors see COPYING for license
ec7001
+#
ec7001
+
ec7001
+import argparse
ec7001
+import os
ec7001
+import tempfile
ec7001
+from unittest.mock import patch
ec7001
+
ec7001
+from ipahealthcheck.core.core import RunChecks
ec7001
+from ipahealthcheck.core.plugin import Results
ec7001
+
ec7001
+options = argparse.Namespace(check=None, source=None, debug=False,
ec7001
+                             indent=2, list_sources=False,
ec7001
+                             output_type='json', output_file=None,
ec7001
+                             verbose=False, version=False, config=None)
ec7001
+
ec7001
+
ec7001
+@patch('ipahealthcheck.core.core.run_service_plugins')
ec7001
+@patch('ipahealthcheck.core.core.run_plugins')
ec7001
+@patch('ipahealthcheck.core.core.parse_options')
ec7001
+def test_options_merge(mock_parse, mock_run, mock_service):
ec7001
+    """
ec7001
+    Test merging file-based and CLI options
ec7001
+    """
ec7001
+    mock_service.return_value = (Results(), [])
ec7001
+    mock_run.return_value = Results()
ec7001
+    mock_parse.return_value = options
ec7001
+    fd, config_path = tempfile.mkstemp()
ec7001
+    os.close(fd)
ec7001
+    with open(config_path, "w") as fd:
ec7001
+        fd.write('[default]\n')
ec7001
+        fd.write('output_type=human\n')
ec7001
+        fd.write('indent=5\n')
ec7001
+
ec7001
+    try:
ec7001
+        run = RunChecks(['ipahealthcheck.registry'], config_path)
ec7001
+
ec7001
+        run.run_healthcheck()
ec7001
+
ec7001
+        # verify two valus that have defaults with our overriden values
ec7001
+        assert run.options.output_type == 'human'
ec7001
+        assert run.options.indent == '5'
ec7001
+    finally:
ec7001
+        os.remove(config_path)
ec7001
-- 
ec7001
2.31.1
ec7001