Source code for heat.tests.test_waitcondition

# vim: tabstop=4 shiftwidth=4 softtabstop=4

#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.


import mox
import uuid
import datetime
import json

import eventlet
from nose.plugins.attrib import attr
from oslo.config import cfg
import unittest

from heat.tests import fakes
from heat.tests.utils import stack_delete_after

import heat.db as db_api
from heat.common import template_format
from heat.common import identifier
from heat.engine import parser
from heat.engine.resources import wait_condition as wc
from heat.common import context

test_template_waitcondition = '''
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "Just a WaitCondition.",
  "Parameters" : {},
  "Resources" : {
    "WaitHandle" : {
      "Type" : "AWS::CloudFormation::WaitConditionHandle"
    },
    "WaitForTheHandle" : {
      "Type" : "AWS::CloudFormation::WaitCondition",
      "Properties" : {
        "Handle" : {"Ref" : "WaitHandle"},
        "Timeout" : "5"
      }
    }
  }
}
'''

test_template_wc_count = '''
{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "Just a WaitCondition.",
  "Parameters" : {},
  "Resources" : {
    "WaitHandle" : {
      "Type" : "AWS::CloudFormation::WaitConditionHandle"
    },
    "WaitForTheHandle" : {
      "Type" : "AWS::CloudFormation::WaitCondition",
      "Properties" : {
        "Handle" : {"Ref" : "WaitHandle"},
        "Timeout" : "5",
        "Count" : "3"
      }
    }
  }
}
'''


