diff --git a/mistral/api/controllers/v2/validation.py b/mistral/api/controllers/v2/validation.py index 5c91fda75..e43691315 100644 --- a/mistral/api/controllers/v2/validation.py +++ b/mistral/api/controllers/v2/validation.py @@ -32,7 +32,7 @@ class SpecValidationController(rest.RestController): definition = pecan.request.text try: - self._parse_func(definition) + self._parse_func(definition, validate=True) except exc.DSLParsingException as e: return {'valid': False, 'error': str(e)} diff --git a/mistral/lang/base.py b/mistral/lang/base.py index 4fdb03ab4..57fed494a 100644 --- a/mistral/lang/base.py +++ b/mistral/lang/base.py @@ -52,7 +52,7 @@ ALL = ( PARAMS_PTRN = re.compile("([-_\w]+)=(%s)" % "|".join(ALL)) -def instantiate_spec(spec_cls, data): +def instantiate_spec(spec_cls, data, validate=False): """Instantiates specification accounting for specification hierarchies. :param spec_cls: Specification concrete or base class. In case if base @@ -60,17 +60,22 @@ def instantiate_spec(spec_cls, data): _polymorphic_key and _polymorphic_value in order to find a concrete class that needs to be instantiated. :param data: Raw specification data as a dictionary. + :type data: dict + :param validate: If it equals False then semantics and schema validation + will be skipped + :type validate: bool """ if issubclass(spec_cls, BaseSpecList): # Ignore polymorphic search for specification lists because # it doesn't make sense for them. - return spec_cls(data) + return spec_cls(data, validate) if not hasattr(spec_cls, '_polymorphic_key'): - spec = spec_cls(data) + spec = spec_cls(data, validate) - spec.validate_semantics() + if validate: + spec.validate_semantics() return spec @@ -101,9 +106,10 @@ def instantiate_spec(spec_cls, data): ) if cls._polymorphic_value == data.get(key_name, key_default): - spec = cls(data) + spec = cls(data, validate) - spec.validate_semantics() + if validate: + spec.validate_semantics() return spec @@ -176,10 +182,12 @@ class BaseSpec(object): return schema - def __init__(self, data): + def __init__(self, data, validate): self._data = data + self._validate = validate - self.validate_schema() + if validate: + self.validate_schema() def validate_schema(self): """Validates DSL entity schema that this specification represents. @@ -227,10 +235,10 @@ class BaseSpec(object): def _spec_property(self, prop_name, spec_cls): prop_val = self._data.get(prop_name) - return ( - instantiate_spec(spec_cls, prop_val) if prop_val is not None - else None - ) + if prop_val is not None: + return instantiate_spec(spec_cls, prop_val, self._validate) + else: + return None def _group_spec(self, spec_cls, *prop_names): if not prop_names: @@ -244,7 +252,7 @@ class BaseSpec(object): if prop_val: data[prop_name] = prop_val - return instantiate_spec(spec_cls, data) + return instantiate_spec(spec_cls, data, self._validate) def _inject_version(self, prop_names): for prop_name in prop_names: @@ -322,8 +330,8 @@ class BaseListSpec(BaseSpec): "required": ["version"], } - def __init__(self, data): - super(BaseListSpec, self).__init__(data) + def __init__(self, data, validate): + super(BaseListSpec, self).__init__(data, validate) self.items = [] @@ -331,7 +339,10 @@ class BaseListSpec(BaseSpec): if k != 'version': v['name'] = k self._inject_version([k]) - self.items.append(instantiate_spec(self.item_class, v)) + + self.items.append( + instantiate_spec(self.item_class, v, validate) + ) def validate_schema(self): super(BaseListSpec, self).validate_schema() @@ -357,7 +368,7 @@ class BaseSpecList(object): _version = '2.0' - def __init__(self, data): + def __init__(self, data, validate): self.items = {} for k, v in data.items(): @@ -369,7 +380,7 @@ class BaseSpecList(object): v['name'] = k v['version'] = self._version - self.items[k] = instantiate_spec(self.item_class, v) + self.items[k] = instantiate_spec(self.item_class, v, validate) def item_keys(self): return self.items.keys() diff --git a/mistral/lang/parser.py b/mistral/lang/parser.py index dc92c184a..0af0bf9b9 100644 --- a/mistral/lang/parser.py +++ b/mistral/lang/parser.py @@ -80,15 +80,17 @@ def _get_spec_version(spec_dict): # Factory methods to get specifications either from raw YAML formatted text or # from dictionaries parsed from YAML formatted text. -def get_workbook_spec(spec_dict): +def get_workbook_spec(spec_dict, validate): if _get_spec_version(spec_dict) == V2_0: - return base.instantiate_spec(wb_v2.WorkbookSpec, spec_dict) + return base.instantiate_spec( + wb_v2.WorkbookSpec, spec_dict, validate + ) return None -def get_workbook_spec_from_yaml(text): - return get_workbook_spec(parse_yaml(text)) +def get_workbook_spec_from_yaml(text, validate=True): + return get_workbook_spec(parse_yaml(text), validate) def get_action_spec(spec_dict): @@ -106,12 +108,14 @@ def get_action_spec_from_yaml(text, action_name): return get_action_spec(spec_dict) -def get_action_list_spec(spec_dict): - return base.instantiate_spec(actions_v2.ActionListSpec, spec_dict) +def get_action_list_spec(spec_dict, validate): + return base.instantiate_spec( + actions_v2.ActionListSpec, spec_dict, validate + ) -def get_action_list_spec_from_yaml(text): - return get_action_list_spec(parse_yaml(text)) +def get_action_list_spec_from_yaml(text, validate=True): + return get_action_list_spec(parse_yaml(text), validate=validate) def get_workflow_spec(spec_dict): @@ -130,16 +134,18 @@ def get_workflow_spec(spec_dict): return None -def get_workflow_list_spec(spec_dict): - return base.instantiate_spec(wf_v2.WorkflowListSpec, spec_dict) +def get_workflow_list_spec(spec_dict, validate): + return base.instantiate_spec( + wf_v2.WorkflowListSpec, spec_dict, validate + ) def get_workflow_spec_from_yaml(text): return get_workflow_spec(parse_yaml(text)) -def get_workflow_list_spec_from_yaml(text): - return get_workflow_list_spec(parse_yaml(text)) +def get_workflow_list_spec_from_yaml(text, validate=True): + return get_workflow_list_spec(parse_yaml(text), validate) def get_task_spec(spec_dict): diff --git a/mistral/lang/v2/actions.py b/mistral/lang/v2/actions.py index 2d37d9d6a..a736967a5 100644 --- a/mistral/lang/v2/actions.py +++ b/mistral/lang/v2/actions.py @@ -34,8 +34,8 @@ class ActionSpec(base.BaseSpec): "additionalProperties": False } - def __init__(self, data): - super(ActionSpec, self).__init__(data) + def __init__(self, data, validate): + super(ActionSpec, self).__init__(data, validate) self._name = data['name'] self._description = data.get('description') diff --git a/mistral/lang/v2/on_clause.py b/mistral/lang/v2/on_clause.py index cb65312af..e5c1bb7e5 100644 --- a/mistral/lang/v2/on_clause.py +++ b/mistral/lang/v2/on_clause.py @@ -94,8 +94,8 @@ class OnClauseSpec(base.BaseSpec): ] } - def __init__(self, data): - super(OnClauseSpec, self).__init__(data) + def __init__(self, data, validate): + super(OnClauseSpec, self).__init__(data, validate) if not isinstance(data, dict): # Old simple schema. diff --git a/mistral/lang/v2/policies.py b/mistral/lang/v2/policies.py index 775b07e98..b18e0d195 100644 --- a/mistral/lang/v2/policies.py +++ b/mistral/lang/v2/policies.py @@ -37,8 +37,8 @@ class PoliciesSpec(base.BaseSpec): def get_schema(cls, includes=['definitions']): return super(PoliciesSpec, cls).get_schema(includes) - def __init__(self, data): - super(PoliciesSpec, self).__init__(data) + def __init__(self, data, validate): + super(PoliciesSpec, self).__init__(data, validate) self._retry = self._spec_property('retry', retry_policy.RetrySpec) self._wait_before = data.get('wait-before', 0) diff --git a/mistral/lang/v2/publish.py b/mistral/lang/v2/publish.py index a3adc1031..9515aa338 100644 --- a/mistral/lang/v2/publish.py +++ b/mistral/lang/v2/publish.py @@ -29,8 +29,8 @@ class PublishSpec(base.BaseSpec): "additionalProperties": False } - def __init__(self, data): - super(PublishSpec, self).__init__(data) + def __init__(self, data, validate): + super(PublishSpec, self).__init__(data, validate) self._branch = self._data.get('branch') self._global = self._data.get('global') diff --git a/mistral/lang/v2/retry_policy.py b/mistral/lang/v2/retry_policy.py index 224d1886d..cc69149f3 100644 --- a/mistral/lang/v2/retry_policy.py +++ b/mistral/lang/v2/retry_policy.py @@ -54,10 +54,10 @@ class RetrySpec(base.BaseSpec): def get_schema(cls, includes=['definitions']): return super(RetrySpec, cls).get_schema(includes) - def __init__(self, data): + def __init__(self, data, validate): data = self._transform_retry_one_line(data) - super(RetrySpec, self).__init__(data) + super(RetrySpec, self).__init__(data, validate) self._break_on = data.get('break-on') self._count = data.get('count') diff --git a/mistral/lang/v2/task_defaults.py b/mistral/lang/v2/task_defaults.py index acb1b4803..08f8bee72 100644 --- a/mistral/lang/v2/task_defaults.py +++ b/mistral/lang/v2/task_defaults.py @@ -53,8 +53,8 @@ class TaskDefaultsSpec(base.BaseSpec): def get_schema(cls, includes=['definitions']): return super(TaskDefaultsSpec, cls).get_schema(includes) - def __init__(self, data): - super(TaskDefaultsSpec, self).__init__(data) + def __init__(self, data, validate): + super(TaskDefaultsSpec, self).__init__(data, validate) self._policies = self._group_spec( policies.PoliciesSpec, diff --git a/mistral/lang/v2/tasks.py b/mistral/lang/v2/tasks.py index 96ee80403..70ad9f1b2 100644 --- a/mistral/lang/v2/tasks.py +++ b/mistral/lang/v2/tasks.py @@ -107,8 +107,8 @@ class TaskSpec(base.BaseSpec): ] } - def __init__(self, data): - super(TaskSpec, self).__init__(data) + def __init__(self, data, validate): + super(TaskSpec, self).__init__(data, validate) self._name = data['name'] self._description = data.get('description') @@ -248,10 +248,14 @@ class TaskSpec(base.BaseSpec): spec = None if state == states.SUCCESS and self._publish: - spec = publish.PublishSpec({'branch': self._publish}) + spec = publish.PublishSpec( + {'branch': self._publish}, + validate=self._validate + ) elif state == states.ERROR and self._publish_on_error: spec = publish.PublishSpec( - {'branch': self._publish_on_error} + {'branch': self._publish_on_error}, + validate=self._validate ) return spec @@ -291,8 +295,8 @@ class DirectWorkflowTaskSpec(TaskSpec): _direct_workflow_schema ) - def __init__(self, data): - super(DirectWorkflowTaskSpec, self).__init__(data) + def __init__(self, data, validate): + super(DirectWorkflowTaskSpec, self).__init__(data, validate) self._join = data.get('join') @@ -376,8 +380,8 @@ class ReverseWorkflowTaskSpec(TaskSpec): _reverse_workflow_schema ) - def __init__(self, data): - super(ReverseWorkflowTaskSpec, self).__init__(data) + def __init__(self, data, validate): + super(ReverseWorkflowTaskSpec, self).__init__(data, validate) self._requires = data.get('requires', []) diff --git a/mistral/lang/v2/workbook.py b/mistral/lang/v2/workbook.py index 72c93d5f9..22a501ab1 100644 --- a/mistral/lang/v2/workbook.py +++ b/mistral/lang/v2/workbook.py @@ -51,8 +51,8 @@ class WorkbookSpec(base.BaseSpec): "additionalProperties": False } - def __init__(self, data): - super(WorkbookSpec, self).__init__(data) + def __init__(self, data, validate): + super(WorkbookSpec, self).__init__(data, validate) self._inject_version(['actions', 'workflows']) diff --git a/mistral/lang/v2/workflows.py b/mistral/lang/v2/workflows.py index 2d60627c4..af1f1cadc 100644 --- a/mistral/lang/v2/workflows.py +++ b/mistral/lang/v2/workflows.py @@ -44,8 +44,8 @@ class WorkflowSpec(base.BaseSpec): "additionalProperties": False } - def __init__(self, data): - super(WorkflowSpec, self).__init__(data) + def __init__(self, data, validate): + super(WorkflowSpec, self).__init__(data, validate) self._name = data['name'] self._description = data.get('description') @@ -152,8 +152,8 @@ class DirectWorkflowSpec(WorkflowSpec): } } - def __init__(self, data): - super(DirectWorkflowSpec, self).__init__(data) + def __init__(self, data, validate): + super(DirectWorkflowSpec, self).__init__(data, validate) # Init simple dictionary based caches for inbound and # outbound task specifications. In fact, we don't need diff --git a/mistral/services/workbooks.py b/mistral/services/workbooks.py index f55333cda..77fafebc6 100644 --- a/mistral/services/workbooks.py +++ b/mistral/services/workbooks.py @@ -18,7 +18,9 @@ from mistral.services import actions def create_workbook_v2(definition, namespace='', scope='private'): - wb_spec = spec_parser.get_workbook_spec_from_yaml(definition) + wb_spec = spec_parser.get_workbook_spec_from_yaml( + definition, validate=True + ) wb_values = _get_workbook_values( wb_spec, @@ -36,7 +38,9 @@ def create_workbook_v2(definition, namespace='', scope='private'): def update_workbook_v2(definition, namespace='', scope='private'): - wb_spec = spec_parser.get_workbook_spec_from_yaml(definition) + wb_spec = spec_parser.get_workbook_spec_from_yaml( + definition, validate=True + ) values = _get_workbook_values(wb_spec, definition, scope, namespace) diff --git a/mistral/services/workflows.py b/mistral/services/workflows.py index c9e9a1d8f..7a49ccb00 100644 --- a/mistral/services/workflows.py +++ b/mistral/services/workflows.py @@ -54,7 +54,9 @@ def sync_db(): def create_workflows(definition, scope='private', is_system=False, run_in_tx=True, namespace=''): LOG.debug("Creating workflows") - wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml(definition) + wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml( + definition, validate=True + ) db_wfs = [] if run_in_tx: @@ -98,7 +100,9 @@ def update_workflows(definition, scope='private', identifier=None, namespace=''): LOG.debug("Updating workflows") - wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml(definition) + wf_list_spec = spec_parser.get_workflow_list_spec_from_yaml( + definition, validate=True + ) wfs = wf_list_spec.get_workflows() if identifier and len(wfs) > 1: