fix json logging and add file exclude options

This commit is contained in:
Robert Kaussow 2019-04-04 15:35:47 +02:00
parent fa14538f20
commit 2b574970c2
9 changed files with 69 additions and 82 deletions

View File

@ -1,14 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
import argparse import argparse
import json
import logging import logging
from ansiblelater import __version__ from ansiblelater import LOG, __version__, logger
from ansiblelater import LOG from ansiblelater.command import base, candidates
from ansiblelater import logger
from ansiblelater.command import base
from ansiblelater.command import candidates
def main(): def main():

View File

@ -5,18 +5,12 @@ import codecs
import copy import copy
import os import os
import re import re
import sys
from distutils.version import LooseVersion from distutils.version import LooseVersion
import ansible from ansiblelater import LOG, utils
from ansiblelater import LOG
from ansiblelater import utils
from ansiblelater.logger import flag_extra
from ansiblelater.command.review import Error from ansiblelater.command.review import Error
from ansiblelater.exceptions import ( # noqa from ansiblelater.exceptions import LaterAnsibleError, LaterError # noqa
LaterError, LaterAnsibleError from ansiblelater.logger import flag_extra
)
try: try:
# Ansible 2.4 import of module loader # Ansible 2.4 import of module loader
@ -78,13 +72,13 @@ class Candidate(object):
if self.expected_version: if self.expected_version:
if isinstance(self, RoleFile): if isinstance(self, RoleFile):
LOG.warn("%s %s is in a role that contains a meta/main.yml without a declared " LOG.warn("%s %s is in a role that contains a meta/main.yml without a declared "
"standards version. " "standards version. "
"Using latest standards version %s" % "Using latest standards version %s" %
(type(self).__name__, self.path, version)) (type(self).__name__, self.path, version))
else: else:
LOG.warn("%s %s does not present standards version. " LOG.warn("%s %s does not present standards version. "
"Using latest standards version %s" % "Using latest standards version %s" %
(type(self).__name__, self.path, version)) (type(self).__name__, self.path, version))
LOG.info("%s %s declares standards version %s" % LOG.info("%s %s declares standards version %s" %
(type(self).__name__, self.path, version)) (type(self).__name__, self.path, version))
@ -114,8 +108,8 @@ class Candidate(object):
result = standard.check(self, settings.config) result = standard.check(self, settings.config)
if not result: if not result:
utils.sysexit_with_message("Standard '%s' returns an empty result object." % utils.sysexit_with_message("Standard '{}' returns an empty result object.".format(
(standard.check.__name__)) standard.check.__name__))
labels = {"tag": "review", "standard": standard.name, "file": self.path, "passed": True} labels = {"tag": "review", "standard": standard.name, "file": self.path, "passed": True}
@ -128,13 +122,16 @@ class Candidate(object):
if not standard.version: if not standard.version:
LOG.warn("{id}Best practice '{name}' not met:\n{path}:{error}".format( LOG.warn("{id}Best practice '{name}' not met:\n{path}:{error}".format(
id=standard.id, name=standard.name, path=self.path, error=err), extra=flag_extra(err_labels)) id=standard.id, name=standard.name, path=self.path, error=err),
extra=flag_extra(err_labels))
elif LooseVersion(standard.version) > LooseVersion(self.version): elif LooseVersion(standard.version) > LooseVersion(self.version):
LOG.warn("{id}Future standard '{name}' not met:\n{path}:{error}".format( LOG.warn("{id}Future standard '{name}' not met:\n{path}:{error}".format(
id=standard.id, name=standard.name, path=self.path, error=err), extra=flag_extra(err_labels)) id=standard.id, name=standard.name, path=self.path, error=err),
extra=flag_extra(err_labels))
else: else:
LOG.error("{id}Standard '{name}' not met:\n{path}:{error}".format( LOG.error("{id}Standard '{name}' not met:\n{path}:{error}".format(
id=standard.id, name=standard.name, path=self.path, error=err), extra=flag_extra(err_labels)) id=standard.id, name=standard.name, path=self.path, error=err),
extra=flag_extra(err_labels))
errors = errors + 1 errors = errors + 1
if not result.errors: if not result.errors:
if not standard.version: if not standard.version:

View File

@ -1,8 +1,5 @@
"""Review candidates.""" """Review candidates."""
import os
import sys
from six import iteritems from six import iteritems
@ -44,5 +41,3 @@ class Result(object):
def message(self): def message(self):
return "\n".join(["{0}:{1}".format(self.candidate, error) return "\n".join(["{0}:{1}".format(self.candidate, error)
for error in self.errors]) for error in self.errors])

View File

@ -7,10 +7,7 @@ class LaterError(Exception):
"""Generic exception for later.""" """Generic exception for later."""
def __init__(self, msg, original): def __init__(self, msg, original):
""" """Initialize new exception."""
Initialize new exception.
"""
super(LaterError, self).__init__(msg + (": %s" % original)) super(LaterError, self).__init__(msg + (": %s" % original))
self.original = original self.original = original

