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.engine import constraints as constr
from heat.engine import function
from heat.engine import parameters
from heat.engine import support
@ -382,7 +383,13 @@ class Properties(collections.Mapping):
if key in self.data:
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)
# the resolver function could raise any number of exceptions,
# so handle this generically

View File

@ -14,6 +14,8 @@
from heat.common.i18n import _
from heat.common.i18n import _LW
from heat.engine import attributes
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine import signal_responder
from heat.engine import stack_user
@ -151,3 +153,10 @@ class StackUserResource(stack_user.StackUser):
def handle_create(self):
super(StackUserResource, self).handle_create()
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
from heat.common import exception
from heat.engine.cfn import functions as cfn_funcs
from heat.engine import constraints
from heat.engine.hot import parameters as hot_param
from heat.engine import parameters
@ -1055,6 +1056,49 @@ class PropertiesTest(testtools.TestCase):
except exception.StackValidationFailed:
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):
params_snippet = {
"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 utils
import neutronclient.common.exceptions as neutron_exp
empty_template = {"HeatTemplateFormatVersion": "2012-12-12"}
@ -47,14 +49,18 @@ class ResourceTest(HeatTestCase):
resource._register_class('GenericResourceType',
generic_rsrc.GenericResource)
resource._register_class('ResourceWithCustomConstraint',
generic_rsrc.ResourceWithCustomConstraint)
env = environment.Environment()
env.load({u'resource_registry':
{u'OS::Test::GenericResource': u'GenericResourceType'}})
self.env = environment.Environment()
self.env.load({u'resource_registry':
{u'OS::Test::GenericResource': u'GenericResourceType',
u'OS::Test::ResourceWithCustomConstraint':
u'ResourceWithCustomConstraint'}})
self.stack = parser.Stack(utils.dummy_context(), 'test_stack',
parser.Template(empty_template), env=env,
stack_id=str(uuid.uuid4()))
parser.Template(empty_template),
env=self.env, stack_id=str(uuid.uuid4()))
self.patch('heat.engine.resource.warnings')
def test_get_class_ok(self):
@ -1015,6 +1021,46 @@ class ResourceTest(HeatTestCase):
mock_create.side_effect = Exception()
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):
def setUp(self):