From c751abc05debe98dff4efde44bb1c9cb43230562 Mon Sep 17 00:00:00 2001 From: Steven Hardy Date: Fri, 1 Feb 2013 13:01:18 +0000 Subject: [PATCH] heat engine : WaitCondition add Handle property validation Add validation to the Handle property for WaitCondition, so we check that the identifier in the provided URL maps to a WaitConditionHandle resource in the same stack as the WaitCondition fixes bug 1100754 Change-Id: I44c8f52435016fad5a35183472d2b0df26c1915d --- heat/engine/resources/wait_condition.py | 21 ++++ heat/tests/test_waitcondition.py | 132 ++++++++++++++++++++++-- 2 files changed, 143 insertions(+), 10 deletions(-) diff --git a/heat/engine/resources/wait_condition.py b/heat/engine/resources/wait_condition.py index 373ea1e917..4fb2b4293e 100644 --- a/heat/engine/resources/wait_condition.py +++ b/heat/engine/resources/wait_condition.py @@ -204,6 +204,26 @@ class WaitCondition(resource.Resource): self.timeout / self.SLEEP_DIV), self.MIN_SLEEP) + def _validate_handle_url(self): + handle_url = self.properties['Handle'] + handle_id = identifier.ResourceIdentifier.from_arn_url(handle_url) + if handle_id.tenant != self.stack.context.tenant_id: + raise ValueError("WaitCondition invalid Handle tenant %s" % + handle_id.tenant) + if handle_id.stack_name != self.stack.name: + raise ValueError("WaitCondition invalid Handle stack %s" % + handle_id.stack_name) + if handle_id.stack_id != self.stack.id: + raise ValueError("WaitCondition invalid Handle stack %s" % + handle_id.stack_id) + if handle_id.resource_name not in self.stack: + raise ValueError("WaitCondition invalid Handle %s" % + handle_id.resource_name) + if not isinstance(self.stack[handle_id.resource_name], + WaitConditionHandle): + raise ValueError("WaitCondition invalid Handle %s" % + handle_id.resource_name) + def _get_handle_resource_name(self): handle_url = self.properties['Handle'] handle_id = identifier.ResourceIdentifier.from_arn_url(handle_url) @@ -213,6 +233,7 @@ class WaitCondition(resource.Resource): return eventlet.Timeout(self.timeout) def handle_create(self): + self._validate_handle_url() tmo = None status = FAILURE reason = "Unknown reason" diff --git a/heat/tests/test_waitcondition.py b/heat/tests/test_waitcondition.py index cd3865ec38..8baba8bbd3 100644 --- a/heat/tests/test_waitcondition.py +++ b/heat/tests/test_waitcondition.py @@ -17,6 +17,7 @@ import mox import uuid import time import datetime +import json import eventlet import unittest @@ -96,22 +97,26 @@ class WaitConditionTest(unittest.TestCase): # Note tests creating a stack should be decorated with @stack_delete_after # to ensure the stack is properly cleaned up def create_stack(self, stack_name='test_stack', - template=test_template_waitcondition, params={}): + template=test_template_waitcondition, params={}, + stub=True): temp = template_format.parse(template) template = parser.Template(temp) parameters = parser.Parameters(stack_name, template, params) - stack = parser.Stack(context.get_admin_context(), stack_name, - template, parameters) + ctx = context.get_admin_context() + ctx.tenant_id = 'test_tenant' + stack = parser.Stack(ctx, stack_name, template, parameters) - stack.store() + self.stack_id = stack.store() - self.m.StubOutWithMock(wc.WaitConditionHandle, 'keystone') - wc.WaitConditionHandle.keystone().MultipleTimes().AndReturn(self.fc) + if stub: + self.m.StubOutWithMock(wc.WaitConditionHandle, 'keystone') + wc.WaitConditionHandle.keystone().MultipleTimes().AndReturn( + self.fc) - id = identifier.ResourceIdentifier('test_tenant', stack.name, - stack.id, '', 'WaitHandle') - self.m.StubOutWithMock(wc.WaitConditionHandle, 'identifier') - wc.WaitConditionHandle.identifier().MultipleTimes().AndReturn(id) + id = identifier.ResourceIdentifier('test_tenant', stack.name, + stack.id, '', 'WaitHandle') + self.m.StubOutWithMock(wc.WaitConditionHandle, 'identifier') + wc.WaitConditionHandle.identifier().MultipleTimes().AndReturn(id) return stack @@ -263,6 +268,113 @@ class WaitConditionTest(unittest.TestCase): self.assertEqual(wc_att, u'{"123": "foo", "456": "dog"}') self.m.VerifyAll() + @stack_delete_after + def test_validate_handle_url_bad_stackid(self): + # Stub out the stack ID so we have a known value + stack_id = 'STACKABCD1234' + self.m.StubOutWithMock(uuid, 'uuid4') + uuid.uuid4().AndReturn(stack_id) + self.m.ReplayAll() + + t = json.loads(test_template_waitcondition) + badhandle = ("http://127.0.0.1:8000/v1/waitcondition/" + + "arn%3Aopenstack%3Aheat%3A%3Atest_tenant" + + "%3Astacks%2Ftest_stack%2F" + + "bad1" + + "%2Fresources%2FWaitHandle") + t['Resources']['WaitForTheHandle']['Properties']['Handle'] = badhandle + self.stack = self.create_stack(template=json.dumps(t), stub=False) + self.m.ReplayAll() + + resource = self.stack.resources['WaitForTheHandle'] + self.assertRaises(ValueError, resource.handle_create) + + self.m.VerifyAll() + + @stack_delete_after + def test_validate_handle_url_bad_stackname(self): + # Stub out the stack ID so we have a known value + stack_id = 'STACKABCD1234' + self.m.StubOutWithMock(uuid, 'uuid4') + uuid.uuid4().AndReturn(stack_id) + self.m.ReplayAll() + + t = json.loads(test_template_waitcondition) + badhandle = ("http://127.0.0.1:8000/v1/waitcondition/" + + "arn%3Aopenstack%3Aheat%3A%3Atest_tenant" + + "%3Astacks%2FBAD_stack%2F" + + stack_id + "%2Fresources%2FWaitHandle") + t['Resources']['WaitForTheHandle']['Properties']['Handle'] = badhandle + self.stack = self.create_stack(template=json.dumps(t), stub=False) + + resource = self.stack.resources['WaitForTheHandle'] + self.assertRaises(ValueError, resource.handle_create) + + self.m.VerifyAll() + + @stack_delete_after + def test_validate_handle_url_bad_tenant(self): + # Stub out the stack ID so we have a known value + stack_id = 'STACKABCD1234' + self.m.StubOutWithMock(uuid, 'uuid4') + uuid.uuid4().AndReturn(stack_id) + self.m.ReplayAll() + + t = json.loads(test_template_waitcondition) + badhandle = ("http://127.0.0.1:8000/v1/waitcondition/" + + "arn%3Aopenstack%3Aheat%3A%3ABAD_tenant" + + "%3Astacks%2Ftest_stack%2F" + + stack_id + "%2Fresources%2FWaitHandle") + t['Resources']['WaitForTheHandle']['Properties']['Handle'] = badhandle + self.stack = self.create_stack(template=json.dumps(t), stub=False) + + resource = self.stack.resources['WaitForTheHandle'] + self.assertRaises(ValueError, resource.handle_create) + + self.m.VerifyAll() + + @stack_delete_after + def test_validate_handle_url_bad_resource(self): + # Stub out the stack ID so we have a known value + stack_id = 'STACKABCD1234' + self.m.StubOutWithMock(uuid, 'uuid4') + uuid.uuid4().AndReturn(stack_id) + self.m.ReplayAll() + + t = json.loads(test_template_waitcondition) + badhandle = ("http://127.0.0.1:8000/v1/waitcondition/" + + "arn%3Aopenstack%3Aheat%3A%3Atest_tenant" + + "%3Astacks%2Ftest_stack%2F" + + stack_id + "%2Fresources%2FBADHandle") + t['Resources']['WaitForTheHandle']['Properties']['Handle'] = badhandle + self.stack = self.create_stack(template=json.dumps(t), stub=False) + + resource = self.stack.resources['WaitForTheHandle'] + self.assertRaises(ValueError, resource.handle_create) + + self.m.VerifyAll() + + @stack_delete_after + def test_validate_handle_url_bad_resource_type(self): + # Stub out the stack ID so we have a known value + stack_id = 'STACKABCD1234' + self.m.StubOutWithMock(uuid, 'uuid4') + uuid.uuid4().AndReturn(stack_id) + self.m.ReplayAll() + + t = json.loads(test_template_waitcondition) + badhandle = ("http://127.0.0.1:8000/v1/waitcondition/" + + "arn%3Aopenstack%3Aheat%3A%3Atest_tenant" + + "%3Astacks%2Ftest_stack%2F" + + stack_id + "%2Fresources%2FWaitForTheHandle") + t['Resources']['WaitForTheHandle']['Properties']['Handle'] = badhandle + self.stack = self.create_stack(template=json.dumps(t), stub=False) + + resource = self.stack.resources['WaitForTheHandle'] + self.assertRaises(ValueError, resource.handle_create) + + self.m.VerifyAll() + @attr(tag=['unit', 'resource', 'WaitConditionHandle']) @attr(speed='fast')