Completed
Pull Request — master (#2304)
by Arma
07:07
created

st2common.validators.workflow.mistral.MistralWorkflowValidator   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 75
Duplicated Lines 0 %
Metric Value
wmc 15
dl 0
loc 75
rs 10
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
0 ignored issues
show
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.liveaction -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.liveaction -> st2common.transport -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.db.liveaction -> st2common.util.action_db -> st2common.persistence.liveaction).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.db.action -> st2common.models.db.liveaction -> st2common.util.action_db -> st2common.persistence.action).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.liveaction -> st2common.transport -> st2common.transport.bootstrap_utils -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.execution -> st2common.transport -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.actionalias -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.runner -> st2common.persistence.base -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (st2common.models.api.base -> st2common.util.schema -> st2common.util.action_db -> st2common.persistence.action -> st2common.persistence.executionstate -> st2common.transport -> st2common.transport.reactor -> st2common.models.api.trace).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import re
17
18
import six
19
import yaml
20
from mistralclient.api import client as mistral
21
from oslo_config import cfg
22
23
from st2common.exceptions.workflow import WorkflowDefinitionException
24
from st2common import log as logging
25
from st2common.util.workflow import mistral as utils
26
from st2common.util.url import get_url_without_trailing_slash
27
from st2common.validators.workflow.base import WorkflowValidator
28
29
30
LOG = logging.getLogger(__name__)
31
32
33
def get_validator():
34
    return MistralWorkflowValidator()
35
36
37
class MistralWorkflowValidator(WorkflowValidator):
38
39
    url = get_url_without_trailing_slash(cfg.CONF.mistral.v2_base_url)
40
41
    def __init__(self):
42
        super(MistralWorkflowValidator, self).__init__()
43
        self._client = mistral.client(
44
            mistral_url=self.url,
45
            username=cfg.CONF.mistral.keystone_username,
46
            api_key=cfg.CONF.mistral.keystone_password,
47
            project_name=cfg.CONF.mistral.keystone_project_name,
48
            auth_url=cfg.CONF.mistral.keystone_auth_url)
49
50
    @staticmethod
51
    def parse(message):
52
        result = {
53
            'type': None,
54
            'path': None,
55
            'message': message
56
        }
57
58
        # Check message for schema specific error.
59
        m1 = re.search('^Invalid DSL: (.+)\n', message)
60
61
        if m1:
62
            result['type'] = 'schema'
63
            result['message'] = m1.group(1)
64
65
            path = re.search('On instance(.+):', message)
66
67
            if path:
68
                result['path'] = path.group(1).strip("[']").replace("']['", ".")
69
70
        # Check message for YAQL specific error.
71
        m2 = re.search('^Parse error: (.+)$', message)
72
73
        if m2:
74
            result['type'] = 'yaql'
75
            result['message'] = m2.group(1)
76
77
        # Check message for action parameters specific error.
78
        if any([candidate in message
79
                for candidate in ['Missing required parameters',
80
                                  'Unexpected parameters',
81
                                  'st2.callback is deprecated']]):
82
            result['type'] = 'action'
83
84
        return result
85
86
    def validate(self, definition):
87
        def_dict = yaml.safe_load(definition)
88
        is_workbook = ('workflows' in def_dict)
89
90
        if not is_workbook:
91
            # Non-workbook definition containing multiple workflows is not supported.
92
            if len([k for k, _ in six.iteritems(def_dict) if k != 'version']) != 1:
93
                return [self.parse('Multiple workflows is not supported workflow '
94
                                   'only (not a workbook) definition.')]
95
96
        # Select validation function.
97
        func = self._client.workbooks.validate if is_workbook else self._client.workflows.validate
98
99
        # Validate before custom DSL transformation.
100
        result = func(definition)
101
102
        if not result.get('valid', None):
103
            return [self.parse(result.get('error', 'Unknown exception.'))]
104
105
        try:
106
            # Run custom DSL transformer to check action parameters.
107
            utils.transform_definition(def_dict)
108
        except WorkflowDefinitionException as e:
109
            return [self.parse(e.message)]
110
111
        return []
112