Browse Source

Remove extra a specification validation

Currently when we get a specification using the instantiate_spec function,
we always validate their schema and semantics over and over again.
To prevent it we add new validate parameter to a Spec class.
The validate parameter must be True when we create a workflow, workbook
or action using a mistral-api. In all other cases, it must be False.

Change-Id: Ia450ea9635bc75c204fe031cfeeab154f1d03862
Closes-Bug: #1738769
Signed-off-by: Vitalii Solodilov <mcdkr@yandex.ru>
Vitalii Solodilov 8 months ago
parent
commit
4bf03d8e7d

+ 1
- 1
mistral/api/controllers/v2/validation.py View File

@@ -32,7 +32,7 @@ class SpecValidationController(rest.RestController):
32 32
         definition = pecan.request.text
33 33
 
34 34
         try:
35
-            self._parse_func(definition)
35
+            self._parse_func(definition, validate=True)
36 36
         except exc.DSLParsingException as e:
37 37
             return {'valid': False, 'error': str(e)}
38 38
 

+ 29
- 18
mistral/lang/base.py View File

@@ -52,7 +52,7 @@ ALL = (
52 52
 PARAMS_PTRN = re.compile("([-_\w]+)=(%s)" % "|".join(ALL))
53 53
 
54 54
 
55
-def instantiate_spec(spec_cls, data):
55
+def instantiate_spec(spec_cls, data, validate=False):
56 56
     """Instantiates specification accounting for specification hierarchies.
57 57
 
58 58
     :param spec_cls: Specification concrete or base class. In case if base
@@ -60,17 +60,22 @@ def instantiate_spec(spec_cls, data):
60 60
         _polymorphic_key and _polymorphic_value in order to find a concrete
61 61
         class that needs to be instantiated.
62 62
     :param data: Raw specification data as a dictionary.
63
+    :type data: dict
64
+    :param validate: If it equals False then semantics and schema validation
65
+        will be skipped
66
+    :type validate: bool
63 67
     """
64 68
 
65 69
     if issubclass(spec_cls, BaseSpecList):
66 70
         # Ignore polymorphic search for specification lists because
67 71
         # it doesn't make sense for them.
68
-        return spec_cls(data)
72
+        return spec_cls(data, validate)
69 73
 
70 74
     if not hasattr(spec_cls, '_polymorphic_key'):
71
-        spec = spec_cls(data)
75
+        spec = spec_cls(data, validate)
72 76
 
73
-        spec.validate_semantics()
77
+        if validate:
78
+            spec.validate_semantics()
74 79
 
75 80
         return spec
76 81
 
@@ -101,9 +106,10 @@ def instantiate_spec(spec_cls, data):
101 106
             )
102 107
 
103 108
         if cls._polymorphic_value == data.get(key_name, key_default):
104
-            spec = cls(data)
109
+            spec = cls(data, validate)
105 110
 
106
-            spec.validate_semantics()
111
+            if validate:
112
+                spec.validate_semantics()
107 113
 
108 114
             return spec
109 115
 
@@ -176,10 +182,12 @@ class BaseSpec(object):
176 182
 
177 183
         return schema
178 184
 
179
-    def __init__(self, data):
185
+    def __init__(self, data, validate):
180 186
         self._data = data
187
+        self._validate = validate
181 188
 
182
-        self.validate_schema()
189
+        if validate:
190
+            self.validate_schema()
183 191
 
184 192
     def validate_schema(self):
