Allow YAQL expressions in task's dependencies
Since Fuel 9.0 we have a task-based deployment turned on by default. That means our tasks are executing simultaneously on several nodes, and old static approach for specifying dependencies doesn't work anymore. In order to avoid dependency loops and make possible to rely on different tasks based on some condition, we've got to be able to calculate `cross-depends` and `cross-depended-by` attributes on fly. Fortunately, we already have traversal mechanism that evaluates YAQL expressions almost everywhere. So all we need to do is to remove validation limitation and allow to upload deployment tasks with YAQL expressions in those fields. Change-Id: I393dfb06f9e25ecd3ca79fddc84104a8cf993094 Partial-Bug: #1541309
This commit is contained in:
parent
a056929141
commit
886ab3087c
|
@ -18,22 +18,6 @@ from nailgun.consts import NODE_RESOLVE_POLICY
|
|||
from nailgun.consts import ORCHESTRATOR_TASK_TYPES
|
||||
|
||||
|
||||
RELATION_SCHEMA = {
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'type': 'object',
|
||||
'required': ['name'],
|
||||
'properties': {
|
||||
'name': {'type': 'string'},
|
||||
'role': {
|
||||
'oneOf': [
|
||||
{'type': 'string'},
|
||||
{'type': 'array'},
|
||||
]
|
||||
},
|
||||
'policy': {'type': 'string', 'enum': list(NODE_RESOLVE_POLICY)},
|
||||
}
|
||||
}
|
||||
|
||||
YAQL_EXP = {
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'type': 'object',
|
||||
|
@ -43,6 +27,25 @@ YAQL_EXP = {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
RELATION_SCHEMA = {
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'type': 'object',
|
||||
'required': ['name'],
|
||||
'properties': {
|
||||
'name': {
|
||||
'oneOf': [
|
||||
{'type': 'string'}, YAQL_EXP],
|
||||
},
|
||||
'role': {
|
||||
'oneOf': [
|
||||
{'type': 'string'}, {'type': 'array'}, YAQL_EXP]
|
||||
},
|
||||
'policy': {'type': 'string', 'enum': list(NODE_RESOLVE_POLICY)},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TASK_STRATEGY = {
|
||||
'$schema': 'http://json-schema.org/draft-04/schema#',
|
||||
'type': 'object',
|
||||
|
@ -79,8 +82,14 @@ TASK_SCHEMA = {
|
|||
'parameters': TASK_PARAMETERS,
|
||||
'required_for': {'type': 'array'},
|
||||
'requires': {'type': 'array'},
|
||||
'cross-depends': {'type': 'array', 'items': RELATION_SCHEMA},
|
||||
'cross-depended-by': {'type': 'array', 'items': RELATION_SCHEMA}
|
||||
'cross-depends': {
|
||||
'oneOf': [
|
||||
{'type': 'array', 'items': RELATION_SCHEMA}, YAQL_EXP]
|
||||
},
|
||||
'cross-depended-by': {
|
||||
'oneOf': [
|
||||
{'type': 'array', 'items': RELATION_SCHEMA}, YAQL_EXP]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ from nailgun import consts
|
|||
from nailgun.db.sqlalchemy.models.base import Base
|
||||
from nailgun.db.sqlalchemy.models.fields import JSON
|
||||
from nailgun.db.sqlalchemy.models.mutable import MutableDict
|
||||
from nailgun.db.sqlalchemy.models.mutable import MutableList
|
||||
|
||||
|
||||
class DeploymentGraph(Base):
|
||||
|
@ -110,12 +109,12 @@ class DeploymentGraphTask(Base):
|
|||
nullable=False)
|
||||
# cross-depended-by with hypen is deprecated notation
|
||||
cross_depended_by = sa.Column(
|
||||
MutableList.as_mutable(JSON),
|
||||
JSON,
|
||||
default=[],
|
||||
server_default='[]')
|
||||
# cross-depends with hypen is deprecated notation
|
||||
cross_depends = sa.Column(
|
||||
MutableList.as_mutable(JSON),
|
||||
JSON,
|
||||
default=[],
|
||||
server_default='[]')
|
||||
parameters = sa.Column(
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
# Copyright 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 textwrap
|
||||
|
||||
import jsonschema
|
||||
|
||||
from nailgun.api.v1.validators import orchestrator_graph
|
||||
from nailgun.test import base
|
||||
|
||||
|
||||
class TestGraphSolverTasksValidator(base.BaseUnitTest):
|
||||
|
||||
validator = orchestrator_graph.GraphSolverTasksValidator
|
||||
|
||||
def test_task_requires_list(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"requires": ["tools"]
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_required_for_list(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"required_for": ["tools"]
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_cross_depends_list(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"cross-depends": [{
|
||||
"name": "something"
|
||||
}]
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_cross_depends_yaql(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"cross-depends": {
|
||||
"yaql_exp": "$.do_something()"
|
||||
}
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_cross_depends_yaql_inside(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"cross-depends": [{
|
||||
"name": {
|
||||
"yaql_exp": "$.do_something()"
|
||||
},
|
||||
"role": {
|
||||
"yaql_exp": "$.do_something()"
|
||||
}
|
||||
}]
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_cross_depended_by_list(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"cross-depended-by": [{
|
||||
"name": "something",
|
||||
"role": "something"
|
||||
}]
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_cross_depended_by_yaql(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"cross-depended-by": {
|
||||
"yaql_exp": "$.do_something()"
|
||||
}
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_cross_depended_by_yaql_inside(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"cross-depended-by": [{
|
||||
"name": {
|
||||
"yaql_exp": "$.do_something()"
|
||||
},
|
||||
"role": {
|
||||
"yaql_exp": "$.do_something()"
|
||||
}
|
||||
}]
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_strategy_int(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"parameters": {
|
||||
"strategy": {
|
||||
"type": "parallel",
|
||||
"amount": 42
|
||||
}
|
||||
}
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
||||
|
||||
def test_task_strategy_yaql(self):
|
||||
self.assertNotRaises(
|
||||
jsonschema.exceptions.ValidationError,
|
||||
self.validator.validate_update,
|
||||
textwrap.dedent('''
|
||||
[{
|
||||
"id": "netconfig",
|
||||
"type": "puppet",
|
||||
"parameters": {
|
||||
"strategy": {
|
||||
"type": "parallel",
|
||||
"amount": {
|
||||
"yaql_exp": "$.do_something()"
|
||||
}
|
||||
}
|
||||
}
|
||||
}]
|
||||
'''),
|
||||
instance=None)
|
Loading…
Reference in New Issue