Merge "Skip validation if depends on not created resource"
This commit is contained in:
commit
cdfa0f9e59
@ -17,6 +17,7 @@ import six
|
|||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
from heat.engine import constraints as constr
|
from heat.engine import constraints as constr
|
||||||
|
from heat.engine import function
|
||||||
from heat.engine import parameters
|
from heat.engine import parameters
|
||||||
from heat.engine import support
|
from heat.engine import support
|
||||||
|
|
||||||
@ -382,7 +383,13 @@ class Properties(collections.Mapping):
|
|||||||
|
|
||||||
if key in self.data:
|
if key in self.data:
|
||||||
try:
|
try:
|
||||||
value = self.resolve(self.data[key])
|
unresolved_value = self.data[key]
|
||||||
|
if validate:
|
||||||
|
deps = function.dependencies(unresolved_value)
|
||||||
|
if any(res.action == res.INIT for res in deps):
|
||||||
|
validate = False
|
||||||
|
|
||||||
|
value = self.resolve(unresolved_value)
|
||||||
return prop.get_value(value, validate)
|
return prop.get_value(value, validate)
|
||||||
# the resolver function could raise any number of exceptions,
|
# the resolver function could raise any number of exceptions,
|
||||||
# so handle this generically
|
# so handle this generically
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
from heat.common.i18n import _
|
from heat.common.i18n import _
|
||||||
from heat.common.i18n import _LW
|
from heat.common.i18n import _LW
|
||||||
from heat.engine import attributes
|
from heat.engine import attributes
|
||||||
|
from heat.engine import constraints
|
||||||
|
from heat.engine import properties
|
||||||
from heat.engine import resource
|
from heat.engine import resource
|
||||||
from heat.engine import signal_responder
|
from heat.engine import signal_responder
|
||||||
from heat.engine import stack_user
|
from heat.engine import stack_user
|
||||||
@ -151,3 +153,10 @@ class StackUserResource(stack_user.StackUser):
|
|||||||
def handle_create(self):
|
def handle_create(self):
|
||||||
super(StackUserResource, self).handle_create()
|
super(StackUserResource, self).handle_create()
|
||||||
self.resource_id_set(self._get_user_id())
|
self.resource_id_set(self._get_user_id())
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceWithCustomConstraint(GenericResource):
|
||||||
|
properties_schema = \
|
||||||
|
{'Foo': properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
constraints=[constraints.CustomConstraint('neutron.network')])}
|
||||||
|
@ -15,6 +15,7 @@ import six
|
|||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from heat.common import exception
|
from heat.common import exception
|
||||||
|
from heat.engine.cfn import functions as cfn_funcs
|
||||||
from heat.engine import constraints
|
from heat.engine import constraints
|
||||||
from heat.engine.hot import parameters as hot_param
|
from heat.engine.hot import parameters as hot_param
|
||||||
from heat.engine import parameters
|
from heat.engine import parameters
|
||||||
@ -1055,6 +1056,49 @@ class PropertiesTest(testtools.TestCase):
|
|||||||
except exception.StackValidationFailed:
|
except exception.StackValidationFailed:
|
||||||
self.fail("Constraints should not have been evaluated.")
|
self.fail("Constraints should not have been evaluated.")
|
||||||
|
|
||||||
|
def test_resolve_ref_with_constraints(self):
|
||||||
|
# create test custom constraint
|
||||||
|
class IncorrectConstraint(constraints.BaseCustomConstraint):
|
||||||
|
|
||||||
|
expected_exceptions = (Exception,)
|
||||||
|
|
||||||
|
def validate_with_client(self, client, value):
|
||||||
|
raise Exception("Test exception")
|
||||||
|
|
||||||
|
class TestCustomConstraint(constraints.CustomConstraint):
|
||||||
|
@property
|
||||||
|
def custom_constraint(self):
|
||||||
|
return IncorrectConstraint()
|
||||||
|
|
||||||
|
# create schema with test constraint
|
||||||
|
schema = {
|
||||||
|
'foo': properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
constraints=[TestCustomConstraint('test_constraint')]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# define parameters for function
|
||||||
|
def test_resolver(prop):
|
||||||
|
return 'None'
|
||||||
|
|
||||||
|
class rsrc(object):
|
||||||
|
action = INIT = "INIT"
|
||||||
|
|
||||||
|
stack = {'another_res': rsrc()}
|
||||||
|
|
||||||
|
# define properties with function and constraint
|
||||||
|
props = properties.Properties(
|
||||||
|
schema,
|
||||||
|
{'foo': cfn_funcs.ResourceRef(
|
||||||
|
stack, 'get_resource', 'another_res')},
|
||||||
|
test_resolver)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.assertIsNone(props.validate())
|
||||||
|
except exception.StackValidationFailed:
|
||||||
|
self.fail("Constraints should not have been evaluated.")
|
||||||
|
|
||||||
def test_schema_from_params(self):
|
def test_schema_from_params(self):
|
||||||
params_snippet = {
|
params_snippet = {
|
||||||
"DBUsername": {
|
"DBUsername": {
|
||||||
|
@ -37,6 +37,8 @@ from heat.tests.common import HeatTestCase
|
|||||||
from heat.tests import generic_resource as generic_rsrc
|
from heat.tests import generic_resource as generic_rsrc
|
||||||
from heat.tests import utils
|
from heat.tests import utils
|
||||||
|
|
||||||
|
import neutronclient.common.exceptions as neutron_exp
|
||||||
|
|
||||||
|
|
||||||
empty_template = {"HeatTemplateFormatVersion": "2012-12-12"}
|
empty_template = {"HeatTemplateFormatVersion": "2012-12-12"}
|
||||||
|
|
||||||
@ -47,14 +49,18 @@ class ResourceTest(HeatTestCase):
|
|||||||
|
|
||||||
resource._register_class('GenericResourceType',
|
resource._register_class('GenericResourceType',
|
||||||
generic_rsrc.GenericResource)
|
generic_rsrc.GenericResource)
|
||||||
|
resource._register_class('ResourceWithCustomConstraint',
|
||||||
|
generic_rsrc.ResourceWithCustomConstraint)
|
||||||
|
|
||||||
env = environment.Environment()
|
self.env = environment.Environment()
|
||||||
env.load({u'resource_registry':
|
self.env.load({u'resource_registry':
|
||||||
{u'OS::Test::GenericResource': u'GenericResourceType'}})
|
{u'OS::Test::GenericResource': u'GenericResourceType',
|
||||||
|
u'OS::Test::ResourceWithCustomConstraint':
|
||||||
|
u'ResourceWithCustomConstraint'}})
|
||||||
|
|
||||||
self.stack = parser.Stack(utils.dummy_context(), 'test_stack',
|
self.stack = parser.Stack(utils.dummy_context(), 'test_stack',
|
||||||
parser.Template(empty_template), env=env,
|
parser.Template(empty_template),
|
||||||
stack_id=str(uuid.uuid4()))
|
env=self.env, stack_id=str(uuid.uuid4()))
|
||||||
self.patch('heat.engine.resource.warnings')
|
self.patch('heat.engine.resource.warnings')
|
||||||
|
|
||||||
def test_get_class_ok(self):
|
def test_get_class_ok(self):
|
||||||
@ -1015,6 +1021,46 @@ class ResourceTest(HeatTestCase):
|
|||||||
mock_create.side_effect = Exception()
|
mock_create.side_effect = Exception()
|
||||||
self.assertFalse(res.is_using_neutron())
|
self.assertFalse(res.is_using_neutron())
|
||||||
|
|
||||||
|
def _test_skip_validation_if_custom_constraint(self, tmpl):
|
||||||
|
stack = parser.Stack(utils.dummy_context(), 'test', tmpl, env=self.env)
|
||||||
|
stack.store()
|
||||||
|
path = ('heat.engine.clients.os.neutron.NetworkConstraint.'
|
||||||
|
'validate_with_client')
|
||||||
|
with mock.patch(path) as mock_validate:
|
||||||
|
mock_validate.side_effect = neutron_exp.NeutronClientException
|
||||||
|
rsrc2 = stack['bar']
|
||||||
|
self.assertIsNone(rsrc2.validate())
|
||||||
|
|
||||||
|
def test_ref_skip_validation_if_custom_constraint(self):
|
||||||
|
tmpl = template.Template({
|
||||||
|
'HeatTemplateFormatVersion': '2012-12-12',
|
||||||
|
'Resources': {
|
||||||
|
'foo': {'Type': 'OS::Test::GenericResource'},
|
||||||
|
'bar': {
|
||||||
|
'Type': 'OS::Test::ResourceWithCustomConstraint',
|
||||||
|
'Properties': {
|
||||||
|
'Foo': {'Ref': 'foo'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self._test_skip_validation_if_custom_constraint(tmpl)
|
||||||
|
|
||||||
|
def test_hot_ref_skip_validation_if_custom_constraint(self):
|
||||||
|
tmpl = template.Template({
|
||||||
|
'heat_template_version': '2013-05-23',
|
||||||
|
'resources': {
|
||||||
|
'foo': {'type': 'GenericResourceType'},
|
||||||
|
'bar': {
|
||||||
|
'type': 'ResourceWithCustomConstraint',
|
||||||
|
'properties': {
|
||||||
|
'Foo': {'get_resource': 'foo'},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self._test_skip_validation_if_custom_constraint(tmpl)
|
||||||
|
|
||||||
|
|
||||||
class ResourceAdoptTest(HeatTestCase):
|
class ResourceAdoptTest(HeatTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user