View File

@ -5,12 +5,12 @@ import os
import sys import sys
import colorama import colorama
from six import iteritems
from ansible.module_utils.parsing.convert_bool import boolean as to_bool from ansible.module_utils.parsing.convert_bool import boolean as to_bool
from pythonjsonlogger import jsonlogger from pythonjsonlogger import jsonlogger
from six import iteritems
CONSOLE_FORMAT = "%(levelname)s: %(message)s" CONSOLE_FORMAT = "%(levelname)s: %(message)s"
JSON_FORMAT = "(levelname) (asctime)" JSON_FORMAT = "(asctime) (levelname) (message)"
def _should_do_markup(): def _should_do_markup():
@ -24,13 +24,6 @@ def _should_do_markup():
colorama.init(autoreset=True, strip=not _should_do_markup()) colorama.init(autoreset=True, strip=not _should_do_markup())
clashing_keywords = {key for key in dir(logging.LogRecord(None, None, "", 0, "", (), None, None)) if "__" not in key}
additional_clashing_keywords = {
"message",
"asctime"
}
clashing_keywords = clashing_keywords.union(additional_clashing_keywords)
def flag_extra(kwargs): def flag_extra(kwargs):
"""Ensure kwargs not conflict with the logging module.""" """Ensure kwargs not conflict with the logging module."""

View File

@ -1,14 +1,16 @@
"""Global settings object definition.""" """Global settings object definition."""
import copy
import logging import logging
import os import os
import anyconfig import anyconfig
from appdirs import AppDirs from appdirs import AppDirs
from globmatch import glob_match
from jsonschema._utils import format_as_index from jsonschema._utils import format_as_index
from pkg_resources import resource_filename from pkg_resources import resource_filename
from ansiblelater import logger, utils from ansiblelater import utils
config_dir = AppDirs("ansible-later").user_config_dir config_dir = AppDirs("ansible-later").user_config_dir
default_config_file = os.path.join(config_dir, "config.yml") default_config_file = os.path.join(config_dir, "config.yml")
@ -37,6 +39,7 @@ class Settings(object):
self.args = self._set_args(args) self.args = self._set_args(args)
self.config = self._get_config() self.config = self._get_config()
self.schema = None self.schema = None
self._update_filelist()
def _set_args(self, args): def _set_args(self, args):
self.config_file = args.get("config_file") or default_config_file self.config_file = args.get("config_file") or default_config_file
@ -80,6 +83,8 @@ class Settings(object):
"rules": { "rules": {
"standards": rules_dir, "standards": rules_dir,
"filter": [], "filter": [],
"ignore_dotfiles": True,
"exclude_files": []
}, },
"logging": { "logging": {
"level": logging.WARN, "level": logging.WARN,
@ -98,7 +103,7 @@ class Settings(object):
filelist = [] filelist = []
for root, dirs, files in os.walk("."): for root, dirs, files in os.walk("."):
for filename in files: for filename in files:
filelist.append(os.path.join(root, filename)) filelist.append(os.path.relpath(os.path.normpath(os.path.join(root, filename))))
else: else:
filelist = args["rules"]["files"] filelist = args["rules"]["files"]
@ -114,3 +119,16 @@ class Settings(object):
schema=format_as_index(list(e.relative_schema_path)[:-1]) schema=format_as_index(list(e.relative_schema_path)[:-1])
) )
utils.sysexit_with_message("{schema}: {msg}".format(schema=schema_error, msg=e.message)) utils.sysexit_with_message("{schema}: {msg}".format(schema=schema_error, msg=e.message))
def _update_filelist(self):
files = self.config["rules"]["files"]
excludes = self.config["rules"]["exclude_files"]
ignore_dotfiles = self.config["rules"]["ignore_dotfiles"]
if ignore_dotfiles:
excludes.append(".")
valid = copy.copy(files)
for item in valid:
if glob_match(item, excludes):
files.remove(item)

View File

@ -28,4 +28,3 @@ class Standard(object):
def __repr__(self): # noqa def __repr__(self): # noqa
return "Standard: %s (version: %s, types: %s)" % ( return "Standard: %s (version: %s, types: %s)" % (
self.name, self.version, self.types) self.name, self.version, self.types)

View File

