1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
# not use this file except in compliance with the License. You may obtain
5
# a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
# License for the specific language governing permissions and limitations
17
from oslo.config import cfg
19
from heat.tests import generic_resource
20
from heat.tests import fakes
21
from heat.tests.common import HeatTestCase
22
from heat.tests import utils
23
from heat.tests.utils import reset_dummy_db
25
from heat.common import context
26
from heat.common import exception
27
from heat.common import template_format
29
from heat.engine import parser
30
from heat.engine import resource
31
from heat.engine import signal_responder as sr
34
test_template_signal = '''
36
"AWSTemplateFormatVersion" : "2010-09-09",
37
"Description" : "Just a test.",
40
"signal_handler" : {"Type" : "SignalResourceType"},
41
"resource_X" : {"Type" : "GenericResourceType"}
44
"signed_url": {"Fn::GetAtt": ["signal_handler", "AlarmUrl"]}
50
class SignalTest(HeatTestCase):
53
super(SignalTest, self).setUp()
54
utils.setup_dummy_db()
56
resource._register_class('SignalResourceType',
57
generic_resource.SignalResource)
58
resource._register_class('GenericResourceType',
59
generic_resource.GenericResource)
61
cfg.CONF.set_default('heat_waitcondition_server_url',
62
'http://127.0.0.1:8000/v1/waitcondition')
64
self.stack_id = 'STACKABCD1234'
65
self.fc = fakes.FakeKeystoneClient()
68
super(SignalTest, self).tearDown()
71
# Note tests creating a stack should be decorated with @stack_delete_after
72
# to ensure the stack is properly cleaned up
73
def create_stack(self, stack_name='test_stack', stub=True):
74
temp = template_format.parse(test_template_signal)
75
template = parser.Template(temp)
76
ctx = context.get_admin_context()
77
ctx.tenant_id = 'test_tenant'
78
stack = parser.Stack(ctx, stack_name, template,
79
disable_rollback=True)
81
# Stub out the stack ID so we have a known value
82
with utils.UUIDStub(self.stack_id):
86
self.m.StubOutWithMock(sr.SignalResponder, 'keystone')
87
sr.SignalResponder.keystone().MultipleTimes().AndReturn(
91
@utils.stack_delete_after
92
def test_FnGetAtt_Alarm_Url(self):
93
self.stack = self.create_stack()
98
rsrc = self.stack.resources['signal_handler']
99
created_time = datetime.datetime(2012, 11, 29, 13, 49, 37)
100
rsrc.created_time = created_time
101
self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
103
expected_url = "".join([
104
'http://127.0.0.1:8000/v1/signal/',
105
'arn%3Aopenstack%3Aheat%3A%3Atest_tenant%3Astacks%2F',
106
'test_stack%2FSTACKABCD1234%2Fresources%2F',
108
'Timestamp=2012-11-29T13%3A49%3A37Z&',
109
'SignatureMethod=HmacSHA256&',
110
'AWSAccessKeyId=4567&',
111
'SignatureVersion=2&',
113
'MJIFh7LKCpVlK6pCxe2WfYrRsfO7FU3Wt%2BzQFo2rYSY%3D'])
115
self.assertEqual(expected_url, rsrc.FnGetAtt('AlarmUrl'))
118
@utils.stack_delete_after
119
def test_signal(self):
120
test_d = {'Data': 'foo', 'Reason': 'bar',
121
'Status': 'SUCCESS', 'UniqueId': '123'}
123
self.stack = self.create_stack()
125
# to confirm we get a call to handle_signal
126
self.m.StubOutWithMock(generic_resource.SignalResource,
128
generic_resource.SignalResource.handle_signal(test_d).AndReturn(None)
133
rsrc = self.stack.resources['signal_handler']
134
self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
136
rsrc.signal(details=test_d)
140
@utils.stack_delete_after
141
def test_signal_wrong_resource(self):
142
# assert that we get the correct exception when calling a
143
# resource.signal() that does not have a handle_signal()
144
self.stack = self.create_stack()
149
rsrc = self.stack.resources['resource_X']
150
self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
152
err_metadata = {'Data': 'foo', 'Status': 'SUCCESS', 'UniqueId': '123'}
153
self.assertRaises(exception.ResourceFailure, rsrc.signal,
154
details=err_metadata)
158
@utils.stack_delete_after
159
def test_signal_reception_wrong_state(self):
160
# assert that we get the correct exception when calling a
161
# resource.signal() that is in having a destructive action.
162
self.stack = self.create_stack()
167
rsrc = self.stack.resources['signal_handler']
168
self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
169
# manually override the action to DELETE
170
rsrc.action = rsrc.DELETE
172
err_metadata = {'Data': 'foo', 'Status': 'SUCCESS', 'UniqueId': '123'}
173
self.assertRaises(exception.ResourceFailure, rsrc.signal,
174
details=err_metadata)
178
@utils.stack_delete_after
179
def test_signal_reception_failed_call(self):
180
# assert that we get the correct exception from resource.signal()
181
# when resource.handle_signal() raises an exception.
182
self.stack = self.create_stack()
184
test_d = {'Data': 'foo', 'Reason': 'bar',
185
'Status': 'SUCCESS', 'UniqueId': '123'}
187
# to confirm we get a call to handle_signal
188
self.m.StubOutWithMock(generic_resource.SignalResource,
190
generic_resource.SignalResource.handle_signal(test_d).AndRaise(
196
rsrc = self.stack.resources['signal_handler']
197
self.assertEqual(rsrc.state, (rsrc.CREATE, rsrc.COMPLETE))
199
self.assertRaises(exception.ResourceFailure,
200
rsrc.signal, details=test_d)