Skip to content

Commit

Permalink
Unify command-line options and configuration
Browse files Browse the repository at this point in the history
This makes it possible to add command-line options to the
configuration file.

The config file is loaded then the command-line options are
merged in. The first one option set takes precedence. So if
an option, say output_type, is in the configuration file then
passing output-type on the command-line will not override it.
The workaround is to pass --config= to ipa-healthcheck in order
to not load the configuration file.

This will allow for greater flexibility when running this automatically
without having to manually change test timer scripting directly.

https://bugzilla.redhat.com/show_bug.cgi?id=1872467

Signed-off-by: Rob Crittenden <[email protected]>
  • Loading branch information
rcritten committed Jan 24, 2022
1 parent 02eb4b2 commit 3ecffcc
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 13 deletions.
10 changes: 9 additions & 1 deletion man/man5/ipahealthcheck.conf.5
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ The following options are relevant for the server:
.TP
.B cert_expiration_days\fR
The number of days left before a certificate expires to start displaying a warning. The default is 28.
.TP
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.
.TP
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).
.TP
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.
.TP
Options that don't make sense for the configuration file include \-\-list\-sources and \-\-input\-file.
.SH "FILES"
.TP
.I /etc/ipahealthcheck/ipahealthcheck.conf
Expand All @@ -49,4 +57,4 @@ configuration file
cert_expiration_days=7

.SH "SEE ALSO"
.BR ipa-healthcheck (8)
.BR ipa\-healthcheck (8)
3 changes: 3 additions & 0 deletions man/man8/ipa-healthcheck.8
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ Display a list of the available sources and the checks associated with those sou

.SS "OPTIONAL ARGUMENTS"
.TP
\fB\-\-config\fR=\fIFILE\fR
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.
.TP
\fB\-\-source\fR=\fISOURCE\fR
Execute checks within the named source, or all sources in the given namespace.
.TP
Expand Down
2 changes: 1 addition & 1 deletion src/ipaclustercheck/core/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ClusterOutput(Output):
severity doesn't apply in this case so exclude those.
"""
def __init__(self, options):
self.filename = options.outfile
self.filename = options.output_file

def strip_output(self, results):
"""Nothing to strip out"""
Expand Down
2 changes: 1 addition & 1 deletion src/ipahealthcheck/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def merge(self, d):
"""
Merge variables from dict ``d`` into the configuration
The last one wins.
The first one wins.
:param d: dict containing configuration
"""
Expand Down
22 changes: 17 additions & 5 deletions src/ipahealthcheck/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ def list_sources(plugins):
def add_default_options(parser, output_registry, default_output):
output_names = [plugin.__name__.lower() for
plugin in output_registry.plugins]
parser.add_argument('--config', dest='config',
default=None, help='Config file to load')
parser.add_argument('--verbose', dest='verbose', action='store_true',
default=False, help='Run in verbose mode')
parser.add_argument('--debug', dest='debug', action='store_true',
Expand All @@ -176,9 +178,10 @@ def add_default_options(parser, output_registry, default_output):
parser.add_argument('--check', dest='check',
default=None,
help='Check to execute, e.g. BazCheck')
parser.add_argument('--output-type', dest='output', choices=output_names,
parser.add_argument('--output-type', dest='output_type',
choices=output_names,
default=default_output, help='Output method')
parser.add_argument('--output-file', dest='outfile', default=None,
parser.add_argument('--output-file', dest='output_file', default=None,
help='File to store output')
parser.add_argument('--version', dest='version', action='store_true',
help='Report the version number and exit')
Expand Down Expand Up @@ -266,7 +269,6 @@ def run_healthcheck(self):
add_output_options(self.parser, self.output_registry)
self.add_options()
options = parse_options(self.parser)
self.options = options

if options.version:
for registry in self.entry_points:
Expand All @@ -290,10 +292,20 @@ def run_healthcheck(self):
if options.debug:
logger.setLevel(logging.DEBUG)

config = read_config(self.configfile)
if options.config is not None:
config = read_config(options.config)
else:
config = read_config(self.configfile)
if config is None:
return 1

# Unify config and options. One of these variables will be
# eventually deprecated in the future. This way all cli
# options can be set in config instead.
config.merge(vars(options))
self.options = config
options = config

# pylint: disable=assignment-from-none
rval = self.pre_check()
# pylint: enable=assignment-from-none
Expand Down Expand Up @@ -327,7 +339,7 @@ def run_healthcheck(self):
plugins.append(plugin)

for out in self.output_registry.plugins:
if out.__name__.lower() == options.output:
if out.__name__.lower() == options.output_type:
output = out(options)
break

Expand Down
4 changes: 2 additions & 2 deletions src/ipahealthcheck/core/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Output:
which will render the results into a string for writing.
"""
def __init__(self, options):
self.filename = options.outfile
self.filename = options.output_file

# Non-required options in the framework, set logical defaults to
# pre 0.6 behavior with everything reported.
Expand Down Expand Up @@ -110,7 +110,7 @@ class JSON(Output):

def __init__(self, options):
super().__init__(options)
self.indent = options.indent
self.indent = int(options.indent)

def generate(self, data):
output = json.dumps(data, indent=self.indent)
Expand Down
6 changes: 3 additions & 3 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
class RunChecks:
def run_healthcheck(self):
options = argparse.Namespace(check=None, debug=False, indent=2,
list_sources=False, outfile=None,
output='json', source=None,
list_sources=False, output_file=None,
output_type='json', source=None,
verbose=False)

for out in output_registry.plugins:
if out.__name__.lower() == options.output:
if out.__name__.lower() == options.output_type:
out(options)
break

Expand Down
45 changes: 45 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#
# Copyright (C) 2022 FreeIPA Contributors see COPYING for license
#

import argparse
import os
import tempfile
from unittest.mock import patch

from ipahealthcheck.core.core import RunChecks
from ipahealthcheck.core.plugin import Results

options = argparse.Namespace(check=None, source=None, debug=False,
indent=2, list_sources=False,
output_type='json', output_file=None,
verbose=False, version=False, config=None)


@patch('ipahealthcheck.core.core.run_service_plugins')
@patch('ipahealthcheck.core.core.run_plugins')
@patch('ipahealthcheck.core.core.parse_options')
def test_options_merge(mock_parse, mock_run, mock_service):
"""
Test merging file-based and CLI options
"""
mock_service.return_value = (Results(), [])
mock_run.return_value = Results()
mock_parse.return_value = options
fd, config_path = tempfile.mkstemp()
os.close(fd)
with open(config_path, "w") as fd:
fd.write('[default]\n')
fd.write('output_type=human\n')
fd.write('indent=5\n')

try:
run = RunChecks(['ipahealthcheck.registry'], config_path)

run.run_healthcheck()

# verify two valus that have defaults with our overriden values
assert run.options.output_type == 'human'
assert run.options.indent == '5'
finally:
os.remove(config_path)

0 comments on commit 3ecffcc

Please sign in to comment.