Source code for compliance.fix
# -*- mode:python; coding:utf-8 -*-
# Copyright (c) 2020 IBM Corp. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Compliance fixer automation module."""
import sys
from compliance.config import get_config
from compliance.utils.test import parse_test_id
[docs]class Fixer(object):
"""This class attempts to resolve check failures."""
def __init__(self, results, dry_run=True, out=sys.stdout):
"""
Construct and initialize the fixer object.
:param results: dictionary of check results.
:param dry_run: dictate whether to inform or perform actions to fix.
:param out: where to write the output for dry-run messages.
"""
self._results = results
self._dry_run = dry_run
self._out = out
self._creds = get_config().creds
[docs] def fix(self):
"""
Perform all fix routines found in executed checks.
Iterate through all compliance checks and looks for `fix_*` methods
with corresponding `test_*` methods. These are executed, and results
are recorded if fixes are made.
Note: Instead of individual `fix_*` methods, you can instead define
a single `fix_failures` method which handles all fixes for the class.
"""
if not self._results:
return
for test_id, test_desc in self._results.items():
if test_desc['status'] != 'fail':
continue
test_obj = test_desc['test'].test
method_name = parse_test_id(test_id)['method']
candidate = method_name.replace('test_', 'fix_')
if len(test_obj.tests) > 1 and hasattr(test_obj, candidate):
getattr(test_obj, candidate)(self)
elif hasattr(test_obj, 'fix_failures'):
test_obj.fix_failures(self)
[docs] def execute_fix(self, test_obj, fct, args=None):
"""
Execute the fix routine for a given check test object.
This method gets called by fix_* methods in checks.
It gets passed a function (method) that must be called in order
to fix one specific issue. The method's fix will either get
executed, or if in dry-run mode, will print out the method's
docstring, injecting any arguments if needed. The check's
fixed_failure_count is incremented if the fix was successful.
:param test_obj: instance of subclass of
:py:class:`compliance.utils.check.ComplianceCheck` on which
to execute the fix
:param fct: a callback function that will actually perform
the fix. this function will be passed a reference to this
fixer, along with a reference to a
compliance.utils.credentials.Config object. this
callback will also need to have a docstring, which is
what will be displayed in dry-run mode. the docstring
will get formatted with the arguments passed to the fix
function.
:param args: dictionary of named arguments to pass to the
fix function fct.
"""
args = args or {}
if self._dry_run:
self._out.write(f'DRY-RUN: {fct.__doc__.format(**args)}\n')
else:
success = fct(
**dict(list(args.items()) + [('creds', self._creds)])
)
if success:
test_obj.fixed_failure_count += 1