209 lines
6.9 KiB
Python
209 lines
6.9 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 re
|
|
import six
|
|
|
|
from muranopkgcheck.checkers import yaql_checker
|
|
from muranopkgcheck import error
|
|
|
|
|
|
ASSIGMENT_KEY = re.compile('^\$.?[\w]')
|
|
|
|
|
|
def check_req(check, required=True):
|
|
return locals()
|
|
|
|
error.register.E203(description='Value should be string type')
|
|
error.register.E200(description='No value should be here')
|
|
error.register.E204(description='Wrong code structure/assigment')
|
|
error.register.E202(description='Not a valid yaql expression')
|
|
error.register.W202(description='Not a valid yaql expression')
|
|
error.register.E201(description='Not a valid variable name')
|
|
|
|
|
|
CODE_STRUCTURE = {
|
|
'Try': {
|
|
'keywords': {
|
|
'Try': check_req('codeblock'),
|
|
'Catch': check_req('catchblock'),
|
|
'Else': check_req('codeblock', False),
|
|
'Finally': check_req('codeblock', False)}},
|
|
'Parallel': {
|
|
'keywords': {
|
|
'Limit': check_req('codeblock', False),
|
|
'Parallel': check_req('codeblock')},
|
|
},
|
|
'Repeat': {
|
|
'keywords': {
|
|
'Repeat': check_req('number'),
|
|
'Do': check_req('codeblock')}},
|
|
'If': {
|
|
'keywords': {
|
|
'If': check_req('predicate'),
|
|
'Then': check_req('codeblock'),
|
|
'Else': check_req('codeblock', False)}
|
|
},
|
|
'Break': {
|
|
'keywords': {
|
|
'Break': check_req('empty')}
|
|
},
|
|
'Return': {
|
|
'Return': check_req('expression'),
|
|
},
|
|
'While': {
|
|
'keywords': {
|
|
'While': check_req('predicate'),
|
|
'Do': check_req('codeblock')}
|
|
},
|
|
'For': {
|
|
'keywords': {
|
|
'For': check_req('string'),
|
|
'In': check_req('expression'),
|
|
'Do': check_req('codeblock')}
|
|
},
|
|
'Match': {
|
|
'keywords': {
|
|
'Match': check_req(('expression', 'codeblock')),
|
|
'Value': check_req('expression'),
|
|
'Default': check_req('codeblock'),
|
|
}
|
|
},
|
|
'Switch': {
|
|
'keywords': {
|
|
'Switch': check_req(('predicate', 'codeblock')),
|
|
'Default': check_req('codeblock')}
|
|
},
|
|
'Throw': {
|
|
'keywords': {
|
|
'Throw': check_req('string'),
|
|
'Message': check_req('string')}
|
|
},
|
|
'Continue': {
|
|
'keywords': {
|
|
'Continue': check_req('empty'),
|
|
}
|
|
},
|
|
'Rethrow': {
|
|
'keywords': {
|
|
'Rethrow': check_req('empty'),
|
|
}
|
|
},
|
|
}
|
|
|
|
|
|
class CheckCodeStructure(object):
|
|
def __init__(self):
|
|
self._check_mappings = {
|
|
'codeblock': self.codeblock,
|
|
'catchblock': self.catchblock,
|
|
'predicate': self.yaql,
|
|
'empty': self.empty,
|
|
'expression': self.yaql,
|
|
'string': self.string,
|
|
'number': self.yaql,
|
|
}
|
|
self._yaql_checker = yaql_checker.YaqlChecker()
|
|
|
|
def string(self, value):
|
|
if not isinstance(value, six.string_types):
|
|
yield error.report.E203('Value of "{0}" should be a string'
|
|
''.format(value), value)
|
|
|
|
def empty(self, value):
|
|
if value:
|
|
yield error.report.E200('Statement should be empty, not a '
|
|
'"{0}"'.format(value), value)
|
|
|
|
def yaql(self, value):
|
|
if not self._yaql_checker(value):
|
|
if isinstance(value, bool):
|
|
return
|
|
yield error.report.W202('"{0}" is not valid yaql expression'
|
|
''.format(value), value)
|
|
|
|
def catchblock(self, catchblock):
|
|
if isinstance(catchblock, list):
|
|
for block in catchblock:
|
|
yield self._single_catchblock(block)
|
|
else:
|
|
yield self._single_catchblock(catchblock)
|
|
|
|
def _single_catchblock(self, catchblock):
|
|
do = catchblock.get('Do')
|
|
if not do:
|
|
yield error.report.E204('Catch is missing "Do" block', catchblock)
|
|
else:
|
|
yield self.codeblock(do)
|
|
yield self.string(catchblock.get('With', ''))
|
|
yield self.string(catchblock.get('As', ''))
|
|
|
|
def codeblock(self, codeblocks):
|
|
if isinstance(codeblocks, list):
|
|
for block in codeblocks:
|
|
yield self._single_block(block)
|
|
else:
|
|
yield self._single_block(codeblocks)
|
|
|
|
def _check_assigment(self, block):
|
|
key = next(iter(block))
|
|
if not isinstance(key, six.string_types) or\
|
|
not ASSIGMENT_KEY.match(key):
|
|
yield error.report.E201('"{0}" is not valid variable name'
|
|
''.format(key), key)
|
|
|
|
def _single_block(self, block):
|
|
if isinstance(block, dict):
|
|
yield self._check_structure(block)
|
|
elif isinstance(block, six.string_types):
|
|
yield self.yaql(block)
|
|
|
|
def _run_check(self, check, value):
|
|
yield self._check_mappings[check](value)
|
|
|
|
def _check_structure(self, block):
|
|
for key, value in six.iteritems(CODE_STRUCTURE):
|
|
if key in block:
|
|
break
|
|
else:
|
|
if len(block.keys()) == 1:
|
|
yield self._check_assigment(block)
|
|
else:
|
|
yield error.report.E204('Wrong code structure/assigment. '
|
|
'Probably a typo', block)
|
|
return
|
|
|
|
keywords = value.get('keywords', {})
|
|
kset = set(keywords.keys())
|
|
block_keys_set = set(block.keys())
|
|
for missing in (kset - block_keys_set):
|
|
if keywords[missing]['required']:
|
|
yield error.report.E204('Missing keyword "{0}" for "{1}" '
|
|
'code structure'
|
|
.format(missing, key), block)
|
|
for unknown in (block_keys_set - kset - {key}):
|
|
yield error.report.E201('Unknown keyword "{0}" in "{1}"'
|
|
.format(unknown, key), unknown)
|
|
for ckey, cvalue in six.iteritems(keywords):
|
|
check = cvalue['check']
|
|
data = block.get(ckey)
|
|
if not data:
|
|
continue
|
|
if isinstance(check, tuple):
|
|
for left, right in six.iteritems(data):
|
|
yield self._run_check(check[0], left)
|
|
yield self._run_check(check[1], right)
|
|
else:
|
|
yield self._run_check(check, data)
|