628b30fc2c
Tool was run against murano-apps catalog, all found bugs was fixed Change-Id: I7b31d6d3adff749126cbafdfbc2a45aa6d16c24a
148 lines
5.2 KiB
Python
148 lines
5.2 KiB
Python
# Copyright (c) 2016 Mirantis, Inc.
|
|
#
|
|
# 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.
|
|
|
|
import abc
|
|
import itertools
|
|
import re
|
|
|
|
import six
|
|
|
|
from muranopkgcheck import error
|
|
from muranopkgcheck.i18n import _
|
|
from muranopkgcheck import log
|
|
|
|
LOG = log.getLogger(__name__)
|
|
FQN_REGEX = re.compile('^([a-zA-Z_$][\w$]*\.)*[a-zA-Z_$][\w$]*$')
|
|
NAME_REGEX = re.compile('^[A-Za-z_][\w]*$')
|
|
|
|
error.register.E005(description='YAML multi document is not allowed')
|
|
error.register.E020(description='Missing required key')
|
|
error.register.E021(description='Unknown keyword')
|
|
error.register.E040(description='Value should be string')
|
|
error.register.W010(description='Unknown keyword')
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class BaseValidator(object):
|
|
|
|
def __init__(self, loaded_package, _filter='.*'):
|
|
self._loaded_pkg = loaded_package
|
|
self._filter = _filter
|
|
|
|
@abc.abstractmethod
|
|
def run(self):
|
|
pass
|
|
|
|
def _valid_string(self, value):
|
|
if not isinstance(value, six.string_types):
|
|
yield error.report.E040(_('Value is not a string "{}"'
|
|
'').format(value), value)
|
|
|
|
def _check_name(self, name):
|
|
if isinstance(name, six.string_types) and NAME_REGEX.match(name):
|
|
return True
|
|
return False
|
|
|
|
def _check_fqn_name(self, fqn):
|
|
if isinstance(fqn, six.string_types) and FQN_REGEX.match(fqn):
|
|
return True
|
|
return False
|
|
|
|
def _check_ns_fqn_name(self, ns_fqn):
|
|
if isinstance(ns_fqn, six.string_types):
|
|
if ':' in ns_fqn:
|
|
ns, fqn = ns_fqn.split(':', 1)
|
|
if NAME_REGEX.match(ns) and FQN_REGEX.match(fqn):
|
|
return True
|
|
elif FQN_REGEX.match(ns_fqn):
|
|
return True
|
|
return False
|
|
|
|
|
|
class YamlValidator(BaseValidator):
|
|
def __init__(self, loaded_package, _filter='.*', allows_multi=False):
|
|
super(YamlValidator, self).__init__(loaded_package, _filter)
|
|
self._checkers = {}
|
|
self._allows_multi = allows_multi
|
|
|
|
def add_checker(self, function, key=None, required=True):
|
|
checkers = self._checkers.setdefault(key, {'checkers': [],
|
|
'required': False})
|
|
checkers['checkers'].append(function)
|
|
if key is None:
|
|
checkers['required'] = False
|
|
elif required:
|
|
checkers['required'] = True
|
|
|
|
def run(self):
|
|
chain_of_suits = []
|
|
for filename in self._loaded_pkg.search_for(self._filter):
|
|
file_ = self._loaded_pkg.read(filename)
|
|
chain_of_suits.append(self._run_single(file_))
|
|
return itertools.chain(*chain_of_suits)
|
|
|
|
def _run_single(self, file_):
|
|
reports_chain = []
|
|
|
|
def run_helper(name, checkers, data):
|
|
for checker in checkers:
|
|
result = checker(data)
|
|
if result:
|
|
reports_chain.append(result)
|
|
|
|
try:
|
|
multi_documents = file_.yaml()
|
|
except Exception as e:
|
|
reports_chain.append([
|
|
error.report.E002('Yaml Error: {0}'.format(e), e)])
|
|
else:
|
|
if multi_documents is None:
|
|
multi_documents = [{}]
|
|
|
|
if len(multi_documents) > 1 and not self._allows_multi:
|
|
reports_chain.append([
|
|
error.report.E005(_('Multi document is not allowed in {}')
|
|
.format(file_._path))])
|
|
|
|
for ast in multi_documents:
|
|
file_check = self._checkers.get(None)
|
|
if file_check:
|
|
run_helper(None, file_check['checkers'], ast)
|
|
for key, value in six.iteritems(ast):
|
|
checkers = self._checkers.get(key)
|
|
if checkers:
|
|
run_helper(key, checkers['checkers'], ast[key])
|
|
else:
|
|
reports_chain.append(self._unknown_keyword(key, value))
|
|
missing = set(key for key, value in
|
|
six.iteritems(self._checkers)
|
|
if value['required']) - set(ast.keys())
|
|
for m in missing:
|
|
reports_chain.append([
|
|
error.report.E020(_('Missing required key "{}"')
|
|
.format(m), m)])
|
|
|
|
return itertools.chain(*reports_chain)
|
|
|
|
def _valid_keywords(self, present, known):
|
|
unknown = set(present) - set(known)
|
|
for u in unknown:
|
|
yield error.report.E021(_('Unknown keyword "{}"').format(u), u)
|
|
|
|
def _unknown_keyword(self, key, value):
|
|
yield error.report.W010(_('Unknown keyword "{}"').format(key), key)
|
|
|
|
def _null_checker(self, value):
|
|
pass
|