185 193
         """Validates DSL entity schema that this specification represents.
@@ -227,10 +235,10 @@ class BaseSpec(object):
227 235
     def _spec_property(self, prop_name, spec_cls):
228 236
         prop_val = self._data.get(prop_name)
229 237
 
230
-        return (
231
-            instantiate_spec(spec_cls, prop_val) if prop_val is not None
232
-            else None
233
-        )
238
+        if prop_val is not None:
239
+            return instantiate_spec(spec_cls, prop_val, self._validate)
240
+        else:
241
+            return None
234 242
 
235 243
     def _group_spec(self, spec_cls, *prop_names):
236 244
         if not prop_names:
@@ -244,7 +252,7 @@ class BaseSpec(object):
244 252
             if prop_val:
245 253
                 data[prop_name] = prop_val
246 254
 
247
-        return instantiate_spec(spec_cls, data)
255
+        return instantiate_spec(spec_cls, data, self._validate)
248 256
 
249 257
     def _inject_version(self, prop_names):
250 258
         for prop_name in prop_names:
@@ -322,8 +330,8 @@ class BaseListSpec(BaseSpec):
322 330
         "required": ["version"],
323 331
     }
324 332
 
325
-    def __init__(self, data):
326
-        super(BaseListSpec, self).__init__(data)
333
+    def __init__(self, data, validate):
334
+        super(BaseListSpec, self).__init__(data, validate)
327 335
 
328 336
         self.items = []
329 337
 
@@ -331,7 +339,10 @@ class BaseListSpec(BaseSpec):
331 339
             if k != 'version':
332 340
                 v['name'] = k
333 341
                 self._inject_version([k])
334
-                self.items.append(instantiate_spec(self.item_class, v))
342
+
343
+                self.items.append(
344
+                    instantiate_spec(self.item_class, v, validate)
345
+                )
335 346
 
336 347
     def validate_schema(self):
337 348
         super(BaseListSpec, self).validate_schema()
@@ -357,7 +368,7 @@ class BaseSpecList(object):
357 368
 
358 369
     _version = '2.0'
359 370
 
360
-    def __init__(self, data):
371
+    def __init__(self, data, validate):
361 372
         self.items = {}
362 373
 
363 374
         for k, v in data.items():
@@ -369,7 +380,7 @@ class BaseSpecList(object):
369 380
                     v['name'] = k
370 381
                     v['version'] = self._version
371 382
 
372
-                self.items[k] = instantiate_spec(self.item_class, v)
383
+                self.items[k] = instantiate_spec(self.item_class, v, validate)
373 384
 
374 385
     def item_keys(self):
375 386
         return self.items.keys()

+ 18
- 12
mistral/lang/parser.py View File

@@ -80,15 +80,17 @@ def _get_spec_version(spec_dict):
80 80
 # Factory methods to get specifications either from raw YAML formatted text or
81 81
 # from dictionaries parsed from YAML formatted text.
82 82
 
83
-def get_workbook_spec(spec_dict):
83
+def get_workbook_spec(spec_dict, validate):
84 84
     if _get_spec_version(spec_dict) == V2_0:
85
-        return base.instantiate_spec(wb_v2.WorkbookSpec, spec_dict)
85
+        return base.instantiate_spec(
86
+            wb_v2.WorkbookSpec, spec_dict, validate
87
+        )
86 88
 
87 89
     return None
88 90
 
89 91
 
90
-def get_workbook_spec_from_yaml(text):
91
-    return get_workbook_spec(parse_yaml(text))
92
+def get_workbook_spec_from_yaml(text, validate=True):
93
+    return get_workbook_spec(parse_yaml(text), validate)
92 94
 
93 95
 
94 96
 def get_action_spec(spec_dict):
@@ -106,12 +108,14 @@ def get_action_spec_from_yaml(text, action_name):
106 108
     return get_action_spec(spec_dict)
107 109
 
108 110
 
109
-def get_action_list_spec(spec_dict):
110
-    return base.instantiate_spec(actions_v2.ActionListSpec, spec_dict)
111
+def get_action_list_spec(spec_dict, validate):
112
+    return base.instantiate_spec(
113
+        actions_v2.ActionListSpec, spec_dict, validate
114
+    )
111 115
 
112 116
 
113
-def get_action_list_spec_from_yaml(text):
114
-    return get_action_list_spec(parse_yaml(text))
117
+def get_action_list_spec_from_yaml(text, validate=True):
118
+    return get_action_list_spec(parse_yaml(text), validate=validate)
115 119
 
116 120
 
117 121
 def get_workflow_spec(spec_dict):
@@ -130,16 +134,18 @@ def get_workflow_spec(spec_dict):
130 134
     return None
131 135
 
132 136
 
133
-def get_workflow_list_spec(spec_dict):
134
-    return base.instantiate_spec(wf_v2.WorkflowListSpec, spec_dict)
137
+def get_workflow_list_spec(spec_dict, validate):
138
+    return base.instantiate_spec(
139
+        wf_v2.WorkflowListSpec, spec_dict, validate
140
+    )
135 141
 
136 142
 
137 143
 def get_workflow_spec_from_yaml(text):
138 144
     return get_workflow_spec(parse_yaml(text))
139 145
 
140 146
 
141
-def get_workflow_list_spec_from_yaml(text):
142
-    return get_workflow_list_spec(parse_yaml(text))
147
+def get_workflow_list_spec_from_yaml(text, validate=True):
148
+    return get_workflow_list_spec(parse_yaml(text), validate)
143 149
 
144 150
 
145 151
 def get_task_spec(spec_dict):

+ 2
- 2
mistral/lang/v2/actions.py View File

@@ -34,8 +34,8 @@ class ActionSpec(base.BaseSpec):
34 34
         "additionalProperties": False
35 35
     }
36 36
 
37
-    def __init__(self, data):
38
-        super(ActionSpec, self).__init__(data)
37
+    def __init__(self, data, validate):
38
+        super(ActionSpec, self).__init__(data, validate)
39 39
 
40 40
         self._name = data['name']
41 41
         self._description = data.get('description')

+ 2
- 2
mistral/lang/v2/on_clause.py View File

@@ -94,8 +94,8 @@ class OnClauseSpec(base.BaseSpec):
94 94
         ]
95 95
     }
96 96
 
97
-    def __init__(self, data):
98
-        super(OnClauseSpec, self).__init__(data)
97
+    def __init__(self, data, validate):
98
+        super(OnClauseSpec, self).__init__(data, validate)
99 99
 
100 100
         if not isinstance(data, dict):
101 101
             # Old simple schema.

+ 2
- 2
mistral/lang/v2/policies.py View File

@@ -37,8 +37,8 @@ class PoliciesSpec(base.BaseSpec):
37 37
     def get_schema(cls, includes=['definitions']):
38 38
         return super(PoliciesSpec, cls).get_schema(includes)
39 39
 
40
-    def __init__(self, data):
41
-        super(PoliciesSpec, self).__init__(data)
40
+    def __init__(self, data, validate):
41
+        super(PoliciesSpec, self).__init__(data, validate)
42 42
 
43 43
         self._retry = self._spec_property('retry', retry_policy.RetrySpec)
44 44
         self._wait_before = data.get('wait-before', 0)

+ 2
- 2
mistral/lang/v2/publish.py View File

@@ -29,8 +29,8 @@ class PublishSpec(base.BaseSpec):
29 29
         "additionalProperties": False
30 30
     }
31 31
 
32
-    def __init__(self, data):
33
-        super(PublishSpec, self).__init__(data)
32
+    def __init__(self, data, validate):
33
+        super(PublishSpec, self).__init__(data, validate)
34 34
 
35 35
         self._branch = self._data.get('branch')
36 36
         self._global = self._data.get('global')

+ 2
- 2
mistral/lang/v2/retry_policy.py View File

@@ -54,10 +54,10 @@ class RetrySpec(base.BaseSpec):
54 54
     def get_schema(cls, includes=['definitions']):
55 55
         return super(RetrySpec, cls).get_schema(includes)
56 56
 
57
-    def __init__(self, data):
57
+    def __init__(self, data, validate):
58 58
         data = self._transform_retry_one_line(data)
59 59
 
60
-        super(RetrySpec, self).__init__(data)
60
+        super(RetrySpec, self).__init__(data, validate)
61 61
 
62 62
         self._break_on = data.get('break-on')
63 63
         self._count = data.get('count')

+ 2
- 2
mistral/lang/v2/task_defaults.py View File

@@ -53,8 +53,8 @@ class TaskDefaultsSpec(base.BaseSpec):
53 53
     def get_schema(cls, includes=['definitions']):
54 54
         return super(TaskDefaultsSpec, cls).get_schema(includes)
55 55
 
56
-    def __init__(self, data):
57
-        super(TaskDefaultsSpec, self).__init__(data)
56
+    def __init__(self, data, validate):
57
+        super(TaskDefaultsSpec, self).__init__(data, validate)
58 58
 
59 59
         self._policies = self._group_spec(
60 60
             policies.PoliciesSpec,

+ 12
- 8
mistral/lang/v2/tasks.py View File

@@ -107,8 +107,8 @@ class TaskSpec(base.BaseSpec):
107 107
         ]
108 108
     }
109 109
 
110
-    def __init__(self, data):
111
-        super(TaskSpec, self).__init__(data)
110
+    def __init__(self, data, validate):
111
+        super(TaskSpec, self).__init__(data, validate)
112 112
 
113 113
         self._name = data['name']
114 114
         self._description = data.get('description')
@@ -248,10 +248,14 @@ class TaskSpec(base.BaseSpec):
248 248
         spec = None
249 249
 
250 250
         if state == states.SUCCESS and self._publish:
251
-            spec = publish.PublishSpec({'branch': self._publish})
251
+            spec = publish.PublishSpec(
252
+                {'branch': self._publish},
253
+                validate=self._validate
254
+            )
252 255
         elif state == states.ERROR and self._publish_on_error:
253 256
             spec = publish.PublishSpec(
254
-                {'branch': self._publish_on_error}
257
+                {'branch': self._publish_on_error},
258
+                validate=self._validate
255 259
             )
256 260
 
257 261
         return spec
@@ -291,8 +295,8 @@ class DirectWorkflowTaskSpec(TaskSpec):
291 295
         _direct_workflow_schema
292 296
     )
293 297
 
294
-    def __init__(self, data):
295
-        super(DirectWorkflowTaskSpec, self).__init__(data)
298
+    def __init__(self, data, validate):
299
+        super(DirectWorkflowTaskSpec, self).__init__(data, validate)
296 300
 
297 301
         self._join = data.get('join')
298 302
 
@@ -376,8 +380,8 @@ class ReverseWorkflowTaskSpec(TaskSpec):
376 380
         _reverse_workflow_schema
377 381
     )
378 382
 
379
-    def __init__(self, data):
380
-        super(ReverseWorkflowTaskSpec, self).__init__(data)
383
+    def __init__(self, data, validate):
384
+        super(ReverseWorkflowTaskSpec, self).__init__(data, validate)
381 385
 
382 386
         self._requires = data.get('requires', [])
383 387
 

+ 2
- 2
mistral/lang/v2/workbook.py View File

@@ -51,8 +51,8 @@ class WorkbookSpec(base.BaseSpec):
51 51
         "additionalProperties": False
52 52
     }
53 53
 
54
-    def __init__(self, data):
55
-        super(WorkbookSpec, self).__init__(data)
54
+    def __init__(self, data, validate):
55
+        super(WorkbookSpec, self).__init__(data, validate)
56 56
 
57 57
         self._inject_version(['actions', 'workflows'])
58 58
 

+ 4
- 4
mistral/lang/v2/workflows.py View File

@@ -44,8 +44,8 @@ class WorkflowSpec(base.BaseSpec):
44 44
         "additionalProperties": False
45 45
     }
46 46
 
47
-    def __init__(self, data):
48
-        super(WorkflowSpec, self).__init__(data)
47
+    def __init__(self, data, validate):
48
+        super(WorkflowSpec, self).__init__(data, validate)
49 49
 
50 50
         self._name = data['name']
51 51
         self._description = data.get('description')
@@ -152,8 +152,8 @@ class DirectWorkflowSpec(WorkflowSpec):
152 152
         }
153 153
     }
154 154
 
155
-    def __init__(self, data):
156
-        super(DirectWorkflowSpec, self).__init__(data)
155
+    def __init__(self, data, validate):
156
+        super(DirectWorkflowSpec, self).__init__(data, validate)
157 157
 
158 158
         # Init simple dictionary based caches for inbound and
159 159
         # outbound task specifications. In fact, we don't need

+ 6
- 2
mistral/services/workbooks.py View File

@@ -18,7 +18,9 @@ from mistral.services import actions
18 18
 
19 19
 
20 20
 def create_workbook_v2(definition, namespace='', scope='private'):
21
-    wb_spec = spec_parser.get_workbook_spec_from_yaml(definition)
21
+    wb_spec = spec_parser.get_workbook_spec_from_yaml(
22
+        definition, validate=True
23
+    )
22 24
 
23 25
     wb_values = _get_workbook_values(
24 26
         wb_spec,
@@ -36,7 +38,9 @@ def create_workbook_v2(definition, namespace='', scope='private'):
36 38
 
37 39
 
38 40
 def update_workbook_v2(definition, namespace='', scope='private'):
39
-    wb_spec = spec_parser.get_workbook_spec_from_yaml(definition)
41
+    wb_spec = spec_parser.get_workbook_spec_from_yaml(
42
+        definition, validate=True
43
+    )
40 44
 
41 45
     values = _get_workbook_values(wb_spec, definition, scope, namespace)
42 46
 

+ 6
- 2
mistral/services/workflows.py View File

@@ -54,7 +54,9 @@ def sync_db():
54 54
 def create_workflows(definition, scope='private', is_system=False,
55 55
                      run_in_tx=True, namespace=''):
56 56
     LOG.debug("Creating workflows")
57
-    wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml(definition)
57
+    wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml(
58
+        definition, validate=True
59
+    )
58 60
     db_wfs = []
59 61
 
60 62
     if run_in_tx:
@@ -98,7 +100,9 @@ def update_workflows(definition, scope='private', identifier=None,
98 100
                      namespace=''):
99 101
     LOG.debug("Updating workflows")
100 102
 
101
-    wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml(definition)
103
+    wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml(
104
+        definition, validate=True
105
+    )
102 106
     wfs = wf_list_spec.get_workflows()
103 107
 
104 108
     if identifier and len(wfs) > 1:

Loading…
Cancel
Save