@attr(tag=['unit', 'resource', 'WaitCondition'])
@attr(speed='slow')
[docs]class WaitConditionTest(unittest.TestCase):
[docs] def setUp(self): self.m = mox.Mox() self.m.StubOutWithMock(wc.WaitConditionHandle, 'get_status') self.m.StubOutWithMock(wc.WaitCondition, '_create_timeout') self.m.StubOutWithMock(eventlet, 'sleep') cfg.CONF.set_default('heat_waitcondition_server_url', 'http://127.0.0.1:8000/v1/waitcondition') self.fc = fakes.FakeKeystoneClient()
[docs] def tearDown(self): self.m.UnsetStubs() # Note tests creating a stack should be decorated with @stack_delete_after # to ensure the stack is properly cleaned up
[docs] def create_stack(self, stack_name='test_stack', template=test_template_waitcondition, params={}, stub=True): temp = template_format.parse(template) template = parser.Template(temp) parameters = parser.Parameters(stack_name, template, params) ctx = context.get_admin_context() ctx.tenant_id = 'test_tenant' stack = parser.Stack(ctx, stack_name, template, parameters, disable_rollback=True) self.stack_id = stack.store() 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) return stack
@stack_delete_after
[docs] def test_post_success_to_handle(self): self.stack = self.create_stack() wc.WaitCondition._create_timeout().AndReturn(eventlet.Timeout(5)) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS']) self.m.ReplayAll() self.stack.create() resource = self.stack.resources['WaitForTheHandle'] self.assertEqual(resource.state, 'CREATE_COMPLETE') r = db_api.resource_get_by_name_and_stack(None, 'WaitHandle', self.stack.id) self.assertEqual(r.name, 'WaitHandle') self.m.VerifyAll()
@stack_delete_after
[docs] def test_post_failure_to_handle(self): self.stack = self.create_stack() wc.WaitCondition._create_timeout().AndReturn(eventlet.Timeout(5)) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn(['FAILURE']) self.m.ReplayAll() self.stack.create() resource = self.stack.resources['WaitForTheHandle'] self.assertEqual(resource.state, 'CREATE_FAILED') r = db_api.resource_get_by_name_and_stack(None, 'WaitHandle', self.stack.id) self.assertEqual(r.name, 'WaitHandle') self.m.VerifyAll()
@stack_delete_after
[docs] def test_post_success_to_handle_count(self): self.stack = self.create_stack(template=test_template_wc_count) wc.WaitCondition._create_timeout().AndReturn(eventlet.Timeout(5)) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS']) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS', 'SUCCESS']) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS', 'SUCCESS', 'SUCCESS']) self.m.ReplayAll() self.stack.create() resource = self.stack.resources['WaitForTheHandle'] self.assertEqual(resource.state, 'CREATE_COMPLETE') r = db_api.resource_get_by_name_and_stack(None, 'WaitHandle', self.stack.id) self.assertEqual(r.name, 'WaitHandle') self.m.VerifyAll()
@stack_delete_after
[docs] def test_post_failure_to_handle_count(self): self.stack = self.create_stack(template=test_template_wc_count) wc.WaitCondition._create_timeout().AndReturn(eventlet.Timeout(5)) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS']) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS', 'FAILURE']) self.m.ReplayAll() self.stack.create() resource = self.stack.resources['WaitForTheHandle'] self.assertEqual(resource.state, 'CREATE_FAILED') r = db_api.resource_get_by_name_and_stack(None, 'WaitHandle', self.stack.id) self.assertEqual(r.name, 'WaitHandle') self.m.VerifyAll()
@stack_delete_after
[docs] def test_timeout(self): self.stack = self.create_stack() tmo = eventlet.Timeout(6) wc.WaitCondition._create_timeout().AndReturn(tmo) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndReturn(None) wc.WaitConditionHandle.get_status().AndReturn([]) eventlet.sleep(1).AndRaise(tmo) self.m.ReplayAll() self.stack.create() resource = self.stack.resources['WaitForTheHandle'] self.assertEqual(resource.state, 'CREATE_FAILED') self.assertEqual(wc.WaitCondition.UPDATE_REPLACE, resource.handle_update({})) self.m.VerifyAll()
@stack_delete_after
[docs] def test_FnGetAtt(self): self.stack = self.create_stack() wc.WaitCondition._create_timeout().AndReturn(eventlet.Timeout(5)) wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS']) self.m.ReplayAll() self.stack.create() resource = self.stack.resources['WaitForTheHandle'] self.assertEqual(resource.state, 'CREATE_COMPLETE') wc_att = resource.FnGetAtt('Data') self.assertEqual(wc_att, unicode({})) handle = self.stack.resources['WaitHandle'] self.assertEqual(handle.state, 'CREATE_COMPLETE') test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} handle.metadata_update(new_metadata=test_metadata) wc_att = resource.FnGetAtt('Data') self.assertEqual(wc_att, '{"123": "foo"}') test_metadata = {'Data': 'dog', 'Reason': 'cat', 'Status': 'SUCCESS', 'UniqueId': '456'} handle.metadata_update(new_metadata=test_metadata) wc_att = resource.FnGetAtt('Data') self.assertEqual(wc_att, u'{"123": "foo", "456": "dog"}') self.m.VerifyAll()
@stack_delete_after
[docs] 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
[docs] 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
[docs] 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
[docs] 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
[docs] 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')
[docs]class WaitConditionHandleTest(unittest.TestCase):
[docs] def setUp(self): self.m = mox.Mox() cfg.CONF.set_default('heat_waitcondition_server_url', 'http://127.0.0.1:8000/v1/waitcondition') self.fc = fakes.FakeKeystoneClient() self.stack = self.create_stack()
[docs] def tearDown(self): self.m.UnsetStubs()
[docs] def create_stack(self, stack_name='test_stack2', params={}): temp = template_format.parse(test_template_waitcondition) template = parser.Template(temp) parameters = parser.Parameters(stack_name, template, params) ctx = context.get_admin_context() ctx.tenant_id = 'test_tenant' stack = parser.Stack(ctx, stack_name, template, parameters, disable_rollback=True) # Stub out the UUID for this test, so we can get an expected signature self.m.StubOutWithMock(uuid, 'uuid4') uuid.uuid4().AndReturn('STACKABCD1234') self.m.ReplayAll() stack.store() # Stub waitcondition status so all goes CREATE_COMPLETE self.m.StubOutWithMock(wc.WaitConditionHandle, 'get_status') wc.WaitConditionHandle.get_status().AndReturn(['SUCCESS']) self.m.StubOutWithMock(wc.WaitCondition, '_create_timeout') wc.WaitCondition._create_timeout().AndReturn(eventlet.Timeout(5)) # Stub keystone() with fake client 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) self.m.ReplayAll() stack.create() return stack
@stack_delete_after
[docs] def test_handle(self): created_time = datetime.datetime(2012, 11, 29, 13, 49, 37) resource = self.stack.resources['WaitHandle'] resource.created_time = created_time self.assertEqual(resource.state, 'CREATE_COMPLETE') expected_url = "".join([ 'http://127.0.0.1:8000/v1/waitcondition/', 'arn%3Aopenstack%3Aheat%3A%3Atest_tenant%3Astacks%2F', 'test_stack2%2FSTACKABCD1234%2Fresources%2F', 'WaitHandle?', 'Timestamp=2012-11-29T13%3A49%3A37Z&', 'SignatureMethod=HmacSHA256&', 'AWSAccessKeyId=4567&', 'SignatureVersion=2&', 'Signature=', 'ePyTwmC%2F1kSigeo%2Fha7kP8Avvb45G9Y7WOQWe4F%2BnXM%3D']) self.assertEqual(expected_url, resource.FnGetRefId()) self.assertEqual(resource.UPDATE_REPLACE, resource.handle_update({})) self.m.VerifyAll()
@stack_delete_after
[docs] def test_metadata_update(self): resource = self.stack.resources['WaitHandle'] self.assertEqual(resource.state, 'CREATE_COMPLETE') test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} resource.metadata_update(new_metadata=test_metadata) handle_metadata = {u'123': {u'Data': u'foo', u'Reason': u'bar', u'Status': u'SUCCESS'}} self.assertEqual(resource.metadata, handle_metadata) self.m.VerifyAll()
@stack_delete_after
[docs] def test_metadata_update_invalid(self): resource = self.stack.resources['WaitHandle'] self.assertEqual(resource.state, 'CREATE_COMPLETE') # metadata_update should raise a ValueError if the metadata # is missing any of the expected keys err_metadata = {'Data': 'foo', 'Status': 'SUCCESS', 'UniqueId': '123'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'UniqueId': '1234'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) err_metadata = {'data': 'foo', 'reason': 'bar', 'status': 'SUCCESS', 'uniqueid': '1234'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) # Also any Status other than SUCCESS or FAILURE should be rejected err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'UCCESS', 'UniqueId': '123'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'wibble', 'UniqueId': '123'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'success', 'UniqueId': '123'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) err_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'FAIL', 'UniqueId': '123'} self.assertRaises(ValueError, resource.metadata_update, new_metadata=err_metadata) self.m.VerifyAll()
@stack_delete_after
[docs] def test_get_status(self): resource = self.stack.resources['WaitHandle'] self.assertEqual(resource.state, 'CREATE_COMPLETE') # UnsetStubs, don't want get_status stubbed anymore.. self.m.VerifyAll() self.m.UnsetStubs() self.assertEqual(resource.get_status(), []) test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} resource.metadata_update(new_metadata=test_metadata) self.assertEqual(resource.get_status(), ['SUCCESS']) test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '456'} resource.metadata_update(new_metadata=test_metadata) self.assertEqual(resource.get_status(), ['SUCCESS', 'SUCCESS']) # re-stub keystone() with fake client or stack delete fails self.m.StubOutWithMock(wc.WaitConditionHandle, 'keystone') wc.WaitConditionHandle.keystone().MultipleTimes().AndReturn(self.fc) self.m.ReplayAll()
@stack_delete_after
[docs] def test_get_status_reason(self): resource = self.stack.resources['WaitHandle'] self.assertEqual(resource.state, 'CREATE_COMPLETE') test_metadata = {'Data': 'foo', 'Reason': 'bar', 'Status': 'SUCCESS', 'UniqueId': '123'} resource.metadata_update(new_metadata=test_metadata) self.assertEqual(resource.get_status_reason('SUCCESS'), 'bar') test_metadata = {'Data': 'dog', 'Reason': 'cat', 'Status': 'SUCCESS', 'UniqueId': '456'} resource.metadata_update(new_metadata=test_metadata) self.assertEqual(resource.get_status_reason('SUCCESS'), 'bar;cat') test_metadata = {'Data': 'boo', 'Reason': 'hoo', 'Status': 'FAILURE', 'UniqueId': '789'} resource.metadata_update(new_metadata=test_metadata) self.assertEqual(resource.get_status_reason('FAILURE'), 'hoo') self.m.VerifyAll()