Merge "Skip validation if depends on not created resource"

This commit is contained in:
Jenkins 2014-09-30 17:14:56 +00:00 committed by Gerrit Code Review
commit cdfa0f9e59
4 changed files with 112 additions and 6 deletions

View File

@ -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

View File

@ -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')])}

View File

@ -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": {

View File

@ -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):