@ -1,22 +1,19 @@
from __future__ import print_function from __future__ import print_function
import contextlib import contextlib
import importlib
import logging
import os import os
import sys
import re import re
import colorama import sys
from distutils.version import LooseVersion
import yaml import yaml
from distutils.version import LooseVersion
from ansiblelater import logger from ansiblelater import logger
from ansible.module_utils.parsing.convert_bool import boolean as to_bool
try: try:
import ConfigParser as configparser import ConfigParser as configparser # noqa
except ImportError: except ImportError:
import configparser import configparser # noqa
LOG = logger.get_logger(__name__) LOG = logger.get_logger(__name__)
@ -67,19 +64,13 @@ def is_line_in_ranges(line, ranges):
return not ranges or any([line in r for r in ranges]) return not ranges or any([line in r for r in ranges])
def read_config(config_file):
config = configparser.RawConfigParser({'standards': None})
config.read(config_file)
return Settings(config, config_file)
def safe_load(string): def safe_load(string):
""" """
Parse the provided string returns a dict. Parse the provided string returns a dict.
:param string: A string to be parsed. :param string: A string to be parsed.
:return: dict :returns: dict
""" """
try: try:
return yaml.safe_load(string) or {} return yaml.safe_load(string) or {}
@ -88,12 +79,14 @@ def safe_load(string):
@contextlib.contextmanager @contextlib.contextmanager
def open_file(filename, mode='r'): def open_file(filename, mode="r"):
""" """
Open the provide file safely and returns a file type. Open the provide file safely and returns a file type.
:param filename: A string containing an absolute path to the file to open. :param filename: A string containing an absolute path to the file to open.
:param mode: A string describing the way in which the file will be used. :param mode: A string describing the way in which the file will be used.
:return: file type :returns: file type
""" """
with open(filename, mode) as stream: with open(filename, mode) as stream:
yield stream yield stream

View File

@ -1,23 +1,21 @@
import codecs import codecs
import yaml
from collections import defaultdict from collections import defaultdict
import yaml
from yamllint import linter from yamllint import linter
from yamllint.config import YamlLintConfig from yamllint.config import YamlLintConfig
# Workaround for import errors with ansble 2.1 and 2.3
from ansible.parsing.dataloader import DataLoader
from ansiblelater.command.review import Error from ansiblelater.command.review import Error
from .yamlhelper import normalize_task from ansiblelater.exceptions import LaterAnsibleError, LaterError
from .yamlhelper import action_tasks
from .yamlhelper import parse_yaml_linenumbers from .yamlhelper import (action_tasks, normalize_task, normalized_yaml,
from .yamlhelper import normalized_yaml parse_yaml_linenumbers)
from ansiblelater.exceptions import LaterError, LaterAnsibleError
def get_tasks(candidate, settings): def get_tasks(candidate, settings):
errors = [] errors = []
try: try:
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f: with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
yamllines = parse_yaml_linenumbers(f, candidate.path) yamllines = parse_yaml_linenumbers(f, candidate.path)
except LaterError as ex: except LaterError as ex:
@ -33,7 +31,7 @@ def get_action_tasks(candidate, settings):
tasks = [] tasks = []
errors = [] errors = []
try: try:
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f: with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
yamllines = parse_yaml_linenumbers(f, candidate.path) yamllines = parse_yaml_linenumbers(f, candidate.path)
if yamllines: if yamllines:
@ -65,19 +63,20 @@ def get_normalized_tasks(candidate, settings):
normalized = [] normalized = []
errors = [] errors = []
try: try:
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f: with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
yamllines = parse_yaml_linenumbers(f, candidate.path) yamllines = parse_yaml_linenumbers(f, candidate.path)
if yamllines: if yamllines:
tasks = action_tasks(yamllines, candidate) tasks = action_tasks(yamllines, candidate)
for task in tasks: for task in tasks:
# An empty `tags` block causes `None` to be returned if # An empty `tags` block causes `None` to be returned if
# the `or []` is not present - `task.get('tags', [])` # the `or []` is not present - `task.get("tags", [])`
# does not suffice. # does not suffice.
if 'skip_ansible_lint' in (task.get('tags') or []): if "skip_ansible_lint" in (task.get("tags") or []):
# No need to normalize_task if we are skipping it. # No need to normalize_task if we are skipping it.
continue continue
normalized.append(normalize_task(task, candidate.path, settings["ansible"]["custom_modules"])) normalized.append(
normalize_task(task, candidate.path, settings["ansible"]["custom_modules"]))
except LaterError as ex: except LaterError as ex:
e = ex.original e = ex.original
@ -112,7 +111,7 @@ def get_raw_yaml(candidate, settings):
errors = [] errors = []
try: try:
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f: with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
content = yaml.safe_load(f) content = yaml.safe_load(f)
except LaterError as ex: except LaterError as ex:
@ -125,7 +124,7 @@ def get_raw_yaml(candidate, settings):
def run_yamllint(candidate, settings, options="extends: default"): def run_yamllint(candidate, settings, options="extends: default"):
errors = [] errors = []
try: try:
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f: with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
for problem in linter.run(f, YamlLintConfig(options)): for problem in linter.run(f, YamlLintConfig(options)):
errors.append(Error(problem.line, problem.desc)) errors.append(Error(problem.line, problem.desc))
except LaterError as ex: except LaterError as ex: