summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Spatzier <thomas.spatzier@de.ibm.com>2014-05-20 11:08:59 +0200
committerThomas Spatzier <thomas.spatzier@de.ibm.com>2014-06-10 13:35:11 +0200
commita39ee7c87562eab3dbf176eb265866f61f5a5af7 (patch)
tree4c0be260fc8c28f9cb577c991cf5e0a2634e7c8c
parentaa7c0306bcc77320b670e0e15895b4b6b3944250 (diff)
downloadheat-a39ee7c87562eab3dbf176eb265866f61f5a5af7.tar.gz
Do no re-validate parameters for existing stacks
This patch disables validation of stack parameters values when loading existing stacks from the database. The old behavior was to always validate stack parameter values when initializing a Stack object. However, this causes problems especially with custom constraints when a constraint could be fulfilled during stack creation but cannot be fulfilled at a later point (e.g. when a flavor or keypair has been deleted). In such a case, the existing stack became completely unusable. This patch changes the behavior to not do parameter validation for existing stacks. The assumption is that validation at stack creation time should be sufficient to make sure the stack is valid, so only valid stacks end up in the database. Therefore, validating parameters again when loading stacks is not really required. Closes-Bug: #1314401 Cherry-pick from review https://review.openstack.org/#/c/94329 (cherry picked from commit 349a6f04b53309f7fa8f3225f98d41670888d20c) Conflicts: heat/tests/test_parser.py Change-Id: I0947c2dcfb9db4c81b07de582aab86f262d0c008
-rw-r--r--heat/engine/parser.py8
-rw-r--r--heat/tests/test_parser.py56
2 files changed, 60 insertions, 4 deletions
diff --git a/heat/engine/parser.py b/heat/engine/parser.py
index 3637317e5..73074919c 100644
--- a/heat/engine/parser.py
+++ b/heat/engine/parser.py
@@ -61,7 +61,7 @@ class Stack(collections.Mapping):
disable_rollback=True, parent_resource=None, owner_id=None,
adopt_stack_data=None, stack_user_project_id=None,
created_time=None, updated_time=None,
- user_creds_id=None, tenant_id=None):
+ user_creds_id=None, tenant_id=None, validate_parameters=True):
'''
Initialise from a context, name, Template object and (optionally)
Environment object. The database ID may also be initialised, if the
@@ -105,7 +105,8 @@ class Stack(collections.Mapping):
self.env = env or environment.Environment({})
self.parameters = self.t.parameters(self.identifier(),
user_params=self.env.params)
- self.parameters.validate(validate_value=True, context=context)
+ self.parameters.validate(validate_value=validate_parameters,
+ context=context)
self._set_param_stackid()
if resolve_data:
@@ -193,7 +194,8 @@ class Stack(collections.Mapping):
stack_user_project_id=stack.stack_user_project_id,
created_time=stack.created_at,
updated_time=stack.updated_at,
- user_creds_id=stack.user_creds_id, tenant_id=stack.tenant)
+ user_creds_id=stack.user_creds_id, tenant_id=stack.tenant,
+ validate_parameters=False)
return stack
diff --git a/heat/tests/test_parser.py b/heat/tests/test_parser.py
index 24bfb76ba..1c7363ba5 100644
--- a/heat/tests/test_parser.py
+++ b/heat/tests/test_parser.py
@@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import collections
import json
import time
@@ -924,7 +925,8 @@ class StackTest(HeatTestCase):
created_time=IgnoreArg(),
updated_time=None,
user_creds_id=stack.user_creds_id,
- tenant_id='test_tenant_id')
+ tenant_id='test_tenant_id',
+ validate_parameters=False)
self.m.ReplayAll()
parser.Stack.load(self.ctx, stack_id=self.stack.id,
@@ -2740,3 +2742,55 @@ class StackTest(HeatTestCase):
resources = self.stack.preview_resources()
self.assertEqual(['foo'], resources)
+
+ def test_stack_load_no_param_value_validation(self):
+ '''
+ Test stack loading with disabled parameter value validation.
+ '''
+ tmpl = template_format.parse('''
+ heat_template_version: 2013-05-23
+ parameters:
+ flavor:
+ type: string
+ description: A flavor.
+ constraints:
+ - custom_constraint: nova.flavor
+ resources:
+ a_resource:
+ type: GenericResourceType
+ ''')
+
+ # Mock objects so the query for flavors in server.FlavorConstraint
+ # works for stack creation
+ fc = fakes.FakeClient()
+ self.m.StubOutWithMock(clients.OpenStackClients, 'nova')
+ clients.OpenStackClients.nova().MultipleTimes().AndReturn(fc)
+
+ fc.flavors = self.m.CreateMockAnything()
+ flavor = collections.namedtuple("Flavor", ["id", "name"])
+ flavor.id = "1234"
+ flavor.name = "dummy"
+ fc.flavors.list().AndReturn([flavor])
+
+ self.m.ReplayAll()
+
+ self.stack = parser.Stack(self.ctx, 'stack_with_custom_constraint',
+ template.Template(tmpl),
+ environment.Environment({'flavor': 'dummy'}))
+
+ self.stack.store()
+ self.stack.create()
+ stack_id = self.stack.id
+
+ self.m.VerifyAll()
+
+ self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
+ self.stack.state)
+
+ loaded_stack = parser.Stack.load(self.ctx, stack_id=self.stack.id)
+ self.assertEqual(stack_id, loaded_stack.parameters['OS::stack_id'])
+
+ # verify that fc.flavors.list() has not been called, i.e. verify that
+ # parameter value validation did not happen and FlavorConstraint was
+ # not invoked
+ self.m.VerifyAll()